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