1use std::collections::HashSet;
4
5use crate::ast::{Rule, RuleKind, Term, TermKind};
6use crate::error::Error;
7use crate::lex::{Spanned, Token, tokens};
8
9const RESULT: &str = "result";
11
12const RESERVED: [&str; 6] = ["rule", "simplify", "lower", "if", "spec", "bounded"];
16
17pub fn parse(path: &str, text: &str) -> Result<Vec<Rule>, Vec<Error>> {
24 let tokens = match tokens(path, text) {
25 Ok(tokens) => tokens,
26 Err(error) => return Err(vec![error]),
27 };
28 let mut reader = Reader { path, tokens: &tokens, at: 0, end: end_of(text) };
29 let mut rules = Vec::new();
30 let mut errors = Vec::new();
31
32 while reader.at < reader.tokens.len() {
33 match reader.rule() {
34 Ok(rule) => {
35 check(path, &rule, &mut errors);
36 rules.push(rule);
37 }
38 Err(error) => {
39 errors.push(error);
40 reader.resync();
41 }
42 }
43 }
44
45 if errors.is_empty() { Ok(rules) } else { Err(errors) }
46}
47
48pub fn parse_terms(path: &str, text: &str) -> Result<Vec<Term>, Vec<Error>> {
58 let tokens = match tokens(path, text) {
59 Ok(tokens) => tokens,
60 Err(error) => return Err(vec![error]),
61 };
62 let mut reader = Reader { path, tokens: &tokens, at: 0, end: end_of(text) };
63 let mut out = Vec::new();
64 while reader.at < reader.tokens.len() {
65 match reader.term() {
66 Ok(term) => out.push(term),
67 Err(error) => return Err(vec![error]),
68 }
69 }
70 Ok(out)
71}
72
73fn end_of(text: &str) -> (u32, u32) {
75 let line = 1 + u32::try_from(text.matches('\n').count()).unwrap_or(u32::MAX);
76 let column = 1 + u32::try_from(text.rsplit('\n').next().unwrap_or_default().chars().count())
77 .unwrap_or(u32::MAX);
78 (line, column)
79}
80
81#[derive(Debug)]
83struct Reader<'a> {
84 path: &'a str,
85 tokens: &'a [Spanned<'a>],
86 at: usize,
87 end: (u32, u32),
88}
89
90impl<'a> Reader<'a> {
91 fn error(&self, message: String) -> Error {
92 let (line, column) = match self.tokens.get(self.at) {
93 Some(token) => (token.line, token.column),
94 None => self.end,
95 };
96 Error { path: self.path.to_owned(), line, column, message }
97 }
98
99 fn peek(&self) -> Option<&'a Token<'a>> {
100 self.tokens.get(self.at).map(|t| &t.token)
101 }
102
103 fn at_clause(&self, name: &str) -> bool {
106 matches!(self.peek(), Some(Token::Open))
107 && matches!(self.tokens.get(self.at + 1).map(|t| &t.token), Some(Token::Atom(a)) if *a == name)
108 }
109
110 fn open(&mut self) -> Result<(), Error> {
111 match self.peek() {
112 Some(Token::Open) => {
113 self.at += 1;
114 Ok(())
115 }
116 _ => Err(self.error("expected a `(`".to_owned())),
117 }
118 }
119
120 fn close(&mut self, what: &str) -> Result<(), Error> {
124 match self.peek() {
125 Some(Token::Close) => {
126 self.at += 1;
127 Ok(())
128 }
129 None => Err(self.error(format!("`({what}` was never closed"))),
130 _ => Err(self.error("expected a `)`".to_owned())),
131 }
132 }
133
134 fn keyword(&mut self, name: &str) -> Result<(), Error> {
135 match self.peek() {
136 Some(Token::Atom(a)) if *a == name => {
137 self.at += 1;
138 Ok(())
139 }
140 _ => Err(self.error(format!("expected `{name}`"))),
141 }
142 }
143
144 fn rule(&mut self) -> Result<Rule, Error> {
146 let (line, column) = match self.tokens.get(self.at) {
147 Some(token) => (token.line, token.column),
148 None => self.end,
149 };
150 self.open()?;
151 self.keyword("rule")?;
152
153 self.open()?;
154 let kind = self.rule_kind()?;
155 let pattern = self.term()?;
156 self.close(kind.as_str())?;
157
158 let guard = if self.at_clause("if") {
159 self.at += 1;
160 self.at += 1;
161 let guard = self.term()?;
162 self.close("if")?;
163 Some(guard)
164 } else {
165 None
166 };
167
168 let replacement = self.term()?;
169
170 self.open()?;
171 self.keyword("spec")?;
172 let spec = self.term()?;
173 self.close("spec")?;
174
175 let bounded = if self.at_clause("bounded") {
178 self.at += 2;
179 let why = self.string()?;
180 self.close("bounded")?;
181 Some(why)
182 } else {
183 None
184 };
185
186 self.close("rule")?;
187 Ok(Rule { kind, pattern, guard, replacement, spec, bounded, line, column })
188 }
189
190 fn rule_kind(&mut self) -> Result<RuleKind, Error> {
196 match self.peek() {
197 Some(Token::Atom(a)) if *a == RuleKind::Simplify.as_str() => {
198 self.at += 1;
199 Ok(RuleKind::Simplify)
200 }
201 Some(Token::Atom(a)) if *a == RuleKind::Lower.as_str() => {
202 self.at += 1;
203 Ok(RuleKind::Lower)
204 }
205 _ => Err(self.error("expected `simplify` or `lower`".to_owned())),
206 }
207 }
208
209 fn string(&mut self) -> Result<String, Error> {
211 match self.peek() {
212 Some(Token::Str(text)) if !text.trim().is_empty() => {
213 let text = (*text).to_owned();
214 self.at += 1;
215 Ok(text)
216 }
217 Some(Token::Str(_)) => {
218 Err(self.error("a bounded proof needs a reason somebody signed for".to_owned()))
219 }
220 _ => Err(self.error("expected a reason, in quotation marks".to_owned())),
221 }
222 }
223
224 fn term(&mut self) -> Result<Term, Error> {
225 let Some(token) = self.tokens.get(self.at) else {
226 return Err(self.error("expected a term and the file ended".to_owned()));
227 };
228 let (line, column) = (token.line, token.column);
229 match &token.token {
230 Token::Int(value) => {
231 self.at += 1;
232 Ok(Term { kind: TermKind::Int(*value), line, column })
233 }
234 Token::Atom(name) => {
239 self.at += 1;
240 Ok(Term { kind: TermKind::Var((*name).to_owned()), line, column })
241 }
242 Token::Close => Err(self.error("expected a term and found a `)`".to_owned())),
243 Token::Str(_) => Err(self.error(
244 "a string is prose for a person and is not something a term can be".to_owned(),
245 )),
246 Token::Open => {
247 self.at += 1;
248 let head = match self.peek() {
249 Some(Token::Atom(head)) => {
250 let head = *head;
251 self.at += 1;
252 head
253 }
254 _ => return Err(self.error("expected a name after the `(`".to_owned())),
255 };
256 if RESERVED.contains(&head) {
257 let message =
261 format!("`{head}` belongs to a rule's own shape, not inside a term");
262 self.at -= 2;
263 return Err(self.error(message));
264 }
265 let mut args = Vec::new();
266 while !matches!(self.peek(), Some(Token::Close)) {
267 if self.peek().is_none() {
268 return Err(self.error(format!("`({head}` was never closed")));
269 }
270 args.push(self.term()?);
271 }
272 self.at += 1;
273 Ok(Term { kind: TermKind::App { head: head.to_owned(), args }, line, column })
274 }
275 }
276 }
277
278 fn resync(&mut self) {
281 self.at += 1;
282 while self.at < self.tokens.len() && !self.at_clause("rule") {
283 self.at += 1;
284 }
285 }
286}
287
288fn check(path: &str, rule: &Rule, errors: &mut Vec<Error>) {
290 let mut found = Vec::new();
291
292 if !matches!(rule.pattern.kind, TermKind::App { .. }) {
293 found.push((&rule.pattern, "a pattern has to name something to match".to_owned()));
294 }
295
296 let mut bound: HashSet<&str> = HashSet::new();
300 let mut in_pattern = Vec::new();
301 rule.pattern.walk(&mut |term| match &term.kind {
302 TermKind::Var(name) => {
303 bound.insert(name.as_str());
304 }
305 TermKind::App { head, .. } if head == RESULT => {
306 let said = "`(result)` is what the replacement produces, so it means nothing here";
307 in_pattern.push((term, said.to_owned()));
308 }
309 _ => {}
310 });
311 found.extend(in_pattern);
312
313 let mut clauses =
314 vec![("the replacement", &rule.replacement), ("the specification", &rule.spec)];
315 if let Some(guard) = &rule.guard {
316 clauses.insert(0, ("the guard", guard));
317 }
318 let mut loose = Vec::new();
319 for (what, term) in clauses {
320 term.walk(&mut |term| match &term.kind {
321 TermKind::Var(name) if !bound.contains(name.as_str()) => {
322 let said = format!("`{name}` is used in {what} and the pattern never bound it");
323 loose.push((term, said));
324 }
325 TermKind::App { head, .. } if head == RESULT && what != "the specification" => {
328 let said = format!("`(result)` belongs in the specification, not in {what}");
329 loose.push((term, said));
330 }
331 _ => {}
332 });
333 }
334 found.extend(loose);
335
336 for (term, message) in found {
337 errors.push(Error { path: path.to_owned(), line: term.line, column: term.column, message });
338 }
339}