Skip to main content

implicit_predicates_gen/
parse.rs

1//! Reads `.pred` files and builds expression graphs.
2//!
3//! The grammar is exactly the accepted subset:
4//!
5//! ```text
6//! file   := item*
7//! item   := ('#' '[' 'exact' ']')? 'fn' NAME '(' params? ')' '->' 'Sign' block
8//! params := param (',' param)* ','?        param := NAME ':' 'Point3'
9//! block  := '{' ('let' NAME '=' expr ';')* expr '}'
10//! ```
11//!
12//! Expressions are read by [`crate::expr`].
13
14use std::collections::HashMap;
15
16use proc_macro2::{Delimiter, LexError, Span, TokenStream, TokenTree};
17
18use crate::cursor::{Cursor, is_punct};
19use crate::error::{Error, Res, diag};
20use crate::expr::{Binding, Scope, parse_expr};
21use crate::graph::{Builder, Graph, Node};
22use crate::limits;
23
24/// A predicate read from a definition file.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct Predicate {
27    /// Name of the function.
28    pub name: String,
29    /// Names of the point parameters, in order.
30    pub params: Vec<String>,
31    /// The main form of the polynomial.
32    pub main: Graph,
33    /// The form marked `#[exact]`, if the file has one.
34    pub exact: Option<Graph>,
35    /// Position of the name of the main form.
36    pub position: Position,
37    /// Position of the name of the exact form, if there is one.
38    pub exact_position: Option<Position>,
39}
40
41/// A position in a definition file.
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct Position {
44    /// Line, starting at 1.
45    pub line: usize,
46    /// Column, starting at 1.
47    pub column: usize,
48}
49
50impl Position {
51    fn of(span: Span) -> Self {
52        let start = span.start();
53        Position {
54            line: start.line,
55            column: start.column + 1,
56        }
57    }
58}
59
60/// One function of the file, before forms are paired.
61struct Form {
62    name: String,
63    name_span: Span,
64    params: Vec<String>,
65    graph: Graph,
66    exact: bool,
67}
68
69/// Reads every predicate defined in `source`.
70///
71/// `file` is only used to label errors. Predicates come in the order of their
72/// main forms.
73///
74/// # Limits
75///
76/// No input makes this function panic or overflow the stack of the calling
77/// thread, whatever its size or shape; a debug build with a 1 MiB stack is
78/// enough. The only limit is the nesting of brackets: at most 32 deep,
79/// counting the braces of the function body, so a parenthesized expression
80/// may nest 31 deep. Comments and literals do not count toward it. Sums,
81/// products and runs of unary minus are read in loops, so this function
82/// accepts them at any length; the later steps of [`crate::generate`] have
83/// budgets of their own: a sum of 100,000 terms in one chain, for instance,
84/// is rejected by [`crate::filter_bound`] with "the expression is too large
85/// for the error bound of the filter". An error for brackets that are
86/// nested too deeply names the limit and suggests splitting the expression
87/// with `let`.
88///
89/// Ordinary comments are skipped. Doc comments (`///`, `//!`, `/** */` and
90/// `/*! */`) are rejected at their position with "doc comments are not
91/// supported in definitions; use `//`", since Rust would read them as
92/// attributes. A predicate is named after a generated module, so its name
93/// must be usable as one: Rust keywords, strict and reserved, `_` and raw
94/// identifiers are rejected.
95///
96/// # Errors
97///
98/// Returns the first problem found: a token that the grammar of definitions
99/// does not allow, a doc comment, a predicate name that cannot name a
100/// module, an undefined or redefined name, an unused parameter, or a
101/// misplaced `#[exact]` form.
102pub fn parse(source: &str, file: &str) -> Result<Vec<Predicate>, Error> {
103    parse_source(source).map_err(|d| Error {
104        file: file.to_string(),
105        line: d.line,
106        column: d.column,
107        message: d.message,
108    })
109}
110
111fn parse_source(source: &str) -> Res<Vec<Predicate>> {
112    limits::check_text(source)?;
113    let tokens: TokenStream = source
114        .parse()
115        .map_err(|e: LexError| diag(e.span(), e.to_string()))?;
116    let mut cur = Cursor::new(tokens, None);
117    let mut predicates: Vec<Predicate> = Vec::new();
118    let mut exacts: Vec<Form> = Vec::new();
119    while !cur.at_end() {
120        let form = parse_item(&mut cur)?;
121        if form.exact {
122            if exacts.iter().any(|e| e.name == form.name) {
123                return Err(diag(
124                    form.name_span,
125                    format!("`{}` already has an exact form", form.name),
126                ));
127            }
128            exacts.push(form);
129        } else if predicates.iter().any(|p| p.name == form.name) {
130            return Err(diag(
131                form.name_span,
132                format!("`{}` is already defined", form.name),
133            ));
134        } else {
135            predicates.push(Predicate {
136                name: form.name,
137                params: form.params,
138                main: form.graph,
139                exact: None,
140                position: Position::of(form.name_span),
141                exact_position: None,
142            });
143        }
144    }
145    for form in exacts {
146        let Some(predicate) = predicates.iter_mut().find(|p| p.name == form.name) else {
147            return Err(diag(
148                form.name_span,
149                format!("the exact form of `{}` has no main form", form.name),
150            ));
151        };
152        if predicate.params != form.params {
153            return Err(diag(
154                form.name_span,
155                format!(
156                    "the exact form of `{}` must have the parameters of the main form",
157                    form.name
158                ),
159            ));
160        }
161        predicate.exact = Some(form.graph);
162        predicate.exact_position = Some(Position::of(form.name_span));
163    }
164    if predicates.is_empty() {
165        return Err(diag(Span::call_site(), "no predicate definitions found"));
166    }
167    Ok(predicates)
168}
169
170/// Reads one function, with its optional `#[exact]` attribute.
171fn parse_item(cur: &mut Cursor) -> Res<Form> {
172    let exact = parse_attr(cur)?;
173    if !cur.peek_word("fn") {
174        return Err(cur.expected("`fn`"));
175    }
176    cur.bump();
177    if let Some(TokenTree::Ident(id)) = cur.peek() {
178        let name = id.to_string();
179        if !can_name_a_module(&name) {
180            return Err(diag(
181                id.span(),
182                format!(
183                    "`{name}` cannot be used as the name of a generated module: \
184                     a predicate needs a name that is not a Rust keyword nor a raw identifier"
185                ),
186            ));
187        }
188    }
189    let (name, name_span) = parse_name(cur, "a function name")?;
190    let (params, spans) = parse_params(cur)?;
191    if !(is_punct(cur.peek(), '-') && is_punct(cur.peek_at(1), '>')) {
192        return Err(cur.expected("`->`"));
193    }
194    cur.bump();
195    cur.bump();
196    if !cur.peek_word("Sign") {
197        return Err(match cur.peek() {
198            Some(token) => diag(token.span(), "the return type must be `Sign`"),
199            None => cur.expected("`Sign`"),
200        });
201    }
202    cur.bump();
203    let (body, close) = match cur.peek() {
204        Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
205            (g.stream(), g.span_close())
206        }
207        _ => return Err(cur.expected("`{`")),
208    };
209    cur.bump();
210    let graph = parse_body(Cursor::new(body, Some(close)), &params)?;
211    for (point, (name, span)) in params.iter().zip(&spans).enumerate() {
212        let used = graph
213            .nodes
214            .iter()
215            .any(|n| matches!(n, Node::Coord { point: p, .. } if *p == point));
216        if !used {
217            return Err(diag(*span, format!("unused parameter `{name}`")));
218        }
219    }
220    Ok(Form {
221        name,
222        name_span,
223        params,
224        graph,
225        exact,
226    })
227}
228
229/// The keywords of Rust, strict and reserved, in every edition up to 2024,
230/// and `_`: none of them can be the name of a module.
231const KEYWORDS: [&str; 53] = [
232    "_", "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",
233    "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move",
234    "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true",
235    "type", "unsafe", "use", "where", "while", "abstract", "become", "box", "do", "final", "gen",
236    "macro", "override", "priv", "try", "typeof", "unsized", "virtual", "yield",
237];
238
239/// Whether `name` can be the name of the module, and of the file, that the
240/// generator writes for a predicate: an identifier that is not a keyword and
241/// not a raw identifier.
242fn can_name_a_module(name: &str) -> bool {
243    !name.starts_with("r#") && !KEYWORDS.contains(&name)
244}
245
246/// Reads `#[exact]` if it is there and tells whether it was.
247fn parse_attr(cur: &mut Cursor) -> Res<bool> {
248    let Some(TokenTree::Punct(hash)) = cur.peek() else {
249        return Ok(false);
250    };
251    if hash.as_char() != '#' {
252        return Ok(false);
253    }
254    let span = hash.span();
255    cur.bump();
256    let is_exact = match cur.peek() {
257        Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
258            let mut inside = Cursor::new(g.stream(), Some(g.span_close()));
259            let found = inside.peek_word("exact");
260            inside.bump();
261            found && inside.at_end()
262        }
263        _ => false,
264    };
265    if !is_exact {
266        return Err(diag(span, "the only attribute allowed is `#[exact]`"));
267    }
268    cur.bump();
269    if is_punct(cur.peek(), '#') {
270        return Err(diag(
271            cur.peek().map_or(span, TokenTree::span),
272            "the only attribute allowed is a single `#[exact]`",
273        ));
274    }
275    Ok(true)
276}
277
278/// Reads a name: any identifier except `fn` and `let`.
279fn parse_name(cur: &mut Cursor, what: &str) -> Res<(String, Span)> {
280    match cur.peek() {
281        Some(TokenTree::Ident(id)) if id != "fn" && id != "let" => {
282            let result = (id.to_string(), id.span());
283            cur.bump();
284            Ok(result)
285        }
286        _ => Err(cur.expected(what)),
287    }
288}
289
290/// Reads the parameter list and returns the names with their positions.
291fn parse_params(cur: &mut Cursor) -> Res<(Vec<String>, Vec<Span>)> {
292    let (stream, close) = match cur.peek() {
293        Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
294            (g.stream(), g.span_close())
295        }
296        _ => return Err(cur.expected("`(`")),
297    };
298    cur.bump();
299    let mut inside = Cursor::new(stream, Some(close));
300    let (mut names, mut spans): (Vec<String>, Vec<Span>) = (Vec::new(), Vec::new());
301    while !inside.at_end() {
302        let (name, span) = parse_name(&mut inside, "a parameter name")?;
303        if names.contains(&name) {
304            return Err(diag(span, format!("`{name}` redefines a name")));
305        }
306        if !inside.peek_punct(':') {
307            return Err(inside.expected("`:`"));
308        }
309        inside.bump();
310        match inside.peek() {
311            Some(TokenTree::Ident(id)) if id == "Point3" => inside.bump(),
312            Some(token) => {
313                return Err(diag(token.span(), "a parameter must have type `Point3`"));
314            }
315            None => return Err(inside.expected("the type `Point3`")),
316        }
317        names.push(name);
318        spans.push(span);
319        if !inside.at_end() {
320            if !inside.peek_punct(',') {
321                return Err(inside.expected("`,`"));
322            }
323            inside.bump();
324        }
325    }
326    Ok((names, spans))
327}
328
329/// Reads the body of a function: `let` bindings and a final expression.
330fn parse_body(mut body: Cursor, params: &[String]) -> Res<Graph> {
331    let mut env: HashMap<String, Binding> = params
332        .iter()
333        .enumerate()
334        .map(|(i, name)| (name.clone(), Binding::Param(i)))
335        .collect();
336    let mut builder = Builder::default();
337    loop {
338        if body.at_end() {
339            return Err(diag(body.close(), "the body needs a final expression"));
340        }
341        if !body.peek_word("let") {
342            let mut scope = Scope {
343                builder: &mut builder,
344                env: &env,
345            };
346            let root = parse_expr(&mut body, &mut scope, 1)?;
347            if let Some(token) = body.peek() {
348                return Err(if is_punct(Some(token), ';') {
349                    diag(
350                        token.span(),
351                        "expected `let` bindings followed by a final expression without `;`",
352                    )
353                } else {
354                    body.expected("an operator or the end of the body")
355                });
356            }
357            return Ok(builder.finish(root));
358        }
359        body.bump();
360        let (name, span) = parse_name(&mut body, "a name")?;
361        if env.contains_key(&name) {
362            return Err(diag(span, format!("`{name}` redefines a name")));
363        }
364        if !body.peek_punct('=') {
365            return Err(body.expected("`=`"));
366        }
367        body.bump();
368        let mut scope = Scope {
369            builder: &mut builder,
370            env: &env,
371        };
372        let value = parse_expr(&mut body, &mut scope, 1)?;
373        if !body.peek_punct(';') {
374            return Err(body.expected("an operator or `;`"));
375        }
376        body.bump();
377        env.insert(name, Binding::Let(value));
378    }
379}