1use 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#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct Predicate {
30 pub name: String,
32 pub params: Vec<String>,
34 pub main: Graph,
36 pub exact: Option<Graph>,
38 pub position: Position,
40 pub exact_position: Option<Position>,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub struct Position {
47 pub line: usize,
49 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
63struct Form {
65 name: String,
66 name_span: Span,
67 params: Vec<String>,
68 graph: Graph,
69 exact: bool,
70}
71
72pub 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
185fn 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)), ¶ms)?;
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
244const 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
254pub(crate) fn can_name_a_module(name: &str) -> bool {
258 !name.starts_with("r#") && !KEYWORDS.contains(&name)
259}
260
261fn 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
293fn 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
305fn 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
344fn 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}