1use crate::ast::{ExpressionIdGenerator, Span};
2use crate::error::LemmaError;
3use crate::semantic::*;
4use pest::iterators::Pair;
5use pest::Parser;
6use pest_derive::Parser;
7use std::sync::Arc;
8
9pub mod expressions;
10pub mod facts;
11pub mod literals;
12pub mod rules;
13pub mod units;
14
15#[derive(Parser)]
16#[grammar = "src/parser/lemma.pest"]
17pub struct LemmaParser;
18
19pub fn parse(content: &str, filename: Option<String>) -> Result<Vec<LemmaDoc>, LemmaError> {
20 let mut id_gen = ExpressionIdGenerator::new();
21 let filename = filename.unwrap_or_else(|| "<input>".to_string());
22
23 match LemmaParser::parse(Rule::lemma_file, content) {
24 Ok(pairs) => {
25 let mut docs = Vec::new();
26 for pair in pairs {
27 if pair.as_rule() == Rule::lemma_file {
28 for inner_pair in pair.into_inner() {
29 if inner_pair.as_rule() == Rule::doc {
30 docs.push(parse_doc(inner_pair, &filename, content, &mut id_gen)?);
31 }
32 }
33 }
34 }
35 Ok(docs)
36 }
37 Err(e) => {
38 let pest_span = match e.line_col {
39 pest::error::LineColLocation::Pos((line, col)) => Span {
40 start: 0,
41 end: 0,
42 line,
43 col,
44 },
45 pest::error::LineColLocation::Span((start_line, start_col), (_, _)) => Span {
46 start: 0,
47 end: 0,
48 line: start_line,
49 col: start_col,
50 },
51 };
52
53 Err(LemmaError::parse(
54 format!("Parse error: {}", e.variant),
55 pest_span,
56 filename,
57 Arc::from(content),
58 "<parse-error>",
59 1,
60 ))
61 }
62 }
63}
64
65pub fn parse_facts(fact_strings: &[&str]) -> Result<Vec<LemmaFact>, LemmaError> {
66 let mut facts = Vec::new();
67
68 for fact_str in fact_strings {
69 let fact_input = format!("fact {}", fact_str);
70 let pairs = LemmaParser::parse(Rule::fact, &fact_input).map_err(|e| {
71 LemmaError::Engine(format!("Failed to parse fact '{}': {}", fact_str, e))
72 })?;
73
74 let fact_pair = pairs.into_iter().next().ok_or_else(|| {
75 LemmaError::Engine(format!("No parse result for fact '{}'", fact_str))
76 })?;
77
78 let inner_pair = fact_pair
79 .into_inner()
80 .next()
81 .ok_or_else(|| LemmaError::Engine(format!("No inner rule for fact '{}'", fact_str)))?;
82
83 let fact = match inner_pair.as_rule() {
84 Rule::fact_definition => crate::parser::facts::parse_fact_definition(inner_pair)?,
85 Rule::fact_override => crate::parser::facts::parse_fact_override(inner_pair)?,
86 _ => {
87 return Err(LemmaError::Engine(format!(
88 "Unexpected rule type for fact '{}'",
89 fact_str
90 )))
91 }
92 };
93
94 facts.push(fact);
95 }
96
97 Ok(facts)
98}
99
100fn parse_doc(
101 pair: Pair<Rule>,
102 filename: &str,
103 _source: &str,
104 id_gen: &mut ExpressionIdGenerator,
105) -> Result<LemmaDoc, LemmaError> {
106 let doc_start_line = pair.as_span().start_pos().line_col().0;
107
108 let mut doc_name: Option<String> = None;
109 let mut commentary: Option<String> = None;
110 let mut facts = Vec::new();
111 let mut rules = Vec::new();
112
113 for inner_pair in pair.into_inner() {
114 match inner_pair.as_rule() {
115 Rule::doc_declaration => {
116 for decl_inner in inner_pair.into_inner() {
117 if decl_inner.as_rule() == Rule::doc_name {
118 doc_name = Some(parse_doc_name(decl_inner)?);
119 break;
120 }
121 }
122 }
123 Rule::commentary_content => {
124 commentary = Some(inner_pair.as_str().trim().to_string());
125 }
126 Rule::fact_definition => {
127 let fact = crate::parser::facts::parse_fact_definition(inner_pair)?;
128 facts.push(fact);
129 }
130 Rule::fact_override => {
131 let fact = crate::parser::facts::parse_fact_override(inner_pair)?;
132 facts.push(fact);
133 }
134 Rule::rule_definition => {
135 let rule = crate::parser::rules::parse_rule_definition(inner_pair, id_gen)?;
136 rules.push(rule);
137 }
138 _ => {}
139 }
140 }
141
142 let name = doc_name.unwrap_or_else(|| "default".to_string());
143 let mut doc = LemmaDoc::new(name)
144 .with_source(filename.to_string())
145 .with_start_line(doc_start_line);
146
147 if let Some(commentary_text) = commentary {
148 doc = doc.set_commentary(commentary_text);
149 }
150
151 for fact in facts {
152 doc = doc.add_fact(fact);
153 }
154 for rule in rules {
155 doc = doc.add_rule(rule);
156 }
157
158 Ok(doc)
159}
160
161fn parse_doc_name(pair: Pair<Rule>) -> Result<String, LemmaError> {
162 Ok(pair.as_str().to_string())
163}