Skip to main content

lemma/parsing/
parser.rs

1use crate::error::Error;
2use crate::limits::ResourceLimits;
3use crate::parsing::ast::{try_parse_type_constraint_command, *};
4use crate::parsing::lexer::{
5    can_be_label, can_be_spec_name_spelling, can_be_unit_name, is_boolean_keyword, is_keyword,
6    is_math_function, is_reserved_repository_name, is_spec_body_keyword,
7    token_is_calendar_period_marker, token_kind_to_boolean_value, token_kind_to_primitive, Lexer,
8    LexerCheckpoint, Token, TokenKind,
9};
10use crate::parsing::source::Source;
11use indexmap::IndexMap;
12use rust_decimal::Decimal;
13use std::sync::Arc;
14
15#[derive(Debug)]
16pub struct ParseResult {
17    pub repositories: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>>,
18    pub expression_count: usize,
19}
20
21impl ParseResult {
22    /// Specs in parse order: repository groups follow declaration order; specs within each group follow source order.
23    #[must_use]
24    pub fn flatten_specs(&self) -> Vec<&LemmaSpec> {
25        self.repositories
26            .values()
27            .flat_map(|specs| specs.iter())
28            .collect()
29    }
30
31    #[must_use]
32    pub fn into_flattened_specs(self) -> Vec<LemmaSpec> {
33        self.repositories.into_values().flatten().collect()
34    }
35}
36
37/// Parse a standalone repository qualifier string (trimmed). Requires EOF after
38/// the qualifier so callers can reject URL/path injection in install ids.
39pub fn parse_repository_qualifier_str(
40    input: &str,
41) -> Result<crate::parsing::ast::RepositoryQualifier, Error> {
42    let limits = ResourceLimits::default();
43    let mut parser = Parser::new(
44        input.trim(),
45        crate::parsing::source::SourceType::Volatile,
46        &limits,
47    );
48    let (qualifier, _) = parser.parse_repository_qualifier()?;
49    if !parser.at(&TokenKind::Eof)? {
50        let tok = parser.peek()?.clone();
51        return Err(parser.error_at_token(
52            &tok,
53            format!(
54                "Unexpected {} after repository qualifier '{}'",
55                tok.kind, qualifier.name
56            ),
57        ));
58    }
59    Ok(qualifier)
60}
61
62pub fn parse(
63    content: &str,
64    source_type: crate::parsing::source::SourceType,
65    limits: &ResourceLimits,
66) -> Result<ParseResult, Error> {
67    if content.len() > limits.max_source_size_bytes {
68        return Err(Error::resource_limit_exceeded(
69            "max_source_size_bytes",
70            format!(
71                "{} bytes ({} MB)",
72                limits.max_source_size_bytes,
73                limits.max_source_size_bytes / (1024 * 1024)
74            ),
75            format!(
76                "{} bytes ({:.2} MB)",
77                content.len(),
78                content.len() as f64 / (1024.0 * 1024.0)
79            ),
80            "Reduce source size or split into multiple specs",
81            None,
82            None,
83            None,
84        ));
85    }
86
87    let mut parser = Parser::new(content, source_type, limits);
88    let repositories = parser.parse_file()?;
89    let mut result = ParseResult {
90        repositories,
91        expression_count: parser.expression_count,
92    };
93    canonicalize_parse_result(&mut result);
94    Ok(result)
95}
96
97fn canonicalize_parse_result(result: &mut ParseResult) {
98    let old = std::mem::take(&mut result.repositories);
99    let mut new_map: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>> = IndexMap::new();
100    for (repo, mut specs) in old {
101        let mut canonical_repo = (*repo).clone();
102        canonicalize_repository(&mut canonical_repo);
103        for spec in &mut specs {
104            canonicalize_lemma_spec(spec);
105        }
106        new_map
107            .entry(Arc::new(canonical_repo))
108            .or_default()
109            .extend(specs);
110    }
111    result.repositories = new_map;
112}
113
114/// Parser position to rewind to after a speculative parse.
115struct ParserCheckpoint {
116    lexer: LexerCheckpoint,
117    expression_count: usize,
118    table_len: usize,
119}
120
121struct Parser {
122    lexer: Lexer,
123    source_type: crate::parsing::source::SourceType,
124    depth_tracker: DepthTracker,
125    expression_count: usize,
126    max_expression_count: usize,
127    max_spec_name_length: usize,
128    max_data_name_length: usize,
129    max_rule_name_length: usize,
130    last_span: Span,
131    /// Parsing a bare command argument: `->` ends the operand instead of chaining onto it.
132    bare_operand: bool,
133    /// Nesting depth of comma-separated call arguments (`find(a, b)`).
134    call_argument_depth: usize,
135    /// Operand starts of the bound chain that last completed without `...`.
136    bound_chain_starts: Vec<usize>,
137    /// Operand starts whose bound chain is known to end without `...`, under the flags
138    /// they were parsed with. Such an operand is a suffix of an already-scanned chain.
139    no_range_at: std::collections::HashMap<usize, ExprFlags>,
140    table: ExprTable<ExpressionKind>,
141    /// Continuation stack of the expression machine, kept for its capacity. Empty between
142    /// runs; a run nested in another starts from a fresh stack.
143    expr_frames: Vec<ExprCont>,
144}
145
146impl Parser {
147    fn new(
148        content: &str,
149        source_type: crate::parsing::source::SourceType,
150        limits: &ResourceLimits,
151    ) -> Self {
152        Parser {
153            lexer: Lexer::new(content, &source_type),
154            source_type,
155            depth_tracker: DepthTracker::with_max_depth(limits.max_expression_depth),
156            expression_count: 0,
157            max_expression_count: limits.max_expression_count,
158            max_spec_name_length: crate::limits::MAX_SPEC_NAME_LENGTH,
159            max_data_name_length: crate::limits::MAX_DATA_NAME_LENGTH,
160            max_rule_name_length: crate::limits::MAX_RULE_NAME_LENGTH,
161            last_span: Span {
162                start: 0,
163                end: 0,
164                line: 1,
165                col: 0,
166            },
167            bare_operand: false,
168            call_argument_depth: 0,
169            bound_chain_starts: Vec::new(),
170            no_range_at: std::collections::HashMap::new(),
171            expr_frames: Vec::new(),
172            table: ExprTable::default(),
173        }
174    }
175
176    fn source_type(&self) -> crate::parsing::source::SourceType {
177        self.source_type.clone()
178    }
179
180    fn peek(&mut self) -> Result<&Token, Error> {
181        self.lexer.peek()
182    }
183
184    fn next(&mut self) -> Result<Token, Error> {
185        let token = self.lexer.next_token()?;
186        self.last_span = token.span.clone();
187        Ok(token)
188    }
189
190    fn at(&mut self, kind: &TokenKind) -> Result<bool, Error> {
191        Ok(&self.peek()?.kind == kind)
192    }
193
194    fn at_any(&mut self, kinds: &[TokenKind]) -> Result<bool, Error> {
195        let current = &self.peek()?.kind;
196        Ok(kinds.contains(current))
197    }
198
199    fn checkpoint(&self) -> ParserCheckpoint {
200        ParserCheckpoint {
201            lexer: self.lexer.checkpoint(),
202            expression_count: self.expression_count,
203            table_len: self.table.len(),
204        }
205    }
206
207    /// Rewinds to `checkpoint`, dropping every expression node parsed since.
208    fn restore(&mut self, checkpoint: ParserCheckpoint) {
209        self.lexer.restore(checkpoint.lexer);
210        self.expression_count = checkpoint.expression_count;
211        self.table.truncate(checkpoint.table_len);
212    }
213
214    fn expect(&mut self, kind: &TokenKind) -> Result<Token, Error> {
215        let token = self.next()?;
216        if &token.kind == kind {
217            Ok(token)
218        } else {
219            Err(self.error_at_token(&token, format!("Expected {}, found {}", kind, token.kind)))
220        }
221    }
222
223    fn at_calendar_period_marker(&mut self) -> Result<bool, Error> {
224        Ok(token_is_calendar_period_marker(self.peek()?))
225    }
226
227    fn expect_calendar_period_marker(&mut self) -> Result<Token, Error> {
228        let token = self.next()?;
229        if token_is_calendar_period_marker(&token) {
230            Ok(token)
231        } else {
232            Err(self.error_at_token(&token, "Expected 'calendar' (date-period predicate marker)"))
233        }
234    }
235
236    fn next_calendar_period_marker(&mut self) -> Result<Token, Error> {
237        self.expect_calendar_period_marker()
238    }
239
240    fn error_at_token(&self, token: &Token, message: impl Into<String>) -> Error {
241        Error::parsing(
242            message,
243            Source::new(self.source_type(), token.span.clone()),
244            None::<String>,
245        )
246    }
247
248    fn error_at_token_with_suggestion(
249        &self,
250        token: &Token,
251        message: impl Into<String>,
252        suggestion: impl Into<String>,
253    ) -> Error {
254        Error::parsing(
255            message,
256            Source::new(self.source_type(), token.span.clone()),
257            Some(suggestion),
258        )
259    }
260
261    fn parse_spec_ref_trailing_effective(&mut self) -> Result<Option<DateTimeValue>, Error> {
262        let mut effective = None;
263        if self.at(&TokenKind::NumberLit)? {
264            let peeked = self.peek()?;
265            if peeked.text.len() == 4 && peeked.text.chars().all(|c| c.is_ascii_digit()) {
266                effective = self.try_parse_effective_from()?;
267            }
268        }
269        Ok(effective)
270    }
271
272    fn make_source(&self, span: Span) -> Source {
273        Source::new(self.source_type(), span)
274    }
275
276    fn span_from(&self, start: &Span) -> Span {
277        // Create a span from start to the current lexer position.
278        // We peek to get the current position.
279        Span {
280            start: start.start,
281            end: start.end.max(start.start),
282            line: start.line,
283            col: start.col,
284        }
285    }
286
287    fn span_covering(&self, start: &Span, end: &Span) -> Span {
288        Span {
289            start: start.start,
290            end: end.end,
291            line: start.line,
292            col: start.col,
293        }
294    }
295
296    // ========================================================================
297    // Top-level: file and spec
298    // ========================================================================
299
300    fn parse_file(&mut self) -> Result<IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>>, Error> {
301        let mut map: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>> = IndexMap::new();
302        let mut current_repo = Arc::new(LemmaRepository::new(None));
303
304        loop {
305            if self.at(&TokenKind::Eof)? {
306                break;
307            }
308
309            if self.at(&TokenKind::Repo)? {
310                let repo_token = self.expect(&TokenKind::Repo)?;
311                let start_line = repo_token.span.line;
312                let (qualifier, _) = self.parse_repository_qualifier()?;
313                crate::limits::check_max_length(
314                    &qualifier.name,
315                    self.max_spec_name_length,
316                    "repository name",
317                    Some(Source::new(self.source_type(), repo_token.span)),
318                )?;
319                current_repo = Arc::new(
320                    LemmaRepository::new(Some(qualifier.name)).with_start_line(start_line),
321                );
322                map.entry(Arc::clone(&current_repo)).or_default();
323                continue;
324            }
325
326            if self.at(&TokenKind::Spec)? {
327                let spec = self.parse_spec()?;
328                map.entry(Arc::clone(&current_repo)).or_default().push(spec);
329                continue;
330            }
331
332            let token = self.next()?;
333            return Err(self.error_at_token_with_suggestion(
334                &token,
335                format!(
336                    "Expected a top-level `repo` or `spec` declaration, found {}",
337                    token.kind
338                ),
339                "Each Lemma file is a sequence of optional `repo <name>` sections followed by `spec <name>` blocks",
340            ));
341        }
342
343        Ok(map)
344    }
345
346    fn parse_spec(&mut self) -> Result<LemmaSpec, Error> {
347        let spec_token = self.expect(&TokenKind::Spec)?;
348        let start_line = spec_token.span.line;
349
350        let (name, name_span) = self.parse_spec_name()?;
351        crate::limits::check_max_length(
352            &name,
353            self.max_spec_name_length,
354            "spec",
355            Some(Source::new(self.source_type(), name_span)),
356        )?;
357
358        let effective_from = self.try_parse_effective_from()?;
359
360        let commentary = self.try_parse_commentary()?;
361
362        let mut spec = LemmaSpec::new(name.clone())
363            .with_source_type(self.source_type())
364            .with_start_line(start_line);
365        spec.effective_from = crate::parsing::ast::EffectiveDate::from_option(effective_from);
366
367        if let Some(commentary_text) = commentary {
368            spec = spec.set_commentary(commentary_text);
369        }
370
371        // First pass: collect type definitions
372        // We need to peek and handle type definitions first, but since we consume tokens
373        // linearly, we'll collect all items in one pass.
374        let mut data = Vec::new();
375        let mut rules = Vec::new();
376        let mut meta_fields = Vec::new();
377
378        loop {
379            let peek_kind = self.peek()?.kind.clone();
380            match peek_kind {
381                TokenKind::Data => {
382                    let datum = self.parse_data()?;
383                    data.push(datum);
384                }
385                TokenKind::Rule => {
386                    let rule = self.parse_rule()?;
387                    rules.push(rule);
388                }
389                TokenKind::Meta => {
390                    let meta = self.parse_meta()?;
391                    meta_fields.push(meta);
392                }
393                TokenKind::Uses => {
394                    let uses_data = self.parse_uses_statement()?;
395                    data.push(uses_data);
396                }
397                TokenKind::Spec | TokenKind::Repo | TokenKind::Eof => break,
398                TokenKind::With => {
399                    let token = self.next()?;
400                    return Err(self.error_at_token_with_suggestion(
401                        &token,
402                        "Standalone `with alias.field: value` is not allowed".to_string(),
403                        "Use `uses alias: spec -> with field: value`",
404                    ));
405                }
406                _ => {
407                    let token = self.next()?;
408                    return Err(self.error_at_token_with_suggestion(
409                        &token,
410                        format!(
411                            "Expected 'data', 'rule', 'meta', 'uses', or a new 'spec', found '{}'",
412                            token.text
413                        ),
414                        "Check the spelling or add the appropriate keyword",
415                    ));
416                }
417            }
418        }
419
420        for data in data {
421            spec = spec.add_data(data);
422        }
423        for rule in rules {
424            spec = spec.add_rule(rule);
425        }
426        for meta in meta_fields {
427            spec = spec.add_meta_field(meta);
428        }
429
430        Ok(spec)
431    }
432
433    fn next_logical_name(&mut self) -> Result<Token, Error> {
434        let token = self.lexer.next_logical_name()?;
435        self.last_span = token.span.clone();
436        Ok(token)
437    }
438
439    fn parse_spec_name(&mut self) -> Result<(String, Span), Error> {
440        let tok = self.next_logical_name()?;
441        if tok.text.starts_with('@') {
442            return Err(Error::parsing(
443                "'@' is not allowed in spec names; it is valid for repository names (`repo @org/name`) and qualifiers (`uses @org/name`)",
444                self.make_source(tok.span),
445                Some(
446                    "Write `spec my_spec`, then reference registry specs as `uses alias: @org/repo spec_name` or `data x: alias.TypeName` after importing with `uses`.",
447                ),
448            ));
449        }
450        if !can_be_spec_name_spelling(&tok.text) {
451            return Err(
452                self.error_at_token(&tok, format!("Expected a spec name, found '{}'", tok.text))
453            );
454        }
455        Ok((tok.text, tok.span))
456    }
457
458    /// Parse a repository qualifier as one opaque [`TokenKind::LogicalName`].
459    fn parse_repository_qualifier(&mut self) -> Result<(RepositoryQualifier, Span), Error> {
460        let tok = self.next_logical_name()?;
461        if !tok.text.starts_with('@') && is_reserved_repository_name(&tok.text) {
462            return Err(self.error_at_token(
463                &tok,
464                format!(
465                    "'{}' is a reserved keyword and cannot be used as a repository name",
466                    tok.text.to_ascii_lowercase()
467                ),
468            ));
469        }
470        Ok((RepositoryQualifier { name: tok.text }, tok.span))
471    }
472
473    /// Parses `[<repository_qualifier>] <spec> [<effective>]`
474    pub fn parse_spec_ref_target(&mut self) -> Result<SpecRef, Error> {
475        let first = self.next_logical_name()?;
476        let mut repository = None;
477        let mut repository_span = None;
478        let (spec_name, spec_name_span) = if first.text.starts_with('@') {
479            repository = Some(RepositoryQualifier {
480                name: first.text.clone(),
481            });
482            repository_span = Some(first.span.clone());
483            self.parse_spec_name()?
484        } else if can_be_label(&self.peek()?.kind) {
485            if is_reserved_repository_name(&first.text) {
486                return Err(self.error_at_token(
487                    &first,
488                    format!(
489                        "'{}' is a reserved keyword and cannot be used as a repository name",
490                        first.text.to_ascii_lowercase()
491                    ),
492                ));
493            }
494            repository = Some(RepositoryQualifier {
495                name: first.text.clone(),
496            });
497            repository_span = Some(first.span.clone());
498            self.parse_spec_name()?
499        } else {
500            (first.text, first.span)
501        };
502        let effective = self.parse_spec_ref_trailing_effective()?;
503        let target_span = self.span_covering(&spec_name_span, &self.last_span);
504
505        let has_repository = repository.is_some();
506        Ok(SpecRef {
507            name: spec_name,
508            repository,
509            effective,
510            repository_span: if has_repository {
511                repository_span
512            } else {
513                None
514            },
515            target_span: Some(target_span),
516        })
517    }
518
519    fn try_parse_effective_from(&mut self) -> Result<Option<DateTimeValue>, Error> {
520        // effective_from is a date/time token right after the spec name.
521        // It's tricky because it looks like a number (e.g. 2026-03-04).
522        // In the old grammar it was a special atomic rule.
523        // We'll check if the next token is a NumberLit that looks like a year.
524        if !self.at(&TokenKind::NumberLit)? {
525            return Ok(None);
526        }
527
528        let peeked = self.peek()?;
529        let peeked_text = peeked.text.clone();
530        let peeked_span = peeked.span.clone();
531
532        // Check if it could be a date: 4-digit number followed by -
533        if peeked_text.len() == 4 && peeked_text.chars().all(|c| c.is_ascii_digit()) {
534            // Collect the full datetime string by consuming tokens
535            let mut dt_str = String::new();
536            let num_tok = self.next()?; // consume the year number
537            dt_str.push_str(&num_tok.text);
538
539            // Try to consume -MM-DD and optional T... parts
540            while self.at(&TokenKind::Minus)? {
541                self.next()?; // consume -
542                dt_str.push('-');
543                let part = self.next()?;
544                dt_str.push_str(&part.text);
545            }
546
547            // Check for T (time part)
548            if self.at(&TokenKind::Identifier)? {
549                let peeked = self.peek()?;
550                if peeked.text.starts_with('T') || peeked.text.starts_with('t') {
551                    let time_part = self.next()?;
552                    dt_str.push_str(&time_part.text);
553                    // Consume any : separated parts
554                    while self.at(&TokenKind::Colon)? {
555                        self.next()?;
556                        dt_str.push(':');
557                        let part = self.next()?;
558                        dt_str.push_str(&part.text);
559                    }
560                    // Check for timezone (+ or Z)
561                    if self.at(&TokenKind::Plus)? {
562                        self.next()?;
563                        dt_str.push('+');
564                        let tz_part = self.next()?;
565                        dt_str.push_str(&tz_part.text);
566                        if self.at(&TokenKind::Colon)? {
567                            self.next()?;
568                            dt_str.push(':');
569                            let tz_min = self.next()?;
570                            dt_str.push_str(&tz_min.text);
571                        }
572                    }
573                }
574            }
575
576            // Try to parse as datetime
577            if let Ok(dtv) = dt_str.parse::<DateTimeValue>() {
578                return Ok(Some(dtv));
579            }
580
581            return Err(Error::parsing(
582                format!("Invalid date/time in spec declaration: '{}'", dt_str),
583                self.make_source(peeked_span),
584                None::<String>,
585            ));
586        }
587
588        Ok(None)
589    }
590
591    fn try_parse_commentary(&mut self) -> Result<Option<String>, Error> {
592        if !self.at(&TokenKind::Commentary)? {
593            return Ok(None);
594        }
595        let token = self.next()?;
596        let trimmed = token.text.trim().to_string();
597        if trimmed.is_empty() {
598            Ok(None)
599        } else {
600            Ok(Some(trimmed))
601        }
602    }
603
604    // ========================================================================
605    // Data parsing
606    // ========================================================================
607
608    fn parse_data(&mut self) -> Result<LemmaData, Error> {
609        let data_token = self.expect(&TokenKind::Data)?;
610        let start_span = data_token.span.clone();
611
612        let reference = self.parse_reference()?;
613        for segment in reference
614            .segments
615            .iter()
616            .chain(std::iter::once(&reference.name))
617        {
618            crate::limits::check_max_length(
619                segment,
620                self.max_data_name_length,
621                "data",
622                Some(Source::new(self.source_type(), start_span.clone())),
623            )?;
624        }
625
626        self.expect(&TokenKind::Colon)?;
627
628        if !reference.segments.is_empty() {
629            let tok = self.peek()?.clone();
630            return Err(self.error_at_token_with_suggestion(
631                &tok,
632                "Dotted paths require `uses` with `-> with`; `data` declares types and values on local names only.",
633                "Use `uses alias: spec` with `  -> with path.to.slot: <value or reference>`.",
634            ));
635        }
636
637        let value = self.parse_data_value()?;
638
639        let span = self.span_covering(&start_span, &self.last_span);
640        let source = self.make_source(span);
641
642        Ok(LemmaData::new(reference, value, source))
643    }
644
645    fn with_rhs_starts_as_literal(kind: &TokenKind) -> bool {
646        matches!(
647            kind,
648            TokenKind::StringLit | TokenKind::NumberLit | TokenKind::Minus | TokenKind::Plus
649        ) || is_boolean_keyword(kind)
650    }
651
652    fn parse_with_rhs(&mut self) -> Result<WithRhs, Error> {
653        let peek_kind = self.peek()?.kind.clone();
654
655        if Self::with_rhs_starts_as_literal(&peek_kind) {
656            let value = self.parse_literal_value()?;
657            return Ok(WithRhs::Literal(value));
658        }
659
660        if can_be_label(&peek_kind) {
661            let target = self.parse_reference()?;
662            if self.at(&TokenKind::Arrow)? && self.lexer.peek_second()?.kind != TokenKind::With {
663                let tok = self.peek()?.clone();
664                return Err(self.error_at_token_with_suggestion(
665                    &tok,
666                    "Constraint chains (`-> ...`) are not allowed on a `uses` binding reference.",
667                    "Use `data name: <type> -> ...` for type constraints on local slots.",
668                ));
669            }
670            return Ok(WithRhs::Reference { target });
671        }
672
673        let tok = self.peek()?.clone();
674        Err(self.error_at_token(
675            &tok,
676            format!(
677                "Expected a reference or literal after `-> with ...:`, found {}",
678                tok.kind
679            ),
680        ))
681    }
682
683    fn parse_reference(&mut self) -> Result<Reference, Error> {
684        let mut segments = Vec::new();
685
686        let first = self.next()?;
687        // Keywords cannot be used as names
688        if is_keyword(&first.kind) {
689            return Err(self.error_at_token_with_suggestion(
690                &first,
691                format!(
692                    "'{}' is a reserved keyword and cannot be used as a name",
693                    first.text.to_ascii_lowercase()
694                ),
695                "Choose a different name that is not a reserved keyword",
696            ));
697        }
698
699        if !can_be_label(&first.kind) {
700            return Err(self.error_at_token(
701                &first,
702                format!("Expected an identifier, found {}", first.kind),
703            ));
704        }
705
706        segments.push(first.text.clone());
707
708        // Consume . separated segments
709        while self.at(&TokenKind::Dot)? {
710            self.next()?; // consume .
711            let seg = self.next()?;
712            if !can_be_label(&seg.kind) {
713                return Err(self.error_at_token(
714                    &seg,
715                    format!("Expected an identifier after '.', found {}", seg.kind),
716                ));
717            }
718            segments.push(seg.text.clone());
719        }
720
721        Ok(Reference::from_path(segments))
722    }
723
724    fn parse_data_value(&mut self) -> Result<DataValue, Error> {
725        if self.at(&TokenKind::Spec)? {
726            let token = self.next()?;
727            return Err(self.error_at_token_with_suggestion(
728                &token,
729                "Cannot import a spec with `data`; use `uses`",
730                "Use `uses <spec_name>` or `uses <alias>: <spec_name>`",
731            ));
732        }
733
734        let peek_kind = self.peek()?.kind.clone();
735
736        if token_kind_to_primitive(&peek_kind).is_some() || can_be_label(&peek_kind) {
737            let (base, constraints) = self.parse_type_arrow_chain()?;
738            return Ok(DataValue::Definition {
739                base: Some(base),
740                constraints,
741                value: None,
742            });
743        }
744
745        // Otherwise, it's a literal value
746        let value = self.parse_literal_value()?;
747        Ok(DataValue::Definition {
748            base: None,
749            constraints: None,
750            value: Some(value),
751        })
752    }
753
754    /// Parse a single `uses` item: `[alias ':']` then [`Self::parse_spec_ref_target`] (optional
755    /// repository qualifier, spec name, optional effective date pin).
756    fn parse_uses_item(&mut self, start_span: &Span) -> Result<LemmaData, Error> {
757        let explicit_alias = if self.lexer.peek_second()?.kind == TokenKind::Colon {
758            let peek = self.peek()?.clone();
759            if is_keyword(&peek.kind) {
760                return Err(self.error_at_token_with_suggestion(
761                    &peek,
762                    format!(
763                        "'{}' is a reserved keyword and cannot be used as a name",
764                        peek.text.to_ascii_lowercase()
765                    ),
766                    "Choose a different name that is not a reserved keyword",
767                ));
768            }
769            if can_be_label(&peek.kind) {
770                let alias_tok = self.next()?;
771                self.expect(&TokenKind::Colon)?;
772                Some(alias_tok)
773            } else {
774                None
775            }
776        } else {
777            None
778        };
779
780        let spec_ref = self.parse_spec_ref_target()?;
781
782        let spec_name_source = spec_ref
783            .target_span
784            .as_ref()
785            .map(|sp| Source::new(self.source_type(), sp.clone()));
786
787        crate::limits::check_max_length(
788            &spec_ref.name,
789            self.max_spec_name_length,
790            "spec",
791            spec_name_source.clone(),
792        )?;
793
794        let alias = if let Some(ref alias_tok) = explicit_alias {
795            crate::limits::check_max_length(
796                &alias_tok.text,
797                self.max_data_name_length,
798                "data",
799                Some(Source::new(self.source_type(), alias_tok.span.clone())),
800            )?;
801            alias_tok.text.clone()
802        } else {
803            let implicit = spec_ref.name.clone();
804            crate::limits::check_max_length(
805                &implicit,
806                self.max_data_name_length,
807                "data",
808                spec_name_source,
809            )?;
810            implicit
811        };
812
813        if self.at(&TokenKind::List)? {
814            self.next()?;
815            let list = self.parse_uses_list(&alias)?;
816            let span = self.span_covering(start_span, &self.last_span);
817            return Ok(LemmaData::new(
818                Reference::local(alias),
819                DataValue::ImportList { spec_ref, list },
820                self.make_source(span),
821            ));
822        }
823
824        let mut bindings = Vec::new();
825        while self.at(&TokenKind::Arrow)? {
826            if self.lexer.peek_second()?.kind != TokenKind::With {
827                let tok = self.peek()?.clone();
828                return Err(self.error_at_token_with_suggestion(
829                    &tok,
830                    "Expected `with` after `->` in a `uses` block.",
831                    "Write `  -> with path.to.slot: <value or reference>` under the `uses` line.",
832                ));
833            }
834            self.next()?; // ->
835            let with_token = self.expect(&TokenKind::With)?;
836            bindings.extend(self.parse_uses_field_pairs(&alias, &with_token)?);
837        }
838
839        let span = self.span_covering(start_span, &self.last_span);
840        Ok(LemmaData::new(
841            Reference::local(alias),
842            DataValue::Import { spec_ref, bindings },
843            self.make_source(span),
844        ))
845    }
846
847    /// `-> key`, `-> with` rows; query commands under `uses x: spec list` are a parse Error.
848    fn parse_uses_list(&mut self, alias: &str) -> Result<UsesList, Error> {
849        let mut list = UsesList {
850            key: None,
851            rows: Vec::new(),
852        };
853        while self.at(&TokenKind::Arrow)? {
854            let arrow = self.next()?;
855            let name_tok = self.next()?;
856            match name_tok.kind {
857                TokenKind::Key => {
858                    if list.key.is_some() {
859                        return Err(self.error_at_token(
860                            &name_tok,
861                            format!("`{alias}` declares `-> key` twice; list every key field in one `-> key`"),
862                        ));
863                    }
864                    let mut fields = vec![self.parse_reference()?];
865                    while self.at(&TokenKind::Comma)? {
866                        self.next()?;
867                        fields.push(self.parse_reference()?);
868                    }
869                    let span = self.span_covering(&arrow.span, &self.last_span);
870                    list.key = Some(UsesListKey {
871                        fields,
872                        source_location: self.make_source(span),
873                    });
874                }
875                TokenKind::With => {
876                    let row = if self.at_uses_field_pair()? {
877                        let bindings = self.parse_uses_field_pairs(alias, &name_tok)?;
878                        let span = self.span_covering(&arrow.span, &self.last_span);
879                        UsesListRow::Fields {
880                            bindings,
881                            source_location: self.make_source(span),
882                        }
883                    } else {
884                        let values = self.parse_uses_list_values()?;
885                        let span = self.span_covering(&arrow.span, &self.last_span);
886                        UsesListRow::Values {
887                            values,
888                            source_location: self.make_source(span),
889                        }
890                    };
891                    list.rows.push(row);
892                }
893                _ => {
894                    let command = self.parse_list_command_body(name_tok)?;
895                    let command = display_list_command(&self.table, &command);
896                    return Err(self.error_at_token(
897                        &arrow,
898                        format!(
899                            "data supplies values; `-> {command}` is a query on `{alias}`, write it in a rule"
900                        ),
901                    ));
902                }
903            }
904        }
905        Ok(list)
906    }
907
908    /// `-> with ada, grace` under `uses x: spec list`: references to instances (literals are
909    /// rejected at planning with the row spec's name).
910    fn parse_uses_list_values(&mut self) -> Result<Vec<WithRhs>, Error> {
911        let mut values = Vec::new();
912        loop {
913            values.push(self.parse_with_rhs()?);
914            if !self.at(&TokenKind::Comma)? {
915                break;
916            }
917            self.next()?;
918            if self.at_command_terminator()? || self.at(&TokenKind::Comma)? {
919                let tok = self.peek()?.clone();
920                return Err(self.error_at_token_with_suggestion(
921                    &tok,
922                    "Trailing or empty comma in list".to_string(),
923                    "Remove the extra comma, or add the missing row",
924                ));
925            }
926        }
927        if self.at_uses_field_pair()? {
928            let tok = self.peek()?.clone();
929            return Err(self.error_at_token_with_suggestion(
930                &tok,
931                "A `-> with` line holds either references or field pairs, not both".to_string(),
932                "Put the field pairs on their own `-> with` line",
933            ));
934        }
935        Ok(values)
936    }
937
938    /// Comma-separated `path: value` pairs after `-> with` in a `uses` block.
939    fn parse_uses_field_pairs(
940        &mut self,
941        alias: &str,
942        with_token: &Token,
943    ) -> Result<Vec<UsesBinding>, Error> {
944        let mut bindings = Vec::new();
945        let mut binding_start_span = with_token.span.clone();
946        loop {
947            let path = self.parse_reference()?;
948            for segment in path.segments.iter().chain(std::iter::once(&path.name)) {
949                crate::limits::check_max_length(
950                    segment,
951                    self.max_data_name_length,
952                    "uses binding",
953                    Some(Source::new(self.source_type(), binding_start_span.clone())),
954                )?;
955            }
956            if path
957                .segments
958                .first()
959                .is_some_and(|segment| segment == alias)
960            {
961                return Err(self.error_at_token_with_suggestion(
962                    with_token,
963                    format!(
964                        "Binding path must be relative to the imported spec, not prefixed with import alias `{alias}`."
965                    ),
966                    format!(
967                        "Use `-> with {}: …` without the `{alias}.` prefix.",
968                        path.name
969                    ),
970                ));
971            }
972
973            let (rhs, _) =
974                self.parse_assignment_after_key(|parser| parser.parse_with_rhs(), "with", false)?;
975
976            let binding_span = self.span_covering(&binding_start_span, &self.last_span);
977            bindings.push(UsesBinding {
978                path,
979                rhs,
980                source_location: self.make_source(binding_span),
981            });
982
983            if self.at(&TokenKind::Comma)? {
984                self.next()?;
985                if self.at_command_terminator()? || self.at(&TokenKind::Comma)? {
986                    let tok = self.peek()?.clone();
987                    return Err(self.error_at_token_with_suggestion(
988                        &tok,
989                        "Trailing or empty comma in list".to_string(),
990                        "Remove the extra comma, or add the missing field pair",
991                    ));
992                }
993                if !self.at_uses_field_pair()? {
994                    let tok = self.peek()?.clone();
995                    return Err(self.error_at_token_with_suggestion(
996                        &tok,
997                        "Expected a field pair after comma".to_string(),
998                        "Remove the extra comma, or add `path: value`",
999                    ));
1000                }
1001                binding_start_span = self.peek()?.span.clone();
1002                continue;
1003            }
1004
1005            if self.at_uses_field_pair()? {
1006                let tok = self.peek()?.clone();
1007                return Err(self.error_at_token_with_suggestion(
1008                    &tok,
1009                    "Expected a comma between field pairs".to_string(),
1010                    "Separate field pairs with commas, for example `-> with qty: 3, sku: \"latte\"`",
1011                ));
1012            }
1013            return Ok(bindings);
1014        }
1015    }
1016
1017    fn at_uses_field_pair(&mut self) -> Result<bool, Error> {
1018        if self.at_command_terminator()? {
1019            return Ok(false);
1020        }
1021        self.at_path_then_colon()
1022    }
1023
1024    /// True when the next tokens are `label (. label)* :`, the start of a field pair.
1025    /// Does not consume input.
1026    fn at_path_then_colon(&mut self) -> Result<bool, Error> {
1027        if !can_be_label(&self.peek()?.kind) {
1028            return Ok(false);
1029        }
1030        let checkpoint = self.checkpoint();
1031        let result = self.scan_path_then_colon();
1032        self.restore(checkpoint);
1033        result
1034    }
1035
1036    fn scan_path_then_colon(&mut self) -> Result<bool, Error> {
1037        self.next()?;
1038        loop {
1039            match self.peek()?.kind {
1040                TokenKind::Colon => return Ok(true),
1041                TokenKind::Dot => {
1042                    self.next()?;
1043                    if !can_be_label(&self.peek()?.kind) {
1044                        return Ok(false);
1045                    }
1046                    self.next()?;
1047                }
1048                _ => return Ok(false),
1049            }
1050        }
1051    }
1052
1053    fn parse_uses_statement(&mut self) -> Result<LemmaData, Error> {
1054        let uses_token = self.expect(&TokenKind::Uses)?;
1055        let start_span = uses_token.span.clone();
1056        self.parse_uses_item(&start_span)
1057    }
1058
1059    // ========================================================================
1060    // Rule parsing
1061    // ========================================================================
1062
1063    fn parse_rule(&mut self) -> Result<LemmaRule, Error> {
1064        let rule_token = self.expect(&TokenKind::Rule)?;
1065        let start_span = rule_token.span.clone();
1066
1067        let name_tok = self.next()?;
1068        if is_keyword(&name_tok.kind) {
1069            return Err(self.error_at_token_with_suggestion(
1070                &name_tok,
1071                format!(
1072                    "'{}' is a reserved keyword and cannot be used as a rule name",
1073                    name_tok.text.to_ascii_lowercase()
1074                ),
1075                "Choose a different name that is not a reserved keyword",
1076            ));
1077        }
1078        if !can_be_label(&name_tok.kind) {
1079            return Err(self.error_at_token(
1080                &name_tok,
1081                format!("Expected a rule name, found {}", name_tok.kind),
1082            ));
1083        }
1084        let rule_name = name_tok.text.clone();
1085        crate::limits::check_max_length(
1086            &rule_name,
1087            self.max_rule_name_length,
1088            "rule",
1089            Some(Source::new(self.source_type(), name_tok.span.clone())),
1090        )?;
1091
1092        self.expect(&TokenKind::Colon)?;
1093
1094        // Parse the base expression or veto result (`veto "msg"`). `veto is …` is an expression.
1095        let root = if self.at(&TokenKind::Veto)? && !self.at_bare_veto_followed_by_is()? {
1096            self.parse_veto_expression()?
1097        } else {
1098            self.parse_expression()?
1099        };
1100
1101        let expression = self.finish_expression(root);
1102
1103        // Parse unless clauses
1104        let mut unless_clauses = Vec::new();
1105        while self.at(&TokenKind::Unless)? {
1106            unless_clauses.push(self.parse_unless_clause()?);
1107        }
1108
1109        let end_span = if let Some(last_unless) = unless_clauses.last() {
1110            last_unless.source_location.span.clone()
1111        } else if let Some(loc) = expression.root_source() {
1112            loc.span.clone()
1113        } else {
1114            start_span.clone()
1115        };
1116
1117        let span = self.span_covering(&start_span, &end_span);
1118        Ok(LemmaRule {
1119            name: rule_name,
1120            expression,
1121            unless_clauses,
1122            source_location: self.make_source(span),
1123        })
1124    }
1125
1126    fn parse_veto_expression(&mut self) -> Result<ExprId, Error> {
1127        let veto_tok = self.expect(&TokenKind::Veto)?;
1128        let start_span = veto_tok.span.clone();
1129
1130        let message = if self.at(&TokenKind::StringLit)? {
1131            let str_tok = self.next()?;
1132            Some(str_tok.text)
1133        } else {
1134            None
1135        };
1136
1137        let span = self.span_from(&start_span);
1138        self.new_expression(
1139            ExpressionKind::Veto(VetoExpression { message }),
1140            self.make_source(span),
1141        )
1142    }
1143
1144    fn parse_unless_clause(&mut self) -> Result<UnlessClause, Error> {
1145        let unless_tok = self.expect(&TokenKind::Unless)?;
1146        let start_span = unless_tok.span.clone();
1147
1148        let condition_root = self.parse_expression()?;
1149        let condition = self.finish_expression(condition_root);
1150
1151        self.expect(&TokenKind::Then)?;
1152
1153        let result_root = if self.at(&TokenKind::Veto)? {
1154            self.parse_veto_expression()?
1155        } else {
1156            self.parse_expression()?
1157        };
1158
1159        let end_span = self
1160            .expr_span(result_root)
1161            .unwrap_or_else(|| start_span.clone());
1162        let span = self.span_covering(&start_span, &end_span);
1163        let result = self.finish_expression(result_root);
1164
1165        Ok(UnlessClause {
1166            condition,
1167            result,
1168            source_location: self.make_source(span),
1169        })
1170    }
1171
1172    /// Parse a type arrow chain: [`ParentType`] followed by `(-> command)*`.
1173    fn parse_type_arrow_chain(&mut self) -> Result<(ParentType, Option<Vec<Constraint>>), Error> {
1174        let peek_kind = self.peek()?.kind.clone();
1175        let base = if let Some(primitive) = token_kind_to_primitive(&peek_kind) {
1176            self.next()?;
1177            if self.at(&TokenKind::Dot)? {
1178                let dot_tok = self.peek()?.clone();
1179                return Err(self.error_at_token_with_suggestion(
1180                    &dot_tok,
1181                    "A primitive type cannot be the left segment of a qualified parent path",
1182                    "Use `data name: alias.typename` where `alias` is the `uses` import name and `typename` is the parent type.",
1183                ));
1184            }
1185            ParentType::Primitive { primitive }
1186        } else {
1187            let (name, _) = self.parse_spec_name()?;
1188            if let Some((alias, type_name)) = name.rsplit_once('.') {
1189                ParentType::Qualified {
1190                    spec_alias: alias.to_string(),
1191                    inner: Box::new(ParentType::Custom {
1192                        name: type_name.to_string(),
1193                    }),
1194                }
1195            } else {
1196                ParentType::Custom { name }
1197            }
1198        };
1199
1200        let base = if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
1201            self.next()?;
1202            ParentType::Ranged {
1203                inner: Box::new(base),
1204            }
1205        } else {
1206            base
1207        };
1208
1209        let base = if self.at(&TokenKind::List)? {
1210            self.next()?;
1211            ParentType::List {
1212                inner: Box::new(base),
1213            }
1214        } else {
1215            base
1216        };
1217
1218        let constraints = self.parse_trailing_constraints()?;
1219
1220        Ok((base, constraints))
1221    }
1222
1223    fn parse_trailing_constraints(&mut self) -> Result<Option<Vec<Constraint>>, Error> {
1224        let mut commands = Vec::new();
1225        while self.at(&TokenKind::Arrow)? {
1226            let arrow_token = self.next()?;
1227            let constraint_start_span = arrow_token.span.clone();
1228            let (cmd, cmd_args, deprecated_without_colon) = self.parse_constraint_command()?;
1229            let constraint_span = self.span_covering(&constraint_start_span, &self.last_span);
1230            commands.push(Constraint {
1231                command: cmd,
1232                args: cmd_args,
1233                source_location: self.make_source(constraint_span),
1234                deprecated_without_colon,
1235            });
1236        }
1237        let constraints = if commands.is_empty() {
1238            None
1239        } else {
1240            Some(commands)
1241        };
1242        Ok(constraints)
1243    }
1244
1245    fn parse_constraint_command(
1246        &mut self,
1247    ) -> Result<(TypeConstraintCommand, Vec<CommandArg>, bool), Error> {
1248        let name_tok = self.next()?;
1249        if matches!(name_tok.kind, TokenKind::Key) {
1250            return Err(self.error_at_token(
1251                &name_tok,
1252                "value lists are numbered; `-> key` is for instance lists".to_string(),
1253            ));
1254        }
1255        if matches!(
1256            name_tok.kind,
1257            TokenKind::Find
1258                | TokenKind::First
1259                | TokenKind::Last
1260                | TokenKind::Sum
1261                | TokenKind::Avg
1262                | TokenKind::Product
1263                | TokenKind::Count
1264                | TokenKind::Min
1265                | TokenKind::Max
1266                | TokenKind::Any
1267                | TokenKind::All
1268                | TokenKind::Where
1269                | TokenKind::Sort
1270                | TokenKind::Top
1271        ) {
1272            return Err(self.error_at_token(
1273                &name_tok,
1274                "data supplies values; query commands belong on rules, not data declarations"
1275                    .to_string(),
1276            ));
1277        }
1278        if !can_be_label(&name_tok.kind)
1279            && !matches!(
1280                name_tok.kind,
1281                TokenKind::Lower | TokenKind::Upper | TokenKind::With
1282            )
1283        {
1284            return Err(self.error_at_token(
1285                &name_tok,
1286                format!("Expected a command name, found {}", name_tok.kind),
1287            ));
1288        }
1289        let cmd = try_parse_type_constraint_command(&name_tok.text).ok_or_else(|| {
1290            self.error_at_token(
1291                &name_tok,
1292                format!(
1293                    "Unknown constraint command '{}'. Valid commands: help, suggest, with, unit, trait, minimum, maximum, lower, upper, decimals, option, options, length",
1294                    name_tok.text
1295                ),
1296            )
1297        })?;
1298
1299        match cmd.continuation_shape() {
1300            ContinuationShape::Assignment => {
1301                let (args, deprecated_without_colon) = self.parse_unit_as_assignment()?;
1302                Ok((cmd, args, deprecated_without_colon))
1303            }
1304            ContinuationShape::Single => Ok((cmd, self.parse_single_command_arg(cmd)?, false)),
1305            ContinuationShape::CommaSeparated => {
1306                Ok((cmd, self.parse_comma_separated_command_args()?, false))
1307            }
1308        }
1309    }
1310
1311    /// Shared spine for assignment continuations (`with`, `unit`): `key: value` or deprecated space.
1312    fn parse_assignment_after_key<T>(
1313        &mut self,
1314        parse_value: impl FnOnce(&mut Self) -> Result<T, Error>,
1315        assignment_context: &str,
1316        allow_deprecated_without_colon: bool,
1317    ) -> Result<(T, bool), Error> {
1318        if self.at(&TokenKind::Colon)? {
1319            self.next()?;
1320            let value = parse_value(self)?;
1321            return Ok((value, false));
1322        }
1323        if allow_deprecated_without_colon {
1324            let value = parse_value(self)?;
1325            return Ok((value, true));
1326        }
1327        let tok = self.peek()?.clone();
1328        Err(self.error_at_token_with_suggestion(
1329            &tok,
1330            format!("Expected `:` after `{assignment_context}` key."),
1331            format!("Use `-> {assignment_context} <key>: <value>`."),
1332        ))
1333    }
1334
1335    fn parse_unit_as_assignment(&mut self) -> Result<(Vec<CommandArg>, bool), Error> {
1336        if self.at_command_terminator()? {
1337            return Ok((Vec::new(), false));
1338        }
1339
1340        let peek_kind = self.peek()?.kind.clone();
1341        if is_keyword(&peek_kind) {
1342            let tok = self.peek()?.clone();
1343            return Err(self.error_at_token_with_suggestion(
1344                &tok,
1345                format!(
1346                    "'{}' is a reserved keyword and cannot be used as a unit name",
1347                    tok.text.to_ascii_lowercase()
1348                ),
1349                "Choose a different name that is not a reserved keyword",
1350            ));
1351        }
1352        if !can_be_label(&peek_kind) {
1353            return Ok((Vec::new(), false));
1354        }
1355
1356        let unit_name_tok = self.next()?;
1357        let unit_name_arg = CommandArg::Label(unit_name_tok.text.clone());
1358
1359        let (unit_arg, deprecated_without_colon) =
1360            self.parse_assignment_after_key(|parser| parser.parse_unit_payload(), "unit", true)?;
1361
1362        Ok((
1363            vec![unit_name_arg, CommandArg::UnitExpr(unit_arg)],
1364            deprecated_without_colon,
1365        ))
1366    }
1367
1368    /// Value side of `-> unit <name>: <payload>` (factor, compound, or prefix + compound).
1369    fn parse_unit_payload(&mut self) -> Result<UnitArg, Error> {
1370        if self.at_command_terminator()? {
1371            let tok = self.peek()?.clone();
1372            return Err(self.error_at_token_with_suggestion(
1373                &tok,
1374                "Expected a unit conversion factor or compound unit expression after `:`.",
1375                "Use `-> unit <name>: <factor>` or `-> unit <name>: <compound>` (e.g. `unit eur: 1.00`, `unit eur_per_hour: eur/hour`).",
1376            ));
1377        }
1378
1379        let numeric_prefix: Option<Decimal> = if self.at(&TokenKind::NumberLit)? {
1380            let num_tok = self.next()?;
1381            Some(parse_decimal_string(&num_tok.text, &num_tok.span, self)?)
1382        } else {
1383            None
1384        };
1385
1386        let peek_kind_after_prefix = self.peek()?.kind.clone();
1387        let has_compound_expr =
1388            can_be_unit_name(&peek_kind_after_prefix) && !self.at_command_terminator()?;
1389
1390        if has_compound_expr {
1391            let factors = self.parse_unit_factors()?;
1392            let prefix = numeric_prefix.unwrap_or(Decimal::ONE);
1393            Ok(UnitArg::Expr(prefix, factors))
1394        } else if let Some(factor) = numeric_prefix {
1395            Ok(UnitArg::Factor(factor))
1396        } else {
1397            let tok = self.peek()?.clone();
1398            Err(self.error_at_token_with_suggestion(
1399                &tok,
1400                "Expected a unit conversion factor or compound unit expression after `:`.",
1401                "Use `-> unit <name>: <factor>` or `-> unit <name>: <compound>` (e.g. `unit eur: 1.00`, `unit eur_per_hour: eur/hour`).",
1402            ))
1403        }
1404    }
1405
1406    fn parse_single_command_arg(
1407        &mut self,
1408        cmd: TypeConstraintCommand,
1409    ) -> Result<Vec<CommandArg>, Error> {
1410        let Some(arg) = self.try_parse_one_command_arg()? else {
1411            return Ok(Vec::new());
1412        };
1413        if self.at(&TokenKind::Comma)? || self.command_arg_starts_without_comma()? {
1414            let tok = self.peek()?.clone();
1415            return Err(self.error_at_token_with_suggestion(
1416                &tok,
1417                format!("`-> {cmd}` takes one value"),
1418                format!("Write `-> {cmd}` with a single argument"),
1419            ));
1420        }
1421        Ok(vec![arg])
1422    }
1423
1424    fn try_parse_one_command_arg(&mut self) -> Result<Option<CommandArg>, Error> {
1425        if self.at(&TokenKind::Arrow)?
1426            || self.at(&TokenKind::Eof)?
1427            || is_spec_body_keyword(&self.peek()?.kind)
1428            || self.at(&TokenKind::Spec)?
1429            || self.at(&TokenKind::Comma)?
1430        {
1431            return Ok(None);
1432        }
1433
1434        let peek_kind = self.peek()?.kind.clone();
1435        match peek_kind {
1436            TokenKind::NumberLit | TokenKind::Minus | TokenKind::Plus | TokenKind::StringLit => {
1437                let value = self.parse_literal_value()?;
1438                Ok(Some(CommandArg::Literal(value)))
1439            }
1440            ref k if is_boolean_keyword(k) => {
1441                let value = self.parse_literal_value()?;
1442                Ok(Some(CommandArg::Literal(value)))
1443            }
1444            ref k if can_be_label(k) => {
1445                let tok = self.next()?;
1446                Ok(Some(CommandArg::Label(tok.text)))
1447            }
1448            _ => Ok(None),
1449        }
1450    }
1451
1452    fn parse_comma_separated_command_args(&mut self) -> Result<Vec<CommandArg>, Error> {
1453        let mut args = Vec::new();
1454        if self.at_command_terminator()? {
1455            return Ok(args);
1456        }
1457        if self.at(&TokenKind::Comma)? {
1458            let tok = self.next()?;
1459            return Err(self.error_at_token_with_suggestion(
1460                &tok,
1461                "Unexpected comma".to_string(),
1462                "Remove the extra comma, or put a value before it",
1463            ));
1464        }
1465
1466        loop {
1467            let Some(arg) = self.try_parse_one_command_arg()? else {
1468                let tok = self.peek()?.clone();
1469                return Err(self.error_at_token_with_suggestion(
1470                    &tok,
1471                    "Expected a value after comma".to_string(),
1472                    "Remove the extra comma, or add the missing value",
1473                ));
1474            };
1475            args.push(arg);
1476
1477            if self.at(&TokenKind::Comma)? {
1478                self.next()?;
1479                if self.at_command_terminator()? || self.at(&TokenKind::Comma)? {
1480                    let tok = self.peek()?.clone();
1481                    return Err(self.error_at_token_with_suggestion(
1482                        &tok,
1483                        "Trailing or empty comma in list".to_string(),
1484                        "Remove the extra comma, or add the missing value",
1485                    ));
1486                }
1487                continue;
1488            }
1489
1490            if self.command_arg_starts_without_comma()? {
1491                let tok = self.peek()?.clone();
1492                return Err(self.error_at_token_with_suggestion(
1493                    &tok,
1494                    "Expected a comma between items".to_string(),
1495                    "Separate items with commas, for example `-> options \"a\", \"b\"`",
1496                ));
1497            }
1498            break;
1499        }
1500        Ok(args)
1501    }
1502
1503    fn command_arg_starts_without_comma(&mut self) -> Result<bool, Error> {
1504        if self.at_command_terminator()? || self.at(&TokenKind::Comma)? {
1505            return Ok(false);
1506        }
1507        let peek_kind = self.peek()?.kind.clone();
1508        Ok(matches!(
1509            peek_kind,
1510            TokenKind::NumberLit | TokenKind::Minus | TokenKind::Plus | TokenKind::StringLit
1511        ) || is_boolean_keyword(&peek_kind)
1512            || can_be_label(&peek_kind))
1513    }
1514
1515    fn parse_scalar_literal_value(&mut self) -> Result<Value, Error> {
1516        self.parse_scalar_literal_value_with_context("value (number, text, boolean, date, etc.)")
1517    }
1518
1519    fn parse_scalar_literal_value_with_context(&mut self, expected: &str) -> Result<Value, Error> {
1520        let peeked = self.peek()?;
1521        match &peeked.kind {
1522            TokenKind::StringLit => {
1523                let tok = self.next()?;
1524                Ok(Value::Text(tok.text))
1525            }
1526            k if is_boolean_keyword(k) => {
1527                let tok = self.next()?;
1528                Ok(Value::Boolean(token_kind_to_boolean_value(&tok.kind)))
1529            }
1530            TokenKind::NumberLit => self.parse_number_literal(),
1531            TokenKind::Minus | TokenKind::Plus => self.parse_signed_number_literal(),
1532            _ => {
1533                let tok = self.next()?;
1534                Err(self
1535                    .error_at_token(&tok, format!("Expected a {expected}, found '{}'", tok.text)))
1536            }
1537        }
1538    }
1539
1540    /// Returns true when the current token ends the current command argument list.
1541    fn at_command_terminator(&mut self) -> Result<bool, Error> {
1542        if self.at(&TokenKind::Arrow)? || self.at(&TokenKind::Eof)? || self.at(&TokenKind::Spec)? {
1543            return Ok(true);
1544        }
1545        Ok(is_spec_body_keyword(&self.peek()?.kind))
1546    }
1547
1548    /// Parse a sequence of `<measure_ref>[^[-]<integer>]` terms joined by `*` or `/`.
1549    ///
1550    /// `*` is explicit multiplication and resets to numerator mode.
1551    /// `/` switches to denominator mode for all subsequent factors until the next `*`.
1552    /// Exponents in denominator mode are negated.
1553    /// An explicit `^<integer>` overrides the default (±1), still relative to the current mode.
1554    ///
1555    /// Space has no meaning in Lemma — `kg * m / s^2` and `kg*m/s^2` are identical.
1556    /// Space-adjacent labels without an intervening `*` or `/` end the expression;
1557    /// the bare label belongs to the next syntactic element.
1558    ///
1559    /// Examples:
1560    /// - `meter/second`            → `[{meter, +1}, {second, -1}]`
1561    /// - `meter/second^2`          → `[{meter, +1}, {second, -2}]`
1562    /// - `kg * meter / second^2`   → `[{kg, +1}, {meter, +1}, {second, -2}]`
1563    /// - `meter / second * kg`     → `[{meter, +1}, {second, -1}, {kg, +1}]`
1564    fn parse_unit_factors(&mut self) -> Result<Vec<UnitFactor>, Error> {
1565        let mut factors: Vec<UnitFactor> = Vec::new();
1566        let mut summed_exponents: std::collections::BTreeMap<String, i32> =
1567            std::collections::BTreeMap::new();
1568        let mut denominator_mode = false;
1569        // Tracks whether the loop is sitting immediately after an operator (* or /).
1570        // On the first iteration there is an implicit "we just started", so we allow
1571        // the first label without a preceding operator.
1572        let mut operator_just_consumed = true;
1573
1574        loop {
1575            if self.at_command_terminator()? {
1576                if !operator_just_consumed {
1577                    break;
1578                }
1579                // An operator was consumed but no label followed — that is a parse error
1580                // only if we already emitted at least one factor (dangling operator).
1581                // If factors is empty the caller will handle the missing expression.
1582                break;
1583            }
1584
1585            // `*` — explicit multiplication; reset to numerator mode.
1586            if self.at(&TokenKind::Star)? {
1587                if operator_just_consumed && !factors.is_empty() {
1588                    let bad_tok = self.next()?;
1589                    return Err(self.error_at_token(
1590                        &bad_tok,
1591                        "Unexpected '*' in unit expression: two consecutive operators".to_string(),
1592                    ));
1593                }
1594                self.next()?;
1595                denominator_mode = false;
1596                operator_just_consumed = true;
1597                continue;
1598            }
1599
1600            // `/` — switch to denominator mode.
1601            if self.at(&TokenKind::Slash)? {
1602                if operator_just_consumed && !factors.is_empty() {
1603                    let bad_tok = self.next()?;
1604                    return Err(self.error_at_token(
1605                        &bad_tok,
1606                        "Unexpected '/' in unit expression: two consecutive operators".to_string(),
1607                    ));
1608                }
1609                self.next()?;
1610                denominator_mode = true;
1611                operator_just_consumed = true;
1612                continue;
1613            }
1614
1615            // A label is a measure reference.
1616            let peek_kind = self.peek()?.kind.clone();
1617            if !can_be_unit_name(&peek_kind) {
1618                break;
1619            }
1620
1621            // A label without a preceding operator ends the expression.
1622            // The first factor is permitted without an operator (operator_just_consumed starts true).
1623            if !operator_just_consumed {
1624                break;
1625            }
1626            operator_just_consumed = false;
1627
1628            let (measure_ref, measure_span) = self.parse_unit_path()?;
1629
1630            // Optional exponent: `^` followed by an optional `-` and an integer.
1631            let explicit_exp: Option<i32> = if self.at(&TokenKind::Caret)? {
1632                self.next()?; // consume `^`
1633
1634                let negative = if self.at(&TokenKind::Minus)? {
1635                    self.next()?; // consume `-`
1636                    true
1637                } else {
1638                    false
1639                };
1640
1641                if !self.at(&TokenKind::NumberLit)? {
1642                    let bad_tok = self.next()?;
1643                    return Err(self.error_at_token(
1644                        &bad_tok,
1645                        format!(
1646                            "Expected an integer exponent after '^' in unit expression, found {}",
1647                            bad_tok.kind
1648                        ),
1649                    ));
1650                }
1651
1652                let exp_tok = self.next()?;
1653                let raw: i32 = exp_tok.text.parse::<i32>().map_err(|_| {
1654                    self.error_at_token(
1655                        &exp_tok,
1656                        format!(
1657                            "Exponent '{}' is not a valid integer in unit expression",
1658                            exp_tok.text
1659                        ),
1660                    )
1661                })?;
1662
1663                if raw == 0 {
1664                    return Err(self.error_at_token(
1665                        &exp_tok,
1666                        "Exponent cannot be zero in a unit expression".to_string(),
1667                    ));
1668                }
1669
1670                Some(if negative { -raw } else { raw })
1671            } else {
1672                None
1673            };
1674
1675            // Apply denominator mode: negate the exponent (or its default) when in denominator.
1676            let final_exp = match (explicit_exp, denominator_mode) {
1677                (Some(exponent), true) => -exponent,
1678                (Some(exponent), false) => exponent,
1679                (None, true) => -1,
1680                (None, false) => 1,
1681            };
1682
1683            let summed = summed_exponents
1684                .entry(crate::parsing::ast::canonical_logical_name(&measure_ref).into_owned())
1685                .or_insert(0i32);
1686            *summed = summed
1687                .checked_add(final_exp)
1688                .filter(|sum| sum.unsigned_abs() <= crate::limits::MAX_UNIT_EXPONENT.unsigned_abs())
1689                .ok_or_else(|| {
1690                    Error::parsing(
1691                        format!(
1692                            "Unit '{}' exceeds the maximum unit exponent of {} in this unit expression",
1693                            measure_ref,
1694                            crate::limits::MAX_UNIT_EXPONENT
1695                        ),
1696                        Source::new(self.source_type(), measure_span),
1697                        None::<String>,
1698                    )
1699                })?;
1700
1701            factors.push(UnitFactor {
1702                measure_ref,
1703                exp: final_exp,
1704            });
1705        }
1706
1707        Ok(factors)
1708    }
1709
1710    // ========================================================================
1711    // Meta parsing
1712    // ========================================================================
1713
1714    fn parse_meta(&mut self) -> Result<MetaField, Error> {
1715        let meta_tok = self.expect(&TokenKind::Meta)?;
1716        let start_span = meta_tok.span.clone();
1717
1718        let key_tok = self.next()?;
1719        let key = key_tok.text.clone();
1720
1721        self.expect(&TokenKind::Colon)?;
1722
1723        let value = self.parse_meta_literal_value()?;
1724
1725        let span = self.span_covering(&start_span, &self.last_span);
1726
1727        Ok(MetaField {
1728            key,
1729            value,
1730            source_location: self.make_source(span),
1731        })
1732    }
1733
1734    // ========================================================================
1735    // Literal value parsing
1736    // ========================================================================
1737
1738    fn parse_meta_literal_value(&mut self) -> Result<Value, Error> {
1739        if self.at(&TokenKind::Ellipsis)? {
1740            let tok = self.peek()?.clone();
1741            return Err(self.error_at_token(&tok, "Expected a meta literal, found a range"));
1742        }
1743        self.parse_scalar_literal_value_with_context("meta literal")
1744    }
1745
1746    fn parse_literal_value(&mut self) -> Result<Value, Error> {
1747        let left = self.parse_scalar_literal_value()?;
1748        if self.at(&TokenKind::Ellipsis)? {
1749            self.next()?;
1750            let right = self.parse_scalar_literal_value()?;
1751            Ok(Value::Range(Box::new(left), Box::new(right)))
1752        } else {
1753            Ok(left)
1754        }
1755    }
1756
1757    fn parse_signed_number_literal(&mut self) -> Result<Value, Error> {
1758        let sign_tok = self.next()?;
1759        let sign_span = sign_tok.span.clone();
1760        let is_negative = sign_tok.kind == TokenKind::Minus;
1761
1762        if !self.at(&TokenKind::NumberLit)? {
1763            let tok = self.peek()?.clone();
1764            return Err(self.error_at_token(
1765                &tok,
1766                format!(
1767                    "Expected a number after '{}', found '{}'",
1768                    sign_tok.text, tok.text
1769                ),
1770            ));
1771        }
1772
1773        let value = self.parse_number_literal()?;
1774        if !is_negative {
1775            return Ok(value);
1776        }
1777        match try_negate_numeric_literal(value) {
1778            Ok(negated) => Ok(negated),
1779            Err(other) => Err(Error::parsing(
1780                format!("Cannot negate this value: {}", other),
1781                self.make_source(sign_span),
1782                None::<String>,
1783            )),
1784        }
1785    }
1786
1787    fn parse_number_literal(&mut self) -> Result<Value, Error> {
1788        let num_tok = self.next()?;
1789        let num_text = &num_tok.text;
1790        let num_span = num_tok.span.clone();
1791
1792        if self.at(&TokenKind::Comma)? {
1793            let comma = self.peek()?.clone();
1794            let next = self.lexer.peek_second()?.clone();
1795            let digits_grouped = next.kind == TokenKind::NumberLit
1796                && comma.span.start == num_span.end
1797                && next.span.start == comma.span.end;
1798            if digits_grouped {
1799                let grouped = format!("{}_{}", num_text, next.text);
1800                return Err(self.error_at_token_with_suggestion(
1801                    &comma,
1802                    format!(
1803                        "`,` is a list separator, not a digit separator. Write `{grouped}` instead of `{num_text},{}`",
1804                        next.text
1805                    ),
1806                    format!("Use `{grouped}`"),
1807                ));
1808            }
1809        }
1810
1811        // Check if followed by - which could make it a date (YYYY-MM-DD)
1812        if num_text.len() == 4
1813            && num_text.chars().all(|c| c.is_ascii_digit())
1814            && self.at(&TokenKind::Minus)?
1815        {
1816            return self.parse_date_literal(num_text.clone(), num_span);
1817        }
1818
1819        // Check what follows the number
1820        let peeked = self.peek()?;
1821
1822        // Number followed by : could be a time literal (HH:MM:SS)
1823        if num_text.len() == 2
1824            && num_text.chars().all(|c| c.is_ascii_digit())
1825            && peeked.kind == TokenKind::Colon
1826        {
1827            // Only if we're in a data value context... this is ambiguous.
1828            // Time literals look like: 14:30:00 or 14:30
1829            // But we might also have "rule x: expr" where : is assignment.
1830            // The grammar handles this at the grammar level. For us,
1831            // we need to check if the context is right.
1832            // Let's try to parse as time if the following pattern matches.
1833            return self.try_parse_time_literal(num_text.clone(), num_span);
1834        }
1835
1836        // Check for %% (permille) - must be before %
1837        if peeked.kind == TokenKind::PercentPercent {
1838            let pp_tok = self.next()?;
1839            // Check it's not followed by a digit
1840            if let Ok(next_peek) = self.peek() {
1841                if next_peek.kind == TokenKind::NumberLit {
1842                    return Err(self.error_at_token(
1843                        &pp_tok,
1844                        "Permille literal cannot be followed by a digit",
1845                    ));
1846                }
1847            }
1848            let decimal = parse_decimal_string(num_text, &num_span, self)?;
1849            return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1850        }
1851
1852        // Check for % (percent)
1853        if peeked.kind == TokenKind::Percent {
1854            let pct_tok = self.next()?;
1855            // Check it's not followed by a digit or another %
1856            if let Ok(next_peek) = self.peek() {
1857                if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1858                    return Err(self.error_at_token(
1859                        &pct_tok,
1860                        "Percent literal cannot be followed by a digit",
1861                    ));
1862                }
1863            }
1864            let decimal = parse_decimal_string(num_text, &num_span, self)?;
1865            return Ok(Value::NumberWithUnit(decimal, "percent".to_string()));
1866        }
1867
1868        // Check for "permille" keyword
1869        if peeked.kind == TokenKind::Permille {
1870            self.next()?; // consume "permille"
1871            let decimal = parse_decimal_string(num_text, &num_span, self)?;
1872            return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1873        }
1874
1875        let starts_unit = can_be_unit_name(&peeked.kind);
1876        if starts_unit && !self.at_path_then_colon()? {
1877            let (unit_path, _) = self.parse_unit_path()?;
1878            let decimal = parse_decimal_string(num_text, &num_span, self)?;
1879            return Ok(Value::NumberWithUnit(decimal, unit_path));
1880        }
1881
1882        // Plain number
1883        let decimal = parse_decimal_string(num_text, &num_span, self)?;
1884        Ok(Value::Number(decimal))
1885    }
1886
1887    /// One or more labels separated by `.` (bare unit or qualified unit path).
1888    fn parse_unit_path(&mut self) -> Result<(String, Span), Error> {
1889        let first = self.next()?;
1890        if !can_be_unit_name(&first.kind) {
1891            return Err(self.error_at_token(
1892                &first,
1893                format!("Expected a unit name, found {}", first.kind),
1894            ));
1895        }
1896        let mut path = first.text.clone();
1897        let mut end_span = first.span.clone();
1898        while self.at(&TokenKind::Dot)? {
1899            self.next()?;
1900            let seg = self.next()?;
1901            if !can_be_unit_name(&seg.kind) {
1902                return Err(self.error_at_token(
1903                    &seg,
1904                    format!("Expected a unit path segment after '.', found {}", seg.kind),
1905                ));
1906            }
1907            path.push('.');
1908            path.push_str(&seg.text);
1909            end_span = seg.span.clone();
1910        }
1911        Ok((path, end_span))
1912    }
1913
1914    fn parse_date_literal(&mut self, year_text: String, start_span: Span) -> Result<Value, Error> {
1915        let mut dt_str = year_text;
1916
1917        // Consume -MM
1918        self.expect(&TokenKind::Minus)?;
1919        dt_str.push('-');
1920        let month_tok = self.expect(&TokenKind::NumberLit)?;
1921        dt_str.push_str(&month_tok.text);
1922
1923        // Consume -DD
1924        self.expect(&TokenKind::Minus)?;
1925        dt_str.push('-');
1926        let day_tok = self.expect(&TokenKind::NumberLit)?;
1927        dt_str.push_str(&day_tok.text);
1928
1929        // Check for T (time component)
1930        if self.at(&TokenKind::Identifier)? {
1931            let peeked = self.peek()?;
1932            if peeked.text.len() >= 2
1933                && (peeked.text.starts_with('T') || peeked.text.starts_with('t'))
1934            {
1935                // The lexer may have tokenized T14 as a single identifier
1936                let t_tok = self.next()?;
1937                dt_str.push_str(&t_tok.text);
1938
1939                // Consume :MM
1940                if self.at(&TokenKind::Colon)? {
1941                    self.next()?;
1942                    dt_str.push(':');
1943                    let min_tok = self.next()?;
1944                    dt_str.push_str(&min_tok.text);
1945
1946                    // Consume :SS and optional fractional second
1947                    if self.at(&TokenKind::Colon)? {
1948                        self.next()?;
1949                        dt_str.push(':');
1950                        let sec_tok = self.next()?;
1951                        dt_str.push_str(&sec_tok.text);
1952
1953                        // Check for fractional second .NNNNNN
1954                        if self.at(&TokenKind::Dot)? {
1955                            self.next()?;
1956                            dt_str.push('.');
1957                            let frac_tok = self.expect(&TokenKind::NumberLit)?;
1958                            dt_str.push_str(&frac_tok.text);
1959                        }
1960                    }
1961                }
1962
1963                // Check for timezone
1964                self.try_consume_timezone(&mut dt_str)?;
1965            }
1966        }
1967
1968        if let Ok(dtv) = dt_str.parse::<crate::literals::DateTimeValue>() {
1969            return Ok(Value::Date(dtv));
1970        }
1971
1972        Err(Error::parsing(
1973            format!("Invalid date/time format: '{}'", dt_str),
1974            self.make_source(start_span),
1975            None::<String>,
1976        ))
1977    }
1978
1979    fn try_consume_timezone(&mut self, dt_str: &mut String) -> Result<(), Error> {
1980        // Z timezone
1981        if self.at(&TokenKind::Identifier)? {
1982            let peeked = self.peek()?;
1983            if (peeked.text == "Z" || peeked.text == "z") && peeked.span.start == self.last_span.end
1984            {
1985                let z_tok = self.next()?;
1986                dt_str.push_str(&z_tok.text);
1987                return Ok(());
1988            }
1989        }
1990
1991        // +HH:MM or -HH:MM, only when attached directly to the preceding token.
1992        if self.at(&TokenKind::Plus)? || self.at(&TokenKind::Minus)? {
1993            let mut lookahead = self.lexer.clone();
1994            let sign_tok = lookahead.next_token()?;
1995            let hour_tok = lookahead.next_token()?;
1996            let colon_tok = lookahead.next_token()?;
1997            let minute_tok = lookahead.next_token()?;
1998
1999            let attached = sign_tok.span.start == self.last_span.end;
2000            let is_timezone_shape = hour_tok.kind == TokenKind::NumberLit
2001                && colon_tok.kind == TokenKind::Colon
2002                && minute_tok.kind == TokenKind::NumberLit;
2003
2004            if attached && is_timezone_shape {
2005                let sign_tok = self.next()?;
2006                dt_str.push_str(&sign_tok.text);
2007                let hour_tok = self.expect(&TokenKind::NumberLit)?;
2008                dt_str.push_str(&hour_tok.text);
2009                self.expect(&TokenKind::Colon)?;
2010                dt_str.push(':');
2011                let min_tok = self.expect(&TokenKind::NumberLit)?;
2012                dt_str.push_str(&min_tok.text);
2013            }
2014        }
2015
2016        Ok(())
2017    }
2018
2019    fn try_parse_time_literal(
2020        &mut self,
2021        hour_text: String,
2022        start_span: Span,
2023    ) -> Result<Value, Error> {
2024        let mut time_str = hour_text;
2025
2026        // Consume :MM
2027        self.expect(&TokenKind::Colon)?;
2028        time_str.push(':');
2029        let min_tok = self.expect(&TokenKind::NumberLit)?;
2030        time_str.push_str(&min_tok.text);
2031
2032        // Optional :SS
2033        if self.at(&TokenKind::Colon)? {
2034            self.next()?;
2035            time_str.push(':');
2036            let sec_tok = self.expect(&TokenKind::NumberLit)?;
2037            time_str.push_str(&sec_tok.text);
2038
2039            // Optional fractional second .NNNNNN
2040            if self.at(&TokenKind::Dot)? {
2041                self.next()?;
2042                time_str.push('.');
2043                let frac_tok = self.expect(&TokenKind::NumberLit)?;
2044                time_str.push_str(&frac_tok.text);
2045            }
2046        }
2047
2048        // Try timezone
2049        self.try_consume_timezone(&mut time_str)?;
2050
2051        if let Ok(t) = time_str.parse::<TimeValue>() {
2052            return Ok(Value::Time(TimeValue {
2053                hour: t.hour,
2054                minute: t.minute,
2055                second: t.second,
2056                microsecond: t.microsecond,
2057                timezone: t.timezone,
2058            }));
2059        }
2060
2061        Err(Error::parsing(
2062            format!("Invalid time format: '{}'", time_str),
2063            self.make_source(start_span),
2064            None::<String>,
2065        ))
2066    }
2067
2068    // ========================================================================
2069    // Expression grammar
2070    // ========================================================================
2071
2072    fn expr_span(&self, id: ExprId) -> Option<Span> {
2073        self.table.source(id).map(|source| source.span.clone())
2074    }
2075
2076    fn expr_source(&self, id: ExprId) -> Option<Source> {
2077        self.table.source(id).cloned()
2078    }
2079
2080    /// Ends one top-level expression: its nodes move into a fresh table owned by the result.
2081    fn finish_expression(&mut self, root: ExprId) -> Expression {
2082        ExprTree::new(self.table.take_compact(), root)
2083    }
2084
2085    fn new_expression(&mut self, kind: ExpressionKind, source: Source) -> Result<ExprId, Error> {
2086        self.expression_count += 1;
2087        if self.expression_count > self.max_expression_count {
2088            return Err(Error::resource_limit_exceeded(
2089                "max_expression_count",
2090                self.max_expression_count.to_string(),
2091                self.expression_count.to_string(),
2092                "Split logic into multiple rules to reduce expression count",
2093                Some(source),
2094                None,
2095                None,
2096            ));
2097        }
2098        Ok(self.table.push(kind, Some(source)))
2099    }
2100
2101    fn check_depth(&mut self) -> Result<(), Error> {
2102        if let Err(actual) = self.depth_tracker.push_depth() {
2103            let span = self.peek()?.span.clone();
2104            self.depth_tracker.pop_depth();
2105            return Err(Error::resource_limit_exceeded(
2106                "max_expression_depth",
2107                self.depth_tracker.max_depth().to_string(),
2108                actual.to_string(),
2109                "Simplify nested expressions or break into separate rules",
2110                Some(self.make_source(span)),
2111                None,
2112                None,
2113            ));
2114        }
2115        Ok(())
2116    }
2117
2118    fn at_bare_veto_token(&mut self) -> Result<bool, Error> {
2119        if !self.at(&TokenKind::Veto)? {
2120            return Ok(false);
2121        }
2122        let checkpoint = self.checkpoint();
2123        self.next()?;
2124        let bare = !self.at(&TokenKind::StringLit)?;
2125        self.restore(checkpoint);
2126        Ok(bare)
2127    }
2128
2129    fn at_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
2130        if !self.at_bare_veto_token()? {
2131            return Ok(false);
2132        }
2133        let checkpoint = self.checkpoint();
2134        self.next()?;
2135        let followed = self.at(&TokenKind::Is)?;
2136        self.restore(checkpoint);
2137        Ok(followed)
2138    }
2139
2140    fn at_not_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
2141        if !self.at(&TokenKind::Not)? {
2142            return Ok(false);
2143        }
2144        let checkpoint = self.checkpoint();
2145        self.next()?;
2146        if !self.at(&TokenKind::Veto)? {
2147            self.restore(checkpoint);
2148            return Ok(false);
2149        }
2150        self.next()?;
2151        if self.at(&TokenKind::StringLit)? {
2152            self.restore(checkpoint);
2153            return Ok(false);
2154        }
2155        let followed = self.at(&TokenKind::Is)?;
2156        self.restore(checkpoint);
2157        Ok(followed)
2158    }
2159
2160    fn wrap_result_is_veto_expression(
2161        &mut self,
2162        operand: ExprId,
2163        operator_is_not: bool,
2164        keyword_was_negated: bool,
2165        start_span: Span,
2166    ) -> Result<ExprId, Error> {
2167        let negate = operator_is_not ^ keyword_was_negated;
2168        let end_span = self
2169            .expr_span(operand)
2170            .unwrap_or_else(|| start_span.clone());
2171        let span = self.span_covering(&start_span, &end_span);
2172        let core = self.new_expression(
2173            ExpressionKind::ResultIsVeto(operand),
2174            self.make_source(span.clone()),
2175        )?;
2176        if negate {
2177            self.new_expression(
2178                ExpressionKind::LogicalNegation(core, NegationType::Not),
2179                self.make_source(span),
2180            )
2181        } else {
2182            Ok(core)
2183        }
2184    }
2185
2186    fn next_is_list_type_suffix(&mut self) -> Result<bool, Error> {
2187        let checkpoint = self.checkpoint();
2188        self.next()?;
2189        let list_after_range = if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
2190            self.next()?;
2191            self.at(&TokenKind::List)?
2192        } else {
2193            self.at(&TokenKind::List)?
2194        };
2195        self.restore(checkpoint);
2196        Ok(list_after_range)
2197    }
2198
2199    fn wrap_list_type_suffix(&mut self, expr: ExprId, start_span: Span) -> Result<ExprId, Error> {
2200        if !self.at_list_type_suffix()? {
2201            return Ok(expr);
2202        }
2203        let ExpressionKind::Reference(reference) = self.table.kind(expr).clone() else {
2204            return Ok(expr);
2205        };
2206        let mut parent = parent_type_from_reference(&reference);
2207        if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
2208            self.next()?;
2209            parent = ParentType::Ranged {
2210                inner: Box::new(parent),
2211            };
2212        }
2213        self.expect(&TokenKind::List)?;
2214        parent = ParentType::List {
2215            inner: Box::new(parent),
2216        };
2217        let span = self.span_covering(&start_span, &self.last_span);
2218        self.new_expression(
2219            ExpressionKind::ListType { element: parent },
2220            self.make_source(span),
2221        )
2222    }
2223
2224    fn at_list_type_suffix(&mut self) -> Result<bool, Error> {
2225        if self.at(&TokenKind::List)? {
2226            return Ok(true);
2227        }
2228        if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
2229            let checkpoint = self.checkpoint();
2230            self.next()?;
2231            let list = self.at(&TokenKind::List)?;
2232            self.restore(checkpoint);
2233            return Ok(list);
2234        }
2235        Ok(false)
2236    }
2237
2238    fn parse_expression_parent_type(&mut self) -> Result<ParentType, Error> {
2239        let peek_kind = self.peek()?.kind.clone();
2240        let base = if let Some(primitive) = token_kind_to_primitive(&peek_kind) {
2241            self.next()?;
2242            ParentType::Primitive { primitive }
2243        } else {
2244            let (name, _) = self.parse_spec_name()?;
2245            if let Some((alias, type_name)) = name.rsplit_once('.') {
2246                ParentType::Qualified {
2247                    spec_alias: alias.to_string(),
2248                    inner: Box::new(ParentType::Custom {
2249                        name: type_name.to_string(),
2250                    }),
2251                }
2252            } else {
2253                ParentType::Custom { name }
2254            }
2255        };
2256        let base = if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
2257            self.next()?;
2258            ParentType::Ranged {
2259                inner: Box::new(base),
2260            }
2261        } else {
2262            base
2263        };
2264        self.expect(&TokenKind::List)?;
2265        Ok(ParentType::List {
2266            inner: Box::new(base),
2267        })
2268    }
2269
2270    /// A token that starts a bare (unparenthesised) command argument. Never a sign:
2271    /// after a command, `+` / `-` is the binary operator. Never `is`: `items -> count is 0`.
2272    fn at_bare_operand_start(&mut self) -> Result<bool, Error> {
2273        let kind = self.peek()?.kind.clone();
2274        if kind == TokenKind::Is {
2275            return Ok(false);
2276        }
2277        Ok(matches!(
2278            kind,
2279            TokenKind::NumberLit
2280                | TokenKind::StringLit
2281                | TokenKind::Now
2282                | TokenKind::Past
2283                | TokenKind::Future
2284                | TokenKind::Lower
2285                | TokenKind::Upper
2286        ) || is_boolean_keyword(&kind)
2287            || is_math_function(&kind)
2288            || can_be_label(&kind)
2289            || token_kind_to_primitive(&kind).is_some())
2290    }
2291
2292    fn at_command_argument_start(&mut self) -> Result<bool, Error> {
2293        Ok(self.at(&TokenKind::LParen)? || self.at_bare_operand_start()?)
2294    }
2295
2296    /// Tokens that may follow a bare argument. Anything else (an operator) is ambiguous.
2297    fn at_bare_argument_end(&mut self) -> Result<bool, Error> {
2298        Ok(self.at_any(&[
2299            TokenKind::Arrow,
2300            TokenKind::Then,
2301            TokenKind::Unless,
2302            TokenKind::RParen,
2303            TokenKind::Comma,
2304            TokenKind::Eof,
2305            TokenKind::Spec,
2306            TokenKind::Repo,
2307            TokenKind::Uses,
2308        ])? || is_spec_body_keyword(&self.peek()?.kind))
2309    }
2310
2311    fn bare_argument_error(&mut self, command: &str) -> Error {
2312        let tok = match self.peek() {
2313            Ok(tok) => tok.clone(),
2314            Err(error) => return error,
2315        };
2316        self.error_at_token(
2317            &tok,
2318            format!(
2319                "`{command}` without parentheses takes one operand, found '{}' after it; \
2320                 write `{command}(...)` around the whole argument, or `(list -> {command} ...)` \
2321                 to use the result",
2322                tok.text
2323            ),
2324        )
2325    }
2326
2327    fn reject_comma_after_call(&mut self, command: &str) -> Result<(), Error> {
2328        if self.call_argument_depth == 0 && self.at(&TokenKind::Comma)? {
2329            let tok = self.peek()?.clone();
2330            return Err(self.error_at_token(
2331                &tok,
2332                format!(
2333                    "`,` after `{command}(...)`: put all arguments inside the parentheses, \
2334                     `{command}(a, b)`"
2335                ),
2336            ));
2337        }
2338        Ok(())
2339    }
2340
2341    fn reject_command_argument(&mut self, command: &str) -> Result<(), Error> {
2342        if self.at_command_argument_start()? {
2343            let tok = self.peek()?.clone();
2344            return Err(self.error_at_token(&tok, format!("`{command}` takes no argument")));
2345        }
2346        Ok(())
2347    }
2348
2349    fn at_row_condition_operator(&mut self) -> Result<bool, Error> {
2350        Ok(is_comparison_operator(&self.peek()?.kind) || self.at(&TokenKind::In)?)
2351    }
2352
2353    fn parse_row_condition_operator(&mut self) -> Result<RowCondition, Error> {
2354        if self.at(&TokenKind::In)? {
2355            self.next()?;
2356            return Ok(RowCondition::In);
2357        }
2358        Ok(RowCondition::Compare(self.parse_comparison_operator()?))
2359    }
2360
2361    fn condition_error(&mut self, command: &str) -> Error {
2362        let tok = match self.peek() {
2363            Ok(tok) => tok.clone(),
2364            Err(error) => return error,
2365        };
2366        self.error_at_token(
2367            &tok,
2368            format!(
2369                "`{command}` condition without parentheses is one comparison between single \
2370                 operands, found '{}'; write `{command}(...)` around the whole condition",
2371                tok.text
2372            ),
2373        )
2374    }
2375
2376    /// `asc` when omitted.
2377    fn parse_sort_direction(&mut self) -> Result<bool, Error> {
2378        if self.at(&TokenKind::Desc)? {
2379            self.next()?;
2380            return Ok(true);
2381        }
2382        if self.at(&TokenKind::Asc)? {
2383            self.next()?;
2384        }
2385        Ok(false)
2386    }
2387
2388    fn parse_comparison_operator(&mut self) -> Result<ComparisonComputation, Error> {
2389        let tok = self.next()?;
2390        match tok.kind {
2391            TokenKind::Gt => Ok(ComparisonComputation::GreaterThan),
2392            TokenKind::Lt => Ok(ComparisonComputation::LessThan),
2393            TokenKind::Gte => Ok(ComparisonComputation::GreaterThanOrEqual),
2394            TokenKind::Lte => Ok(ComparisonComputation::LessThanOrEqual),
2395            TokenKind::Is => {
2396                // Check for "is not"
2397                if self.at(&TokenKind::Not)? {
2398                    self.next()?; // consume 'not'
2399                    Ok(ComparisonComputation::IsNot)
2400                } else {
2401                    Ok(ComparisonComputation::Is)
2402                }
2403            }
2404            _ => Err(self.error_at_token(
2405                &tok,
2406                format!("Expected a comparison operator, found {}", tok.kind),
2407            )),
2408        }
2409    }
2410
2411    fn parse_not_in_calendar_suffix(
2412        &mut self,
2413        repository: ExprId,
2414        start_span: Span,
2415    ) -> Result<ExprId, Error> {
2416        self.expect(&TokenKind::Not)?;
2417        self.expect(&TokenKind::In)?;
2418        self.expect_calendar_period_marker()?;
2419        let unit = self.parse_calendar_unit()?;
2420        let end = self.peek()?.span.clone();
2421        let span = self.span_covering(&start_span, &end);
2422        self.new_expression(
2423            ExpressionKind::DateCalendar(DateCalendarKind::NotIn, unit, repository),
2424            self.make_source(span),
2425        )
2426    }
2427
2428    fn parse_as_chain(&mut self, mut expr: ExprId, start_span: Span) -> Result<ExprId, Error> {
2429        while self.at(&TokenKind::As)? {
2430            self.expect(&TokenKind::As)?;
2431            let target_tok = self.next()?;
2432            let target = if matches!(target_tok.kind, TokenKind::Permille) {
2433                ConversionTarget::Unit {
2434                    unit_name: "permille".to_string(),
2435                }
2436            } else if let Some(primitive) = token_kind_to_primitive(&target_tok.kind) {
2437                ConversionTarget::Type(primitive)
2438            } else if can_be_label(&target_tok.kind) {
2439                let mut unit_path = target_tok.text.clone();
2440                let mut end_span = target_tok.span.clone();
2441                while self.at(&TokenKind::Dot)? {
2442                    self.next()?;
2443                    let seg = self.next()?;
2444                    if !can_be_label(&seg.kind) {
2445                        return Err(self.error_at_token(
2446                            &seg,
2447                            format!("Expected a unit path segment after '.', found {}", seg.kind),
2448                        ));
2449                    }
2450                    unit_path.push('.');
2451                    unit_path.push_str(&seg.text);
2452                    end_span = seg.span.clone();
2453                }
2454                let target = ConversionTarget::Unit {
2455                    unit_name: unit_path,
2456                };
2457                expr = self.new_expression(
2458                    ExpressionKind::UnitConversion(expr, target),
2459                    self.make_source(self.span_covering(&start_span, &end_span)),
2460                )?;
2461                continue;
2462            } else {
2463                return Err(self.error_at_token(
2464                    &target_tok,
2465                    format!(
2466                        "Expected a type keyword or unit name after 'as', found {}",
2467                        target_tok.kind
2468                    ),
2469                ));
2470            };
2471            expr = self.new_expression(
2472                ExpressionKind::UnitConversion(expr, target),
2473                self.make_source(self.span_covering(&start_span, &target_tok.span)),
2474            )?;
2475        }
2476        Ok(expr)
2477    }
2478
2479    fn is_plain_number_literal(&self, id: ExprId) -> bool {
2480        matches!(
2481            self.table.kind(id),
2482            ExpressionKind::Literal(Value::Number(_))
2483        )
2484    }
2485
2486    fn is_unit_conversion(&self, id: ExprId) -> bool {
2487        matches!(self.table.kind(id), ExpressionKind::UnitConversion(..))
2488    }
2489
2490    /// True when the next token can follow a completed suffix expression (no further operands).
2491    ///
2492    /// Must include every token that can start the next spec-body item, a new `spec`/`repo`,
2493    /// or end the file. See `parse_unit_conversion_before_expression_boundaries` in `parsing/mod.rs`.
2494    fn at_expression_suffix_end(&mut self) -> Result<bool, Error> {
2495        Ok(self.at(&TokenKind::And)?
2496            || self.at(&TokenKind::Unless)?
2497            || self.at(&TokenKind::Then)?
2498            || self.at(&TokenKind::RParen)?
2499            || self.at(&TokenKind::Eof)?
2500            || self.at(&TokenKind::Arrow)?
2501            || self.at(&TokenKind::Comma)?
2502            || self.at(&TokenKind::Spec)?
2503            || self.at(&TokenKind::Repo)?
2504            || self.at(&TokenKind::Uses)?
2505            || is_spec_body_keyword(&self.peek()?.kind))
2506    }
2507
2508    fn parse_calendar_unit(&mut self) -> Result<CalendarPeriodUnit, Error> {
2509        let tok = self.next()?;
2510        if let Some(unit) = CalendarPeriodUnit::from_keyword(&tok.text) {
2511            return Ok(unit);
2512        }
2513        Err(self.error_at_token(
2514            &tok,
2515            format!("Expected 'year', 'month', or 'week', found '{}'", tok.text),
2516        ))
2517    }
2518
2519    // ========================================================================
2520    // Arithmetic and range operands
2521    // ========================================================================
2522
2523    /// One left fold over `steps`. Without steps the operand itself is the result.
2524    fn arithmetic_chain(
2525        &mut self,
2526        first: ExprId,
2527        steps: Vec<ChainStep<ArithmeticComputation>>,
2528    ) -> Result<ExprId, Error> {
2529        let source = match steps.last() {
2530            None => return Ok(first),
2531            Some(last) => last.source_location.clone(),
2532        };
2533        self.new_expression(ExpressionKind::Arithmetic { first, steps }, source)
2534    }
2535
2536    fn parse_number_expression(&mut self) -> Result<ExprId, Error> {
2537        let num_tok = self.next()?;
2538        let num_text = num_tok.text.clone();
2539        let start_span = num_tok.span.clone();
2540
2541        // Check if this is a date literal (YYYY-MM-DD)
2542        if num_text.len() == 4
2543            && num_text.chars().all(|c| c.is_ascii_digit())
2544            && self.at(&TokenKind::Minus)?
2545        {
2546            // Peek further: if next-next is a number, this is likely a date
2547            // We need to be careful: "2024 - 5" is arithmetic, "2024-01-15" is a date
2548            // Date format requires: YYYY-MM-DD where MM and DD are 2 digits
2549            // This is ambiguous at the token level. Let's check if the pattern matches.
2550            // Since dates use -NN- pattern and arithmetic uses - N pattern (with spaces),
2551            // we can use the span positions to disambiguate.
2552            let minus_span = self.peek()?.span.clone();
2553            // If minus is immediately adjacent to the number (no space), it's a date
2554            if minus_span.start == start_span.end {
2555                let value = self.parse_date_literal(num_text, start_span.clone())?;
2556                return self
2557                    .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
2558            }
2559        }
2560
2561        // Check for time literal (HH:MM:SS)
2562        if num_text.len() == 2
2563            && num_text.chars().all(|c| c.is_ascii_digit())
2564            && self.at(&TokenKind::Colon)?
2565        {
2566            let colon_span = self.peek()?.span.clone();
2567            if colon_span.start == start_span.end {
2568                let value = self.try_parse_time_literal(num_text, start_span.clone())?;
2569                return self
2570                    .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
2571            }
2572        }
2573
2574        // Check for %% (permille)
2575        if self.at(&TokenKind::PercentPercent)? {
2576            let pp_tok = self.next()?;
2577            if let Ok(next_peek) = self.peek() {
2578                if next_peek.kind == TokenKind::NumberLit {
2579                    return Err(self.error_at_token(
2580                        &pp_tok,
2581                        "Permille literal cannot be followed by a digit",
2582                    ));
2583                }
2584            }
2585            let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2586            return self.new_expression(
2587                ExpressionKind::Literal(Value::NumberWithUnit(decimal, "permille".to_string())),
2588                self.make_source(start_span),
2589            );
2590        }
2591
2592        // Check for % (percent)
2593        if self.at(&TokenKind::Percent)? {
2594            let pct_span = self.peek()?.span.clone();
2595            // Only consume % if it's directly adjacent (no space) for the shorthand syntax
2596            // Or if it's "50 %" (space separated is also valid per the grammar)
2597            let pct_tok = self.next()?;
2598            if let Ok(next_peek) = self.peek() {
2599                if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
2600                    return Err(self.error_at_token(
2601                        &pct_tok,
2602                        "Percent literal cannot be followed by a digit (use 'mod' for remainder)",
2603                    ));
2604                }
2605            }
2606            let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2607            return self.new_expression(
2608                ExpressionKind::Literal(Value::NumberWithUnit(decimal, "percent".to_string())),
2609                self.make_source(self.span_covering(&start_span, &pct_span)),
2610            );
2611        }
2612
2613        // Check for "permille" keyword
2614        if self.at(&TokenKind::Permille)? {
2615            self.next()?;
2616            let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2617            return self.new_expression(
2618                ExpressionKind::Literal(Value::NumberWithUnit(decimal, "permille".to_string())),
2619                self.make_source(start_span),
2620            );
2621        }
2622
2623        if can_be_unit_name(&self.peek()?.kind) {
2624            let (unit_path, end_span) = self.parse_unit_path()?;
2625            let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2626            return self.new_expression(
2627                ExpressionKind::Literal(Value::NumberWithUnit(decimal, unit_path)),
2628                self.make_source(self.span_covering(&start_span, &end_span)),
2629            );
2630        }
2631
2632        // Plain number
2633        let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2634        self.new_expression(
2635            ExpressionKind::Literal(Value::Number(decimal)),
2636            self.make_source(start_span),
2637        )
2638    }
2639
2640    fn parse_expression_reference(&mut self) -> Result<Reference, Error> {
2641        let mut segments = Vec::new();
2642
2643        let first = self.next()?;
2644        segments.push(first.text.clone());
2645
2646        while self.at(&TokenKind::Dot)? {
2647            self.next()?; // consume .
2648            let seg = self.next()?;
2649            if !can_be_label(&seg.kind) {
2650                return Err(self.error_at_token(
2651                    &seg,
2652                    format!("Expected an identifier after '.', found {}", seg.kind),
2653                ));
2654            }
2655            segments.push(seg.text.clone());
2656        }
2657
2658        Ok(Reference::from_path(segments))
2659    }
2660    fn parse_expression(&mut self) -> Result<ExprId, Error> {
2661        Ok(expr_of(self.run_expression_machine(ExprTask::Expression)?))
2662    }
2663
2664    fn parse_list_command_body(&mut self, name_tok: Token) -> Result<ListCommand, Error> {
2665        Ok(command_of(self.run_expression_machine(
2666            ExprTask::ListCommandBody { name: name_tok },
2667        )?))
2668    }
2669
2670    fn save_flags(&self) -> ExprFlags {
2671        ExprFlags {
2672            bare_operand: self.bare_operand,
2673            call_argument_depth: self.call_argument_depth,
2674        }
2675    }
2676
2677    fn run_expression_machine(&mut self, root: ExprTask) -> Result<ExprVal, Error> {
2678        let mut frames = std::mem::take(&mut self.expr_frames);
2679        let result = self.drive_expression_machine(root, &mut frames);
2680        self.expr_frames = frames;
2681        result
2682    }
2683
2684    fn drive_expression_machine(
2685        &mut self,
2686        root: ExprTask,
2687        frames: &mut Vec<ExprCont>,
2688    ) -> Result<ExprVal, Error> {
2689        let mut step = ExprStep::Enter(root);
2690        loop {
2691            let next = match step {
2692                ExprStep::Enter(task) => self.enter_expression(task, frames),
2693                ExprStep::Yield(value) => match frames.pop() {
2694                    None => return Ok(value),
2695                    Some(cont) => self.resume_expression(cont, value, frames),
2696                },
2697            };
2698            match next {
2699                Ok(next_step) => step = next_step,
2700                Err(error) => {
2701                    self.unwind_expression(frames);
2702                    return Err(error);
2703                }
2704            }
2705        }
2706    }
2707
2708    fn unwind_expression(&mut self, frames: &mut Vec<ExprCont>) {
2709        while let Some(cont) = frames.pop() {
2710            match cont {
2711                ExprCont::Restore(flags) => {
2712                    self.bare_operand = flags.bare_operand;
2713                    self.call_argument_depth = flags.call_argument_depth;
2714                }
2715                ExprCont::PopDepth => self.depth_tracker.pop_depth(),
2716                _ => {}
2717            }
2718        }
2719    }
2720
2721    fn enter_expression(
2722        &mut self,
2723        task: ExprTask,
2724        frames: &mut Vec<ExprCont>,
2725    ) -> Result<ExprStep, Error> {
2726        match task {
2727            ExprTask::Expression => {
2728                self.check_depth()?;
2729                frames.push(ExprCont::PopDepth);
2730                Ok(ExprStep::Enter(ExprTask::AndExpression))
2731            }
2732            ExprTask::AndExpression => {
2733                let start_span = self.peek()?.span.clone();
2734                frames.push(ExprCont::AndFirst { start_span });
2735                Ok(ExprStep::Enter(ExprTask::AndOperand))
2736            }
2737            ExprTask::AndOperand => {
2738                if self.at_not_bare_veto_followed_by_is()? || self.at_bare_veto_followed_by_is()? {
2739                    Ok(ExprStep::Enter(ExprTask::VetoStatus))
2740                } else if self.at(&TokenKind::Not)? {
2741                    Ok(ExprStep::Enter(ExprTask::NotExpression))
2742                } else {
2743                    Ok(ExprStep::Enter(ExprTask::RepositoryWithSuffix))
2744                }
2745            }
2746            ExprTask::VetoStatus => {
2747                let start_span = self.peek()?.span.clone();
2748                let keyword_was_negated = if self.at(&TokenKind::Not)? {
2749                    self.next()?;
2750                    true
2751                } else {
2752                    false
2753                };
2754                self.expect(&TokenKind::Veto)?;
2755                if self.at(&TokenKind::StringLit)? {
2756                    let tok = self.peek()?.clone();
2757                    return Err(self.error_at_token(
2758                        &tok,
2759                        "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
2760                    ));
2761                }
2762                let operator = self.parse_comparison_operator()?;
2763                let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
2764                if !matches!(
2765                    operator,
2766                    ComparisonComputation::Is | ComparisonComputation::IsNot
2767                ) {
2768                    let tok = self.peek()?.clone();
2769                    return Err(self.error_at_token(
2770                        &tok,
2771                        "Expected `is` or `is not` after `veto` in a veto-status comparison",
2772                    ));
2773                }
2774                frames.push(ExprCont::VetoStatus {
2775                    operator_is_not,
2776                    keyword_was_negated,
2777                    start_span,
2778                });
2779                Ok(ExprStep::Enter(ExprTask::RepositoryExpression))
2780            }
2781            ExprTask::NotExpression => {
2782                let not_tok = self.expect(&TokenKind::Not)?;
2783                frames.push(ExprCont::Negation {
2784                    start_span: not_tok.span,
2785                });
2786                Ok(ExprStep::Enter(ExprTask::PrefixArgument {
2787                    keyword: "not".to_string(),
2788                }))
2789            }
2790            ExprTask::RepositoryWithSuffix => {
2791                let start_span = self.peek()?.span.clone();
2792                frames.push(ExprCont::RepositorySuffix { start_span });
2793                Ok(ExprStep::Enter(ExprTask::RepositoryExpression))
2794            }
2795            ExprTask::ComparisonSuffix { left, start_span } => {
2796                self.enter_comparison_suffix(left, start_span, frames)
2797            }
2798            ExprTask::InSuffix {
2799                repository,
2800                start_span,
2801            } => self.enter_in_suffix(repository, start_span, frames),
2802            ExprTask::RepositoryExpression => {
2803                let start_span = self.peek()?.span.clone();
2804                frames.push(ExprCont::RepositoryFirst { start_span });
2805                Ok(ExprStep::Enter(ExprTask::Term { allow_as: true }))
2806            }
2807            ExprTask::Term { allow_as } => {
2808                let start_span = self.peek()?.span.clone();
2809                frames.push(ExprCont::TermFirst {
2810                    allow_as,
2811                    start_span,
2812                });
2813                Ok(ExprStep::Enter(ExprTask::Power))
2814            }
2815            ExprTask::Power => {
2816                let start_span = self.peek()?.span.clone();
2817                frames.push(ExprCont::PowerLeft {
2818                    start_span,
2819                    range_bound: false,
2820                });
2821                Ok(ExprStep::Enter(ExprTask::RangeOperand))
2822            }
2823            ExprTask::RangeOperand => {
2824                let start_span = self.peek()?.span.clone();
2825                let flags = self.save_flags();
2826                if self.no_range_at.get(&start_span.start) == Some(&flags) {
2827                    return Ok(ExprStep::Enter(ExprTask::Factor));
2828                }
2829                frames.push(ExprCont::RangeFactor { start_span, flags });
2830                Ok(ExprStep::Enter(ExprTask::Factor))
2831            }
2832            ExprTask::RangeEllipsisBound => {
2833                let start_span = self.peek()?.span.clone();
2834                frames.push(ExprCont::EllipsisFirst { start_span });
2835                Ok(ExprStep::Enter(ExprTask::PowerForRangeBound))
2836            }
2837            ExprTask::PowerForRangeBound => {
2838                let start_span = self.peek()?.span.clone();
2839                frames.push(ExprCont::PowerLeft {
2840                    start_span,
2841                    range_bound: true,
2842                });
2843                Ok(ExprStep::Enter(ExprTask::Factor))
2844            }
2845            ExprTask::Factor => {
2846                let peeked = self.peek()?;
2847                let start_span = peeked.span.clone();
2848                let kind = peeked.kind.clone();
2849                if kind == TokenKind::Minus {
2850                    self.next()?;
2851                    frames.push(ExprCont::FactorNegate { start_span });
2852                    return Ok(ExprStep::Enter(ExprTask::PrimaryOrMath));
2853                }
2854                if kind == TokenKind::Plus {
2855                    self.next()?;
2856                }
2857                Ok(ExprStep::Enter(ExprTask::PrimaryOrMath))
2858            }
2859            ExprTask::PrimaryOrMath => {
2860                let peeked = self.peek()?;
2861                let start_span = peeked.span.clone();
2862                let kind = peeked.kind.clone();
2863                frames.push(ExprCont::PrimaryOrMathSuffix { start_span });
2864                if is_math_function(&kind) {
2865                    return Ok(ExprStep::Enter(ExprTask::MathFunction));
2866                }
2867                if matches!(kind, TokenKind::Lower | TokenKind::Upper) {
2868                    return Ok(ExprStep::Enter(ExprTask::LowerUpper));
2869                }
2870                Ok(ExprStep::Enter(ExprTask::Primary))
2871            }
2872            ExprTask::MathFunction => {
2873                let func_tok = self.next()?;
2874                let start_span = func_tok.span.clone();
2875                let operator = match func_tok.kind {
2876                    TokenKind::Sqrt => MathematicalComputation::Sqrt,
2877                    TokenKind::Sin => MathematicalComputation::Sin,
2878                    TokenKind::Cos => MathematicalComputation::Cos,
2879                    TokenKind::Tan => MathematicalComputation::Tan,
2880                    TokenKind::Asin => MathematicalComputation::Asin,
2881                    TokenKind::Acos => MathematicalComputation::Acos,
2882                    TokenKind::Atan => MathematicalComputation::Atan,
2883                    TokenKind::Log => MathematicalComputation::Log,
2884                    TokenKind::Exp => MathematicalComputation::Exp,
2885                    TokenKind::Abs => MathematicalComputation::Abs,
2886                    TokenKind::Floor => MathematicalComputation::Floor,
2887                    TokenKind::Ceil => MathematicalComputation::Ceil,
2888                    TokenKind::Round => MathematicalComputation::Round,
2889                    _ => unreachable!("BUG: only math functions should reach here"),
2890                };
2891                frames.push(ExprCont::Math {
2892                    operator,
2893                    start_span,
2894                });
2895                Ok(ExprStep::Enter(ExprTask::PrefixArgument {
2896                    keyword: func_tok.text,
2897                }))
2898            }
2899            ExprTask::LowerUpper => {
2900                let bound_tok = self.next()?;
2901                let start_span = bound_tok.span.clone();
2902                let bound = match bound_tok.kind {
2903                    TokenKind::Lower => RangeBound::Lower,
2904                    TokenKind::Upper => RangeBound::Upper,
2905                    _ => unreachable!("BUG: only lower and upper should reach parse_range_bound"),
2906                };
2907                frames.push(ExprCont::LowerUpperEnd { bound, start_span });
2908                Ok(ExprStep::Enter(ExprTask::PrefixArgument {
2909                    keyword: bound_tok.text,
2910                }))
2911            }
2912            ExprTask::PrefixArgument { keyword } => self.enter_prefix_argument(keyword, frames),
2913            ExprTask::Primary => self.enter_primary(frames),
2914            ExprTask::ListCommandSuffix { list, start_span } => {
2915                self.expect(&TokenKind::Arrow)?;
2916                let name = self.next()?;
2917                frames.push(ExprCont::ListCommandDone { list, start_span });
2918                Ok(ExprStep::Enter(ExprTask::ListCommandBody { name }))
2919            }
2920            ExprTask::ListCommandBody { name } => self.enter_list_command_body(name, frames),
2921            ExprTask::FindKeys => {
2922                if self.at(&TokenKind::LParen)? {
2923                    return Ok(ExprStep::Enter(ExprTask::CallArguments { command: "find" }));
2924                }
2925                if !self.at_bare_operand_start()? {
2926                    let tok = self.peek()?.clone();
2927                    return Err(self.error_at_token(&tok, "`find` needs a key"));
2928                }
2929                frames.push(ExprCont::FindKey { keys: Vec::new() });
2930                Ok(ExprStep::Enter(ExprTask::BareArgument { command: "find" }))
2931            }
2932            ExprTask::CallArguments { command } => {
2933                self.expect(&TokenKind::LParen)?;
2934                let saved = self.save_flags();
2935                self.bare_operand = false;
2936                self.call_argument_depth += 1;
2937                frames.push(ExprCont::CallArgumentsEnd { command });
2938                frames.push(ExprCont::Restore(saved));
2939                frames.push(ExprCont::CallArgumentItems {
2940                    arguments: Vec::new(),
2941                });
2942                Ok(ExprStep::Enter(ExprTask::Expression))
2943            }
2944            ExprTask::SingleCallArgument { command } => {
2945                frames.push(ExprCont::SingleCallArgumentCheck { command });
2946                Ok(ExprStep::Enter(ExprTask::CallArguments { command }))
2947            }
2948            ExprTask::OptionalArgument { command } => {
2949                if self.at(&TokenKind::LParen)? {
2950                    frames.push(ExprCont::WrapSome);
2951                    return Ok(ExprStep::Enter(ExprTask::SingleCallArgument { command }));
2952                }
2953                if self.at_bare_operand_start()? {
2954                    frames.push(ExprCont::WrapSome);
2955                    return Ok(ExprStep::Enter(ExprTask::BareArgument { command }));
2956                }
2957                Ok(ExprStep::Yield(ExprVal::Optional(None)))
2958            }
2959            ExprTask::RequiredArgument { command } => {
2960                if self.at(&TokenKind::LParen)? {
2961                    return Ok(ExprStep::Enter(ExprTask::SingleCallArgument { command }));
2962                }
2963                if self.at_bare_operand_start()? {
2964                    return Ok(ExprStep::Enter(ExprTask::BareArgument { command }));
2965                }
2966                let tok = self.peek()?.clone();
2967                Err(self.error_at_token(&tok, format!("`{command}` needs an argument")))
2968            }
2969            ExprTask::BareArgument { command } => {
2970                frames.push(ExprCont::BareArgumentEnd { command });
2971                Ok(ExprStep::Enter(ExprTask::BareOperand))
2972            }
2973            ExprTask::BareOperand => {
2974                let saved = self.save_flags();
2975                self.bare_operand = true;
2976                frames.push(ExprCont::Restore(saved));
2977                Ok(ExprStep::Enter(ExprTask::RangeOperand))
2978            }
2979            ExprTask::ListPredicate { command } => self.enter_list_predicate(command, frames),
2980            ExprTask::CallListPredicate { command } => {
2981                let checkpoint = self.checkpoint();
2982                self.expect(&TokenKind::LParen)?;
2983                if !self.at_row_condition_operator()? {
2984                    self.restore(checkpoint);
2985                    frames.push(ExprCont::WrapInstance);
2986                    return Ok(ExprStep::Enter(ExprTask::SingleCallArgument { command }));
2987                }
2988                let saved = self.save_flags();
2989                self.bare_operand = false;
2990                frames.push(ExprCont::CallPredicateEnd { command });
2991                frames.push(ExprCont::Restore(saved));
2992                Ok(ExprStep::Enter(ExprTask::ValuePredicateInParentheses {
2993                    command,
2994                }))
2995            }
2996            ExprTask::ValuePredicateInParentheses { command } => {
2997                let op = self.parse_row_condition_operator()?;
2998                frames.push(ExprCont::ValuePredicateEnd { op, command });
2999                Ok(ExprStep::Enter(ExprTask::RepositoryExpression))
3000            }
3001            ExprTask::SortCommand => {
3002                if self.at(&TokenKind::LParen)? {
3003                    frames.push(ExprCont::SortArgument { check_bare: false });
3004                    return Ok(ExprStep::Enter(ExprTask::SingleCallArgument {
3005                        command: "sort",
3006                    }));
3007                }
3008                if self.at_bare_operand_start()? {
3009                    frames.push(ExprCont::SortArgument { check_bare: true });
3010                    return Ok(ExprStep::Enter(ExprTask::BareOperand));
3011                }
3012                let descending = self.parse_sort_direction()?;
3013                Ok(ExprStep::Yield(ExprVal::Command(ListCommand::Sort {
3014                    descending,
3015                    argument: None,
3016                })))
3017            }
3018            ExprTask::ListWithCommand => {
3019                if !self.at(&TokenKind::LParen)? {
3020                    return Ok(ExprStep::Enter(ExprTask::ListWithRows));
3021                }
3022                self.next()?;
3023                let saved = self.save_flags();
3024                self.call_argument_depth += 1;
3025                frames.push(ExprCont::ListWithEnd);
3026                frames.push(ExprCont::Restore(saved));
3027                Ok(ExprStep::Enter(ExprTask::ListWithRows))
3028            }
3029            ExprTask::ListWithRows => {
3030                if self.at_path_then_colon()? {
3031                    return self.enter_pair_field(Vec::new(), frames);
3032                }
3033                frames.push(ExprCont::ValueItem { values: Vec::new() });
3034                Ok(ExprStep::Enter(ExprTask::BareArgument { command: "with" }))
3035            }
3036        }
3037    }
3038
3039    fn resume_expression(
3040        &mut self,
3041        cont: ExprCont,
3042        value: ExprVal,
3043        frames: &mut Vec<ExprCont>,
3044    ) -> Result<ExprStep, Error> {
3045        match cont {
3046            ExprCont::Restore(flags) => {
3047                self.bare_operand = flags.bare_operand;
3048                self.call_argument_depth = flags.call_argument_depth;
3049                Ok(ExprStep::Yield(value))
3050            }
3051            ExprCont::PopDepth => {
3052                self.depth_tracker.pop_depth();
3053                Ok(ExprStep::Yield(value))
3054            }
3055            ExprCont::AndFirst { start_span } => {
3056                self.and_chain(expr_of(value), Vec::new(), start_span, frames)
3057            }
3058            ExprCont::AndOperand {
3059                first,
3060                mut steps,
3061                start_span,
3062            } => {
3063                let operand = expr_of(value);
3064                let end_span = self
3065                    .expr_span(operand)
3066                    .unwrap_or_else(|| start_span.clone());
3067                let span = self.span_covering(&start_span, &end_span);
3068                steps.push(ChainStep {
3069                    op: (),
3070                    operand,
3071                    source_location: self.make_source(span),
3072                });
3073                self.and_chain(first, steps, start_span, frames)
3074            }
3075            ExprCont::VetoStatus {
3076                operator_is_not,
3077                keyword_was_negated,
3078                start_span,
3079            } => expr_step(self.wrap_result_is_veto_expression(
3080                expr_of(value),
3081                operator_is_not,
3082                keyword_was_negated,
3083                start_span,
3084            )),
3085            ExprCont::Negation { start_span } => {
3086                let operand = expr_of(value);
3087                let end_span = self
3088                    .expr_span(operand)
3089                    .unwrap_or_else(|| start_span.clone());
3090                let span = self.span_covering(&start_span, &end_span);
3091                expr_step(self.new_expression(
3092                    ExpressionKind::LogicalNegation(operand, NegationType::Not),
3093                    self.make_source(span),
3094                ))
3095            }
3096            ExprCont::RepositorySuffix { start_span } => {
3097                self.continue_repository(expr_of(value), start_span, frames)
3098            }
3099            ExprCont::ContinueRepository { start_span } => {
3100                self.continue_repository(expr_of(value), start_span, frames)
3101            }
3102            ExprCont::ComparisonRight {
3103                left,
3104                operator,
3105                start_span,
3106            } => {
3107                let right = expr_of(value);
3108                let end_span = self.expr_span(right).unwrap_or_else(|| start_span.clone());
3109                let span = self.span_covering(&start_span, &end_span);
3110                expr_step(self.new_expression(
3111                    ExpressionKind::Comparison(left, operator, right),
3112                    self.make_source(span),
3113                ))
3114            }
3115            ExprCont::InOffset {
3116                direction,
3117                rel_kind,
3118                repository,
3119                start_span,
3120            } => {
3121                let offset = expr_of(value);
3122                let offset_end_span = self.expr_span(offset).unwrap_or_else(|| start_span.clone());
3123                let range = self.new_expression(
3124                    ExpressionKind::PastFutureRange(rel_kind, offset),
3125                    self.make_source(self.span_covering(&direction, &offset_end_span)),
3126                )?;
3127                let span = self.span_covering(&start_span, &offset_end_span);
3128                expr_step(self.new_expression(
3129                    ExpressionKind::RangeContainment(repository, range),
3130                    self.make_source(span),
3131                ))
3132            }
3133            ExprCont::InRange {
3134                repository,
3135                start_span,
3136            } => {
3137                let range = expr_of(value);
3138                let end_span = self.expr_span(range).unwrap_or_else(|| start_span.clone());
3139                let span = self.span_covering(&start_span, &end_span);
3140                expr_step(self.new_expression(
3141                    ExpressionKind::RangeContainment(repository, range),
3142                    self.make_source(span),
3143                ))
3144            }
3145            ExprCont::RepositoryFirst { start_span } => {
3146                self.repository_chain(expr_of(value), Vec::new(), start_span, frames)
3147            }
3148            ExprCont::RepositoryOperand {
3149                first,
3150                mut steps,
3151                start_span,
3152                operation,
3153            } => {
3154                let operand = expr_of(value);
3155                if steps.is_empty()
3156                    && self.is_plain_number_literal(first)
3157                    && self.is_unit_conversion(operand)
3158                {
3159                    let source = self
3160                        .expr_source(operand)
3161                        .unwrap_or_else(|| self.make_source(start_span.clone()));
3162                    return Err(Error::parsing(
3163                        "Cannot add a plain number to a converted value; convert each operand before \
3164                         '+' (e.g. '5 as usd + c as usd')",
3165                        source,
3166                        None::<String>,
3167                    ));
3168                }
3169                let end_span = self
3170                    .expr_span(operand)
3171                    .unwrap_or_else(|| start_span.clone());
3172                let span = self.span_covering(&start_span, &end_span);
3173                steps.push(ChainStep {
3174                    op: operation,
3175                    operand,
3176                    source_location: self.make_source(span),
3177                });
3178                self.repository_chain(first, steps, start_span, frames)
3179            }
3180            ExprCont::TermFirst {
3181                allow_as,
3182                start_span,
3183            } => {
3184                let mut first = expr_of(value);
3185                if allow_as {
3186                    first = self.parse_as_chain(first, start_span.clone())?;
3187                }
3188                self.term_chain(first, Vec::new(), allow_as, start_span, frames)
3189            }
3190            ExprCont::TermOperand {
3191                first,
3192                mut steps,
3193                allow_as,
3194                start_span,
3195                operation,
3196                right_start_span,
3197            } => {
3198                let mut operand = expr_of(value);
3199                if allow_as {
3200                    operand = self.parse_as_chain(operand, right_start_span)?;
3201                }
3202                let end_span = self
3203                    .expr_span(operand)
3204                    .unwrap_or_else(|| start_span.clone());
3205                let span = self.span_covering(&start_span, &end_span);
3206                steps.push(ChainStep {
3207                    op: operation,
3208                    operand,
3209                    source_location: self.make_source(span),
3210                });
3211                self.term_chain(first, steps, allow_as, start_span, frames)
3212            }
3213            ExprCont::PowerLeft {
3214                start_span,
3215                range_bound,
3216            } => self.power_tail(expr_of(value), start_span, range_bound, frames),
3217            ExprCont::PowerRight { left, start_span } => {
3218                let right = expr_of(value);
3219                let end_span = self.expr_span(right).unwrap_or_else(|| start_span.clone());
3220                let span = self.span_covering(&start_span, &end_span);
3221                let step = ChainStep {
3222                    op: ArithmeticComputation::Power,
3223                    operand: right,
3224                    source_location: self.make_source(span),
3225                };
3226                expr_step(self.arithmetic_chain(left, vec![step]))
3227            }
3228            ExprCont::RangeFactor { start_span, flags } => {
3229                let factor = expr_of(value);
3230                if self.at(&TokenKind::Ellipsis)? {
3231                    self.next()?;
3232                    frames.push(ExprCont::RangeRight {
3233                        left: factor,
3234                        start_span,
3235                    });
3236                    return Ok(ExprStep::Enter(ExprTask::RangeEllipsisBound));
3237                }
3238                if !self.at_any(&[TokenKind::Caret, TokenKind::Plus, TokenKind::Minus])? {
3239                    return Ok(ExprStep::Yield(ExprVal::Expr(factor)));
3240                }
3241                frames.push(ExprCont::RangeLeft {
3242                    factor,
3243                    start_span: start_span.clone(),
3244                    checkpoint: Box::new(self.checkpoint()),
3245                    flags,
3246                });
3247                frames.push(ExprCont::EllipsisFirst {
3248                    start_span: start_span.clone(),
3249                });
3250                self.power_tail(factor, start_span, true, frames)
3251            }
3252            ExprCont::RangeLeft {
3253                factor,
3254                start_span,
3255                checkpoint,
3256                flags,
3257            } => {
3258                let left = expr_of(value);
3259                let starts = std::mem::take(&mut self.bound_chain_starts);
3260                if !self.at(&TokenKind::Ellipsis)? {
3261                    for start in starts {
3262                        self.no_range_at.insert(start, flags);
3263                    }
3264                    self.restore(*checkpoint);
3265                    return Ok(ExprStep::Yield(ExprVal::Expr(factor)));
3266                }
3267                self.next()?;
3268                frames.push(ExprCont::RangeRight { left, start_span });
3269                Ok(ExprStep::Enter(ExprTask::RangeEllipsisBound))
3270            }
3271            ExprCont::RangeRight { left, start_span } => {
3272                let right = expr_of(value);
3273                let end_span = self.expr_span(right).unwrap_or_else(|| start_span.clone());
3274                let span = self.span_covering(&start_span, &end_span);
3275                expr_step(self.new_expression(
3276                    ExpressionKind::RangeLiteral(left, right),
3277                    self.make_source(span),
3278                ))
3279            }
3280            ExprCont::EllipsisFirst { start_span } => {
3281                let starts = vec![start_span.start];
3282                self.ellipsis_chain(expr_of(value), Vec::new(), start_span, starts, frames)
3283            }
3284            ExprCont::EllipsisOperand {
3285                first,
3286                mut steps,
3287                start_span,
3288                operation,
3289                starts,
3290            } => {
3291                let operand = expr_of(value);
3292                let end_span = self
3293                    .expr_span(operand)
3294                    .unwrap_or_else(|| start_span.clone());
3295                let span = self.span_covering(&start_span, &end_span);
3296                steps.push(ChainStep {
3297                    op: operation,
3298                    operand,
3299                    source_location: self.make_source(span),
3300                });
3301                self.ellipsis_chain(first, steps, start_span, starts, frames)
3302            }
3303            ExprCont::FactorNegate { start_span } => {
3304                let operand = expr_of(value);
3305                let end_span = self
3306                    .expr_span(operand)
3307                    .unwrap_or_else(|| start_span.clone());
3308                let span = self.span_covering(&start_span, &end_span);
3309                if let ExpressionKind::Literal(literal) = self.table.kind(operand) {
3310                    if let Ok(negated) = try_negate_numeric_literal(literal.clone()) {
3311                        return expr_step(self.new_expression(
3312                            ExpressionKind::Literal(negated),
3313                            self.make_source(span),
3314                        ));
3315                    }
3316                }
3317                let zero = self.new_expression(
3318                    ExpressionKind::Literal(Value::Number(Decimal::ZERO)),
3319                    self.make_source(start_span),
3320                )?;
3321                let step = ChainStep {
3322                    op: ArithmeticComputation::Subtract,
3323                    operand,
3324                    source_location: self.make_source(span),
3325                };
3326                expr_step(self.arithmetic_chain(zero, vec![step]))
3327            }
3328            ExprCont::PrimaryOrMathSuffix { start_span } => {
3329                let expr = expr_of(value);
3330                if !self.bare_operand && self.at(&TokenKind::Arrow)? {
3331                    frames.push(ExprCont::PrimaryOrMathSuffix {
3332                        start_span: start_span.clone(),
3333                    });
3334                    return Ok(ExprStep::Enter(ExprTask::ListCommandSuffix {
3335                        list: expr,
3336                        start_span,
3337                    }));
3338                }
3339                Ok(ExprStep::Yield(ExprVal::Expr(expr)))
3340            }
3341            ExprCont::ListCommandDone { list, start_span } => {
3342                let command = command_of(value);
3343                let span = self.span_covering(&start_span, &self.last_span);
3344                expr_step(self.new_expression(
3345                    ExpressionKind::ListCommand { list, command },
3346                    self.make_source(span),
3347                ))
3348            }
3349            ExprCont::Command(builder) => Ok(ExprStep::Yield(ExprVal::Command(build_command(
3350                builder, value,
3351            )))),
3352            ExprCont::FindKey { mut keys } => {
3353                keys.push(expr_of(value));
3354                self.find_key_next(keys, frames)
3355            }
3356            ExprCont::CallArgumentsEnd { command } => {
3357                self.expect(&TokenKind::RParen)?;
3358                self.reject_comma_after_call(command)?;
3359                Ok(ExprStep::Yield(value))
3360            }
3361            ExprCont::CallArgumentItems { mut arguments } => {
3362                arguments.push(expr_of(value));
3363                self.call_argument_items(arguments, frames)
3364            }
3365            ExprCont::SingleCallArgumentCheck { command } => {
3366                let mut arguments = exprs_of(value);
3367                if arguments.len() != 1 {
3368                    let tok = self.peek()?.clone();
3369                    return Err(self.error_at_token(
3370                        &tok,
3371                        format!("`{command}` takes one argument, got {}", arguments.len()),
3372                    ));
3373                }
3374                Ok(ExprStep::Yield(ExprVal::Expr(arguments.remove(0))))
3375            }
3376            ExprCont::WrapSome => Ok(ExprStep::Yield(ExprVal::Optional(Some(expr_of(value))))),
3377            ExprCont::BareArgumentEnd { command } => {
3378                let argument = expr_of(value);
3379                if !self.at_bare_argument_end()? {
3380                    return Err(self.bare_argument_error(command));
3381                }
3382                Ok(ExprStep::Yield(ExprVal::Expr(argument)))
3383            }
3384            ExprCont::ParenEnd { start_span } => {
3385                let mut inner = expr_of(value);
3386                self.expect(&TokenKind::RParen)?;
3387                if self.at(&TokenKind::Dot)? {
3388                    self.next()?;
3389                    let path = self.parse_reference()?;
3390                    let span = self.span_covering(&start_span, &self.last_span);
3391                    inner = self.new_expression(
3392                        ExpressionKind::Member { base: inner, path },
3393                        self.make_source(span),
3394                    )?;
3395                }
3396                Ok(ExprStep::Yield(ExprVal::Expr(inner)))
3397            }
3398            ExprCont::PastFuture { kind, start_span } => {
3399                let offset = expr_of(value);
3400                let offset_span = self.expr_span(offset).unwrap_or_else(|| start_span.clone());
3401                let span = self.span_covering(&start_span, &offset_span);
3402                expr_step(self.new_expression(
3403                    ExpressionKind::PastFutureRange(kind, offset),
3404                    self.make_source(span),
3405                ))
3406            }
3407            ExprCont::Math {
3408                operator,
3409                start_span,
3410            } => {
3411                let operand = expr_of(value);
3412                let end_span = self
3413                    .expr_span(operand)
3414                    .unwrap_or_else(|| start_span.clone());
3415                let span = self.span_covering(&start_span, &end_span);
3416                expr_step(self.new_expression(
3417                    ExpressionKind::MathematicalComputation(operator, operand),
3418                    self.make_source(span),
3419                ))
3420            }
3421            ExprCont::LowerUpperEnd { bound, start_span } => {
3422                let operand = expr_of(value);
3423                let end_span = self
3424                    .expr_span(operand)
3425                    .unwrap_or_else(|| start_span.clone());
3426                let span = self.span_covering(&start_span, &end_span);
3427                expr_step(self.new_expression(
3428                    ExpressionKind::RangeBound(bound, operand),
3429                    self.make_source(span),
3430                ))
3431            }
3432            ExprCont::PrefixArgumentEnd { keyword } => {
3433                let argument = expr_of(value);
3434                if !self.at_bare_argument_end()? {
3435                    let tok = self.peek()?.clone();
3436                    return Err(self.error_at_token(
3437                        &tok,
3438                        format!(
3439                            "`{keyword}` without parentheses takes one operand, found '{op}' after it; \
3440                             write `{keyword}(a {op} b)` or `{keyword}(a) {op} b`",
3441                            op = tok.text
3442                        ),
3443                    ));
3444                }
3445                Ok(ExprStep::Yield(ExprVal::Expr(argument)))
3446            }
3447            ExprCont::PredicateValueRight { op, command } => {
3448                let right = expr_of(value);
3449                if !self.at_bare_argument_end()? {
3450                    return Err(self.condition_error(command));
3451                }
3452                Ok(ExprStep::Yield(ExprVal::Predicate(ListPredicate::Value {
3453                    op,
3454                    right,
3455                })))
3456            }
3457            ExprCont::PredicateLeft {
3458                start_span,
3459                command,
3460            } => {
3461                let left = expr_of(value);
3462                if self.at_row_condition_operator()? {
3463                    let op = self.parse_row_condition_operator()?;
3464                    frames.push(ExprCont::PredicateRight {
3465                        left,
3466                        op,
3467                        start_span,
3468                        command,
3469                    });
3470                    return Ok(ExprStep::Enter(ExprTask::BareOperand));
3471                }
3472                self.predicate_instance_end(left, command)
3473            }
3474            ExprCont::PredicateRight {
3475                left,
3476                op,
3477                start_span,
3478                command,
3479            } => {
3480                let right = expr_of(value);
3481                let span = self.span_covering(&start_span, &self.last_span);
3482                let kind = match op {
3483                    RowCondition::Compare(op) => ExpressionKind::Comparison(left, op, right),
3484                    RowCondition::In => ExpressionKind::RangeContainment(left, right),
3485                };
3486                let expr = self.new_expression(kind, self.make_source(span))?;
3487                self.predicate_instance_end(expr, command)
3488            }
3489            ExprCont::WrapInstance => Ok(ExprStep::Yield(ExprVal::Predicate(
3490                ListPredicate::Instance {
3491                    expr: expr_of(value),
3492                },
3493            ))),
3494            ExprCont::ValuePredicateEnd { op, command } => {
3495                let right = expr_of(value);
3496                if !self.at(&TokenKind::RParen)? {
3497                    let tok = self.peek()?.clone();
3498                    return Err(self.error_at_token(
3499                        &tok,
3500                        format!(
3501                            "a value-list `{command}` condition is one comparison, found '{}'; chain \
3502                             `where` or use `in` with a range",
3503                            tok.text
3504                        ),
3505                    ));
3506                }
3507                Ok(ExprStep::Yield(ExprVal::Predicate(ListPredicate::Value {
3508                    op,
3509                    right,
3510                })))
3511            }
3512            ExprCont::CallPredicateEnd { command } => {
3513                self.expect(&TokenKind::RParen)?;
3514                self.reject_comma_after_call(command)?;
3515                Ok(ExprStep::Yield(value))
3516            }
3517            ExprCont::SortArgument { check_bare } => {
3518                let argument = expr_of(value);
3519                if check_bare
3520                    && !self.at_any(&[TokenKind::Asc, TokenKind::Desc])?
3521                    && !self.at_bare_argument_end()?
3522                {
3523                    return Err(self.bare_argument_error("sort"));
3524                }
3525                let descending = self.parse_sort_direction()?;
3526                Ok(ExprStep::Yield(ExprVal::Command(ListCommand::Sort {
3527                    descending,
3528                    argument: Some(argument),
3529                })))
3530            }
3531            ExprCont::ListWithEnd => {
3532                let rows = rows_of(value);
3533                self.expect(&TokenKind::RParen)?;
3534                self.reject_comma_after_call("with")?;
3535                Ok(ExprStep::Yield(ExprVal::Rows(rows)))
3536            }
3537            ExprCont::PairValue { path, mut pairs } => {
3538                pairs.push((path, expr_of(value)));
3539                self.pair_value_next(pairs, frames)
3540            }
3541            ExprCont::ValueItem { mut values } => {
3542                values.push(expr_of(value));
3543                self.value_item_next(values, frames)
3544            }
3545        }
3546    }
3547
3548    fn and_chain(
3549        &mut self,
3550        first: ExprId,
3551        steps: Vec<ChainStep<()>>,
3552        start_span: Span,
3553        frames: &mut Vec<ExprCont>,
3554    ) -> Result<ExprStep, Error> {
3555        if !self.at(&TokenKind::And)? {
3556            return match steps.last() {
3557                None => Ok(ExprStep::Yield(ExprVal::Expr(first))),
3558                Some(last) => {
3559                    let source = last.source_location.clone();
3560                    expr_step(
3561                        self.new_expression(ExpressionKind::LogicalAnd { first, steps }, source),
3562                    )
3563                }
3564            };
3565        }
3566        self.next()?;
3567        frames.push(ExprCont::AndOperand {
3568            first,
3569            steps,
3570            start_span,
3571        });
3572        Ok(ExprStep::Enter(ExprTask::AndOperand))
3573    }
3574
3575    fn continue_repository(
3576        &mut self,
3577        mut expr: ExprId,
3578        start_span: Span,
3579        frames: &mut Vec<ExprCont>,
3580    ) -> Result<ExprStep, Error> {
3581        loop {
3582            let kind = self.peek()?.kind.clone();
3583            if is_comparison_operator(&kind) {
3584                return Ok(ExprStep::Enter(ExprTask::ComparisonSuffix {
3585                    left: expr,
3586                    start_span,
3587                }));
3588            }
3589            if kind == TokenKind::Not {
3590                expr = self.parse_not_in_calendar_suffix(expr, start_span.clone())?;
3591                continue;
3592            }
3593            if kind == TokenKind::In {
3594                frames.push(ExprCont::ContinueRepository {
3595                    start_span: start_span.clone(),
3596                });
3597                return Ok(ExprStep::Enter(ExprTask::InSuffix {
3598                    repository: expr,
3599                    start_span,
3600                }));
3601            }
3602            if kind == TokenKind::As {
3603                expr = self.parse_as_chain(expr, start_span.clone())?;
3604                continue;
3605            }
3606            break;
3607        }
3608
3609        if self.at_expression_suffix_end()? {
3610            return expr_step(Ok(expr));
3611        }
3612
3613        let tok = self.peek()?.clone();
3614        Err(self.error_at_token(
3615            &tok,
3616            format!("Unexpected token '{}' after expression", tok.text),
3617        ))
3618    }
3619
3620    fn enter_comparison_suffix(
3621        &mut self,
3622        left: ExprId,
3623        start_span: Span,
3624        frames: &mut Vec<ExprCont>,
3625    ) -> Result<ExprStep, Error> {
3626        let operator = self.parse_comparison_operator()?;
3627        let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
3628
3629        if matches!(
3630            operator,
3631            ComparisonComputation::Is | ComparisonComputation::IsNot
3632        ) && self.at_bare_veto_token()?
3633        {
3634            self.expect(&TokenKind::Veto)?;
3635            if self.at(&TokenKind::StringLit)? {
3636                let tok = self.peek()?.clone();
3637                return Err(self.error_at_token(
3638                    &tok,
3639                    "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
3640                ));
3641            }
3642            return expr_step(self.wrap_result_is_veto_expression(
3643                left,
3644                operator_is_not,
3645                false,
3646                start_span,
3647            ));
3648        }
3649
3650        let right = if self.at(&TokenKind::Not)? {
3651            ExprTask::NotExpression
3652        } else {
3653            ExprTask::RepositoryExpression
3654        };
3655        frames.push(ExprCont::ComparisonRight {
3656            left,
3657            operator,
3658            start_span,
3659        });
3660        Ok(ExprStep::Enter(right))
3661    }
3662
3663    fn enter_in_suffix(
3664        &mut self,
3665        repository: ExprId,
3666        start_span: Span,
3667        frames: &mut Vec<ExprCont>,
3668    ) -> Result<ExprStep, Error> {
3669        self.expect(&TokenKind::In)?;
3670
3671        let peeked = self.peek()?.clone();
3672        if peeked.kind == TokenKind::Past || peeked.kind == TokenKind::Future {
3673            let direction = self.next()?;
3674            let rel_kind = if direction.kind == TokenKind::Past {
3675                DateRelativeKind::InPast
3676            } else {
3677                DateRelativeKind::InFuture
3678            };
3679
3680            if self.at_calendar_period_marker()? {
3681                self.next_calendar_period_marker()?;
3682                let cal_kind = if direction.kind == TokenKind::Past {
3683                    DateCalendarKind::Past
3684                } else {
3685                    DateCalendarKind::Future
3686                };
3687                let unit = self.parse_calendar_unit()?;
3688                let end = self.peek()?.span.clone();
3689                let span = self.span_covering(&start_span, &end);
3690                return expr_step(self.new_expression(
3691                    ExpressionKind::DateCalendar(cal_kind, unit, repository),
3692                    self.make_source(span),
3693                ));
3694            }
3695
3696            if self.at(&TokenKind::And)?
3697                || self.at(&TokenKind::Unless)?
3698                || self.at(&TokenKind::Then)?
3699                || self.at(&TokenKind::RParen)?
3700                || self.at(&TokenKind::Eof)?
3701                || self.at(&TokenKind::Spec)?
3702                || is_spec_body_keyword(&self.peek()?.kind)
3703                || is_comparison_operator(&self.peek()?.kind)
3704            {
3705                let end = self.peek()?.span.clone();
3706                let span = self.span_covering(&start_span, &end);
3707                return expr_step(self.new_expression(
3708                    ExpressionKind::DateRelative(rel_kind, repository),
3709                    self.make_source(span),
3710                ));
3711            }
3712
3713            frames.push(ExprCont::InOffset {
3714                direction: direction.span,
3715                rel_kind,
3716                repository,
3717                start_span,
3718            });
3719            return Ok(ExprStep::Enter(ExprTask::RepositoryExpression));
3720        }
3721
3722        if token_is_calendar_period_marker(&peeked) {
3723            self.next_calendar_period_marker()?;
3724            let unit = self.parse_calendar_unit()?;
3725            let end = self.peek()?.span.clone();
3726            let span = self.span_covering(&start_span, &end);
3727            return expr_step(self.new_expression(
3728                ExpressionKind::DateCalendar(DateCalendarKind::Current, unit, repository),
3729                self.make_source(span),
3730            ));
3731        }
3732
3733        frames.push(ExprCont::InRange {
3734            repository,
3735            start_span,
3736        });
3737        Ok(ExprStep::Enter(ExprTask::RepositoryExpression))
3738    }
3739
3740    fn enter_prefix_argument(
3741        &mut self,
3742        keyword: String,
3743        frames: &mut Vec<ExprCont>,
3744    ) -> Result<ExprStep, Error> {
3745        if self.at(&TokenKind::LParen)? {
3746            return Ok(ExprStep::Enter(ExprTask::Primary));
3747        }
3748        if !self.at_bare_operand_start()? {
3749            let tok = self.peek()?.clone();
3750            return Err(self.error_at_token(
3751                &tok,
3752                format!(
3753                    "`{keyword}` takes one operand or `{keyword}(...)`, found '{}'",
3754                    tok.text
3755                ),
3756            ));
3757        }
3758        self.check_depth()?;
3759        frames.push(ExprCont::PrefixArgumentEnd { keyword });
3760        frames.push(ExprCont::PopDepth);
3761        Ok(ExprStep::Enter(ExprTask::RangeOperand))
3762    }
3763
3764    fn enter_primary(&mut self, frames: &mut Vec<ExprCont>) -> Result<ExprStep, Error> {
3765        let start_span = self.peek()?.span.clone();
3766        let kind = self.peek()?.kind.clone();
3767        match &kind {
3768            TokenKind::LParen => {
3769                self.next()?;
3770                let saved = self.save_flags();
3771                self.bare_operand = false;
3772                self.call_argument_depth = 0;
3773                frames.push(ExprCont::ParenEnd { start_span });
3774                frames.push(ExprCont::Restore(saved));
3775                Ok(ExprStep::Enter(ExprTask::Expression))
3776            }
3777            TokenKind::Now => {
3778                let tok = self.next()?;
3779                expr_step(self.new_expression(ExpressionKind::Now, self.make_source(tok.span)))
3780            }
3781            TokenKind::Past | TokenKind::Future => {
3782                let tok = self.next()?;
3783                let rel_kind = if tok.kind == TokenKind::Past {
3784                    DateRelativeKind::InPast
3785                } else {
3786                    DateRelativeKind::InFuture
3787                };
3788                frames.push(ExprCont::PastFuture {
3789                    kind: rel_kind,
3790                    start_span,
3791                });
3792                Ok(ExprStep::Enter(ExprTask::RepositoryExpression))
3793            }
3794            TokenKind::StringLit => {
3795                let tok = self.next()?;
3796                expr_step(self.new_expression(
3797                    ExpressionKind::Literal(Value::Text(tok.text)),
3798                    self.make_source(tok.span),
3799                ))
3800            }
3801            k if is_boolean_keyword(k) => {
3802                let tok = self.next()?;
3803                expr_step(self.new_expression(
3804                    ExpressionKind::Literal(Value::Boolean(token_kind_to_boolean_value(&tok.kind))),
3805                    self.make_source(tok.span),
3806                ))
3807            }
3808            TokenKind::NumberLit => expr_step(self.parse_number_expression()),
3809            k if can_be_label(k) => {
3810                let reference = self.parse_expression_reference()?;
3811                let span = self.span_covering(&start_span, &self.last_span);
3812                let expr = self
3813                    .new_expression(ExpressionKind::Reference(reference), self.make_source(span))?;
3814                expr_step(self.wrap_list_type_suffix(expr, start_span))
3815            }
3816            k if token_kind_to_primitive(k).is_some() => {
3817                if !self.next_is_list_type_suffix()? {
3818                    let tok = self.next()?;
3819                    return Err(self.error_at_token(
3820                        &tok,
3821                        format!("Expected an expression, found '{}'", tok.text),
3822                    ));
3823                }
3824                let parent = self.parse_expression_parent_type()?;
3825                let span = self.span_covering(&start_span, &self.last_span);
3826                expr_step(self.new_expression(
3827                    ExpressionKind::ListType { element: parent },
3828                    self.make_source(span),
3829                ))
3830            }
3831            _ => {
3832                let tok = self.next()?;
3833                Err(self.error_at_token(
3834                    &tok,
3835                    format!("Expected an expression, found '{}'", tok.text),
3836                ))
3837            }
3838        }
3839    }
3840
3841    fn enter_list_command_body(
3842        &mut self,
3843        name: Token,
3844        frames: &mut Vec<ExprCont>,
3845    ) -> Result<ExprStep, Error> {
3846        match name.kind {
3847            TokenKind::Find => {
3848                frames.push(ExprCont::Command(CommandBuild::Find));
3849                Ok(ExprStep::Enter(ExprTask::FindKeys))
3850            }
3851            TokenKind::First => {
3852                self.reject_command_argument("first")?;
3853                Ok(ExprStep::Yield(ExprVal::Command(ListCommand::First)))
3854            }
3855            TokenKind::Last => {
3856                self.reject_command_argument("last")?;
3857                Ok(ExprStep::Yield(ExprVal::Command(ListCommand::Last)))
3858            }
3859            TokenKind::Count => {
3860                self.reject_command_argument("count")?;
3861                Ok(ExprStep::Yield(ExprVal::Command(ListCommand::Count)))
3862            }
3863            TokenKind::Sum => {
3864                frames.push(ExprCont::Command(CommandBuild::Sum));
3865                Ok(ExprStep::Enter(ExprTask::OptionalArgument {
3866                    command: "sum",
3867                }))
3868            }
3869            TokenKind::Avg => {
3870                frames.push(ExprCont::Command(CommandBuild::Avg));
3871                Ok(ExprStep::Enter(ExprTask::OptionalArgument {
3872                    command: "avg",
3873                }))
3874            }
3875            TokenKind::Product => {
3876                frames.push(ExprCont::Command(CommandBuild::Product));
3877                Ok(ExprStep::Enter(ExprTask::OptionalArgument {
3878                    command: "product",
3879                }))
3880            }
3881            TokenKind::Min => {
3882                frames.push(ExprCont::Command(CommandBuild::Min));
3883                Ok(ExprStep::Enter(ExprTask::OptionalArgument {
3884                    command: "min",
3885                }))
3886            }
3887            TokenKind::Max => {
3888                frames.push(ExprCont::Command(CommandBuild::Max));
3889                Ok(ExprStep::Enter(ExprTask::OptionalArgument {
3890                    command: "max",
3891                }))
3892            }
3893            TokenKind::Any => {
3894                frames.push(ExprCont::Command(CommandBuild::Any));
3895                Ok(ExprStep::Enter(ExprTask::ListPredicate { command: "any" }))
3896            }
3897            TokenKind::All => {
3898                frames.push(ExprCont::Command(CommandBuild::All));
3899                Ok(ExprStep::Enter(ExprTask::ListPredicate { command: "all" }))
3900            }
3901            TokenKind::Where => {
3902                frames.push(ExprCont::Command(CommandBuild::Where));
3903                Ok(ExprStep::Enter(ExprTask::ListPredicate {
3904                    command: "where",
3905                }))
3906            }
3907            TokenKind::Sort => Ok(ExprStep::Enter(ExprTask::SortCommand)),
3908            TokenKind::Top => {
3909                frames.push(ExprCont::Command(CommandBuild::Top));
3910                Ok(ExprStep::Enter(ExprTask::RequiredArgument {
3911                    command: "top",
3912                }))
3913            }
3914            TokenKind::With => {
3915                frames.push(ExprCont::Command(CommandBuild::With));
3916                Ok(ExprStep::Enter(ExprTask::ListWithCommand))
3917            }
3918            _ => Err(self.error_at_token(&name, format!("Unknown list command '{}'", name.text))),
3919        }
3920    }
3921
3922    fn enter_list_predicate(
3923        &mut self,
3924        command: &'static str,
3925        frames: &mut Vec<ExprCont>,
3926    ) -> Result<ExprStep, Error> {
3927        if self.at(&TokenKind::LParen)? {
3928            return Ok(ExprStep::Enter(ExprTask::CallListPredicate { command }));
3929        }
3930        if self.at_row_condition_operator()? {
3931            let op = self.parse_row_condition_operator()?;
3932            frames.push(ExprCont::PredicateValueRight { op, command });
3933            return Ok(ExprStep::Enter(ExprTask::BareOperand));
3934        }
3935        if !self.at_bare_operand_start()? {
3936            let tok = self.peek()?.clone();
3937            return Err(self.error_at_token(
3938                &tok,
3939                format!("`{command}` needs a condition; write `{command}(...)`"),
3940            ));
3941        }
3942        let start_span = self.peek()?.span.clone();
3943        frames.push(ExprCont::PredicateLeft {
3944            start_span,
3945            command,
3946        });
3947        Ok(ExprStep::Enter(ExprTask::BareOperand))
3948    }
3949
3950    fn predicate_instance_end(
3951        &mut self,
3952        expr: ExprId,
3953        command: &'static str,
3954    ) -> Result<ExprStep, Error> {
3955        if !self.at_bare_argument_end()? {
3956            return Err(self.condition_error(command));
3957        }
3958        Ok(ExprStep::Yield(ExprVal::Predicate(
3959            ListPredicate::Instance { expr },
3960        )))
3961    }
3962
3963    fn enter_pair_field(
3964        &mut self,
3965        pairs: Vec<(Reference, ExprId)>,
3966        frames: &mut Vec<ExprCont>,
3967    ) -> Result<ExprStep, Error> {
3968        let path = self.parse_reference()?;
3969        self.expect(&TokenKind::Colon)?;
3970        frames.push(ExprCont::PairValue { path, pairs });
3971        Ok(ExprStep::Enter(ExprTask::BareArgument { command: "with" }))
3972    }
3973
3974    fn pair_value_next(
3975        &mut self,
3976        pairs: Vec<(Reference, ExprId)>,
3977        frames: &mut Vec<ExprCont>,
3978    ) -> Result<ExprStep, Error> {
3979        if !self.at(&TokenKind::Comma)? {
3980            return Ok(ExprStep::Yield(ExprVal::Rows(ListWithRows::FieldPairs(
3981                pairs,
3982            ))));
3983        }
3984        self.next()?;
3985        if !self.at_path_then_colon()? {
3986            let tok = self.peek()?.clone();
3987            return Err(self.error_at_token_with_suggestion(
3988                &tok,
3989                "Expected a field pair after comma".to_string(),
3990                "Remove the extra comma, or add `field: value`",
3991            ));
3992        }
3993        self.enter_pair_field(pairs, frames)
3994    }
3995
3996    fn value_item_next(
3997        &mut self,
3998        values: Vec<ExprId>,
3999        frames: &mut Vec<ExprCont>,
4000    ) -> Result<ExprStep, Error> {
4001        if !self.at(&TokenKind::Comma)? {
4002            return Ok(ExprStep::Yield(ExprVal::Rows(ListWithRows::Values(values))));
4003        }
4004        self.next()?;
4005        if self.at_expression_suffix_end()? || self.at(&TokenKind::Comma)? {
4006            let tok = self.peek()?.clone();
4007            return Err(self.error_at_token_with_suggestion(
4008                &tok,
4009                "Trailing or empty comma in list".to_string(),
4010                "Remove the extra comma, or add the missing value",
4011            ));
4012        }
4013        frames.push(ExprCont::ValueItem { values });
4014        Ok(ExprStep::Enter(ExprTask::BareArgument { command: "with" }))
4015    }
4016
4017    fn find_key_next(
4018        &mut self,
4019        keys: Vec<ExprId>,
4020        frames: &mut Vec<ExprCont>,
4021    ) -> Result<ExprStep, Error> {
4022        if self.at(&TokenKind::Comma)? && self.call_argument_depth == 0 {
4023            self.next()?;
4024            frames.push(ExprCont::FindKey { keys });
4025            return Ok(ExprStep::Enter(ExprTask::BareArgument { command: "find" }));
4026        }
4027        Ok(ExprStep::Yield(ExprVal::Exprs(keys)))
4028    }
4029
4030    fn call_argument_items(
4031        &mut self,
4032        arguments: Vec<ExprId>,
4033        frames: &mut Vec<ExprCont>,
4034    ) -> Result<ExprStep, Error> {
4035        if self.at(&TokenKind::Comma)? {
4036            self.next()?;
4037            frames.push(ExprCont::CallArgumentItems { arguments });
4038            return Ok(ExprStep::Enter(ExprTask::Expression));
4039        }
4040        Ok(ExprStep::Yield(ExprVal::Exprs(arguments)))
4041    }
4042
4043    fn repository_chain(
4044        &mut self,
4045        first: ExprId,
4046        steps: Vec<ChainStep<ArithmeticComputation>>,
4047        start_span: Span,
4048        frames: &mut Vec<ExprCont>,
4049    ) -> Result<ExprStep, Error> {
4050        if !self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
4051            return expr_step(self.arithmetic_chain(first, steps));
4052        }
4053        let op_tok = self.next()?;
4054        let operation = match op_tok.kind {
4055            TokenKind::Plus => ArithmeticComputation::Add,
4056            TokenKind::Minus => ArithmeticComputation::Subtract,
4057            _ => unreachable!("BUG: only + and - should reach here"),
4058        };
4059        frames.push(ExprCont::RepositoryOperand {
4060            first,
4061            steps,
4062            start_span,
4063            operation,
4064        });
4065        Ok(ExprStep::Enter(ExprTask::Term { allow_as: true }))
4066    }
4067
4068    fn term_chain(
4069        &mut self,
4070        first: ExprId,
4071        steps: Vec<ChainStep<ArithmeticComputation>>,
4072        allow_as: bool,
4073        start_span: Span,
4074        frames: &mut Vec<ExprCont>,
4075    ) -> Result<ExprStep, Error> {
4076        if self.at(&TokenKind::Percent)? {
4077            let pct_tok = self.next()?;
4078            return Err(self.error_at_token(
4079                &pct_tok,
4080                "'%' is percent only; use 'mod' for remainder (e.g. 'value mod 7')",
4081            ));
4082        }
4083        if !self.at_any(&[TokenKind::Star, TokenKind::Slash, TokenKind::Mod])? {
4084            return expr_step(self.arithmetic_chain(first, steps));
4085        }
4086        let op_tok = self.next()?;
4087        let operation = match op_tok.kind {
4088            TokenKind::Star => ArithmeticComputation::Multiply,
4089            TokenKind::Slash => ArithmeticComputation::Divide,
4090            TokenKind::Mod => ArithmeticComputation::Modulo,
4091            _ => unreachable!("BUG: only *, /, mod should reach here"),
4092        };
4093        let right_start_span = self.peek()?.span.clone();
4094        frames.push(ExprCont::TermOperand {
4095            first,
4096            steps,
4097            allow_as,
4098            start_span,
4099            operation,
4100            right_start_span,
4101        });
4102        Ok(ExprStep::Enter(ExprTask::Power))
4103    }
4104
4105    fn power_tail(
4106        &mut self,
4107        left: ExprId,
4108        start_span: Span,
4109        range_bound: bool,
4110        frames: &mut Vec<ExprCont>,
4111    ) -> Result<ExprStep, Error> {
4112        if !self.at(&TokenKind::Caret)? {
4113            return Ok(ExprStep::Yield(ExprVal::Expr(left)));
4114        }
4115        self.next()?;
4116        self.check_depth()?;
4117        frames.push(ExprCont::PowerRight { left, start_span });
4118        frames.push(ExprCont::PopDepth);
4119        Ok(ExprStep::Enter(if range_bound {
4120            ExprTask::PowerForRangeBound
4121        } else {
4122            ExprTask::Power
4123        }))
4124    }
4125
4126    fn ellipsis_chain(
4127        &mut self,
4128        first: ExprId,
4129        steps: Vec<ChainStep<ArithmeticComputation>>,
4130        start_span: Span,
4131        mut starts: Vec<usize>,
4132        frames: &mut Vec<ExprCont>,
4133    ) -> Result<ExprStep, Error> {
4134        if !self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
4135            self.bound_chain_starts = starts;
4136            return expr_step(self.arithmetic_chain(first, steps));
4137        }
4138        let op_tok = self.next()?;
4139        let operation = match op_tok.kind {
4140            TokenKind::Plus => ArithmeticComputation::Add,
4141            TokenKind::Minus => ArithmeticComputation::Subtract,
4142            _ => unreachable!("BUG: only + and - should reach here"),
4143        };
4144        starts.push(self.peek()?.span.start);
4145        frames.push(ExprCont::EllipsisOperand {
4146            first,
4147            steps,
4148            start_span,
4149            operation,
4150            starts,
4151        });
4152        Ok(ExprStep::Enter(ExprTask::PowerForRangeBound))
4153    }
4154}
4155
4156/// Expression grammar state saved and restored around nested parses.
4157#[derive(Clone, Copy, PartialEq, Eq)]
4158struct ExprFlags {
4159    bare_operand: bool,
4160    call_argument_depth: usize,
4161}
4162
4163/// A grammar nonterminal the expression machine can enter.
4164enum ExprTask {
4165    Expression,
4166    AndExpression,
4167    AndOperand,
4168    VetoStatus,
4169    NotExpression,
4170    RepositoryWithSuffix,
4171    ComparisonSuffix {
4172        left: ExprId,
4173        start_span: Span,
4174    },
4175    InSuffix {
4176        repository: ExprId,
4177        start_span: Span,
4178    },
4179    RepositoryExpression,
4180    Term {
4181        allow_as: bool,
4182    },
4183    Power,
4184    RangeOperand,
4185    RangeEllipsisBound,
4186    PowerForRangeBound,
4187    Factor,
4188    PrimaryOrMath,
4189    MathFunction,
4190    LowerUpper,
4191    PrefixArgument {
4192        keyword: String,
4193    },
4194    Primary,
4195    ListCommandSuffix {
4196        list: ExprId,
4197        start_span: Span,
4198    },
4199    ListCommandBody {
4200        name: Token,
4201    },
4202    FindKeys,
4203    CallArguments {
4204        command: &'static str,
4205    },
4206    SingleCallArgument {
4207        command: &'static str,
4208    },
4209    OptionalArgument {
4210        command: &'static str,
4211    },
4212    RequiredArgument {
4213        command: &'static str,
4214    },
4215    BareArgument {
4216        command: &'static str,
4217    },
4218    BareOperand,
4219    ListPredicate {
4220        command: &'static str,
4221    },
4222    CallListPredicate {
4223        command: &'static str,
4224    },
4225    ValuePredicateInParentheses {
4226        command: &'static str,
4227    },
4228    SortCommand,
4229    ListWithCommand,
4230    ListWithRows,
4231}
4232
4233/// Command whose argument values are collected before the `ListCommand` is built.
4234#[derive(Clone, Copy)]
4235enum CommandBuild {
4236    Find,
4237    Sum,
4238    Avg,
4239    Product,
4240    Min,
4241    Max,
4242    Any,
4243    All,
4244    Where,
4245    Top,
4246    With,
4247}
4248
4249/// Continuation: what to do with the value a nested nonterminal yields.
4250enum ExprCont {
4251    Restore(ExprFlags),
4252    PopDepth,
4253    AndFirst {
4254        start_span: Span,
4255    },
4256    AndOperand {
4257        first: ExprId,
4258        steps: Vec<ChainStep<()>>,
4259        start_span: Span,
4260    },
4261    VetoStatus {
4262        operator_is_not: bool,
4263        keyword_was_negated: bool,
4264        start_span: Span,
4265    },
4266    Negation {
4267        start_span: Span,
4268    },
4269    RepositorySuffix {
4270        start_span: Span,
4271    },
4272    ContinueRepository {
4273        start_span: Span,
4274    },
4275    ComparisonRight {
4276        left: ExprId,
4277        operator: ComparisonComputation,
4278        start_span: Span,
4279    },
4280    InOffset {
4281        direction: Span,
4282        rel_kind: DateRelativeKind,
4283        repository: ExprId,
4284        start_span: Span,
4285    },
4286    InRange {
4287        repository: ExprId,
4288        start_span: Span,
4289    },
4290    RepositoryFirst {
4291        start_span: Span,
4292    },
4293    RepositoryOperand {
4294        first: ExprId,
4295        steps: Vec<ChainStep<ArithmeticComputation>>,
4296        start_span: Span,
4297        operation: ArithmeticComputation,
4298    },
4299    TermFirst {
4300        allow_as: bool,
4301        start_span: Span,
4302    },
4303    TermOperand {
4304        first: ExprId,
4305        steps: Vec<ChainStep<ArithmeticComputation>>,
4306        allow_as: bool,
4307        start_span: Span,
4308        operation: ArithmeticComputation,
4309        right_start_span: Span,
4310    },
4311    PowerLeft {
4312        start_span: Span,
4313        range_bound: bool,
4314    },
4315    PowerRight {
4316        left: ExprId,
4317        start_span: Span,
4318    },
4319    /// Factor of a range operand is parsed; a range bound may continue it.
4320    RangeFactor {
4321        start_span: Span,
4322        flags: ExprFlags,
4323    },
4324    /// Bound continuing `factor` is parsed speculatively from `checkpoint`, taken right after
4325    /// `factor`. Without a following `...` the parser rewinds there and yields `factor`.
4326    RangeLeft {
4327        factor: ExprId,
4328        start_span: Span,
4329        checkpoint: Box<ParserCheckpoint>,
4330        flags: ExprFlags,
4331    },
4332    RangeRight {
4333        left: ExprId,
4334        start_span: Span,
4335    },
4336    EllipsisFirst {
4337        start_span: Span,
4338    },
4339    EllipsisOperand {
4340        first: ExprId,
4341        steps: Vec<ChainStep<ArithmeticComputation>>,
4342        start_span: Span,
4343        operation: ArithmeticComputation,
4344        starts: Vec<usize>,
4345    },
4346    FactorNegate {
4347        start_span: Span,
4348    },
4349    PrimaryOrMathSuffix {
4350        start_span: Span,
4351    },
4352    ListCommandDone {
4353        list: ExprId,
4354        start_span: Span,
4355    },
4356    Command(CommandBuild),
4357    FindKey {
4358        keys: Vec<ExprId>,
4359    },
4360    CallArgumentsEnd {
4361        command: &'static str,
4362    },
4363    CallArgumentItems {
4364        arguments: Vec<ExprId>,
4365    },
4366    SingleCallArgumentCheck {
4367        command: &'static str,
4368    },
4369    WrapSome,
4370    BareArgumentEnd {
4371        command: &'static str,
4372    },
4373    ParenEnd {
4374        start_span: Span,
4375    },
4376    PastFuture {
4377        kind: DateRelativeKind,
4378        start_span: Span,
4379    },
4380    Math {
4381        operator: MathematicalComputation,
4382        start_span: Span,
4383    },
4384    LowerUpperEnd {
4385        bound: RangeBound,
4386        start_span: Span,
4387    },
4388    PrefixArgumentEnd {
4389        keyword: String,
4390    },
4391    PredicateValueRight {
4392        op: RowCondition,
4393        command: &'static str,
4394    },
4395    PredicateLeft {
4396        start_span: Span,
4397        command: &'static str,
4398    },
4399    PredicateRight {
4400        left: ExprId,
4401        op: RowCondition,
4402        start_span: Span,
4403        command: &'static str,
4404    },
4405    WrapInstance,
4406    ValuePredicateEnd {
4407        op: RowCondition,
4408        command: &'static str,
4409    },
4410    CallPredicateEnd {
4411        command: &'static str,
4412    },
4413    SortArgument {
4414        check_bare: bool,
4415    },
4416    ListWithEnd,
4417    PairValue {
4418        path: Reference,
4419        pairs: Vec<(Reference, ExprId)>,
4420    },
4421    ValueItem {
4422        values: Vec<ExprId>,
4423    },
4424}
4425
4426/// Value a finished nonterminal yields to its continuation.
4427enum ExprVal {
4428    Expr(ExprId),
4429    Optional(Option<ExprId>),
4430    Exprs(Vec<ExprId>),
4431    Command(ListCommand),
4432    Predicate(ListPredicate),
4433    Rows(ListWithRows),
4434}
4435
4436enum ExprStep {
4437    Enter(ExprTask),
4438    Yield(ExprVal),
4439}
4440
4441fn expr_of(value: ExprVal) -> ExprId {
4442    match value {
4443        ExprVal::Expr(expr) => expr,
4444        _ => panic!("BUG: expression machine expected an expression value"),
4445    }
4446}
4447
4448fn optional_of(value: ExprVal) -> Option<ExprId> {
4449    match value {
4450        ExprVal::Optional(argument) => argument,
4451        _ => panic!("BUG: expression machine expected an optional argument"),
4452    }
4453}
4454
4455fn exprs_of(value: ExprVal) -> Vec<ExprId> {
4456    match value {
4457        ExprVal::Exprs(exprs) => exprs,
4458        _ => panic!("BUG: expression machine expected an argument list"),
4459    }
4460}
4461
4462fn predicate_of(value: ExprVal) -> ListPredicate {
4463    match value {
4464        ExprVal::Predicate(predicate) => predicate,
4465        _ => panic!("BUG: expression machine expected a list predicate"),
4466    }
4467}
4468
4469fn rows_of(value: ExprVal) -> ListWithRows {
4470    match value {
4471        ExprVal::Rows(rows) => rows,
4472        _ => panic!("BUG: expression machine expected list rows"),
4473    }
4474}
4475
4476fn command_of(value: ExprVal) -> ListCommand {
4477    match value {
4478        ExprVal::Command(command) => command,
4479        _ => panic!("BUG: expression machine expected a list command"),
4480    }
4481}
4482
4483fn expr_step(result: Result<ExprId, Error>) -> Result<ExprStep, Error> {
4484    result.map(|expr| ExprStep::Yield(ExprVal::Expr(expr)))
4485}
4486
4487fn build_command(builder: CommandBuild, value: ExprVal) -> ListCommand {
4488    match builder {
4489        CommandBuild::Find => ListCommand::Find {
4490            keys: exprs_of(value),
4491        },
4492        CommandBuild::Sum => ListCommand::Sum {
4493            argument: optional_of(value),
4494        },
4495        CommandBuild::Avg => ListCommand::Avg {
4496            argument: optional_of(value),
4497        },
4498        CommandBuild::Product => ListCommand::Product {
4499            argument: optional_of(value),
4500        },
4501        CommandBuild::Min => ListCommand::Min {
4502            argument: optional_of(value),
4503        },
4504        CommandBuild::Max => ListCommand::Max {
4505            argument: optional_of(value),
4506        },
4507        CommandBuild::Any => ListCommand::Any {
4508            form: predicate_of(value),
4509        },
4510        CommandBuild::All => ListCommand::All {
4511            form: predicate_of(value),
4512        },
4513        CommandBuild::Where => ListCommand::Where {
4514            form: predicate_of(value),
4515        },
4516        CommandBuild::Top => ListCommand::Top { n: expr_of(value) },
4517        CommandBuild::With => ListCommand::With {
4518            rows: rows_of(value),
4519        },
4520    }
4521}
4522
4523fn parent_type_from_reference(reference: &Reference) -> ParentType {
4524    if reference.segments.is_empty() {
4525        return ParentType::Custom {
4526            name: reference.name.clone(),
4527        };
4528    }
4529    if reference.segments.len() == 1 {
4530        return ParentType::Qualified {
4531            spec_alias: reference.segments[0].clone(),
4532            inner: Box::new(ParentType::Custom {
4533                name: reference.name.clone(),
4534            }),
4535        };
4536    }
4537    ParentType::Qualified {
4538        spec_alias: reference.segments[0].clone(),
4539        inner: Box::new(ParentType::Custom {
4540            name: reference.name.clone(),
4541        }),
4542    }
4543}
4544
4545// ============================================================================
4546// Helper functions
4547// ============================================================================
4548
4549fn parse_decimal_string(text: &str, span: &Span, parser: &Parser) -> Result<Decimal, Error> {
4550    text.parse::<crate::literals::NumberLiteral>()
4551        .map(|parsed| parsed.0)
4552        .map_err(|message| {
4553            Error::parsing(message, parser.make_source(span.clone()), None::<String>)
4554        })
4555}
4556
4557/// Negate a numeric literal value. Returns `Err(value)` when the value is not a number.
4558fn try_negate_numeric_literal(value: Value) -> Result<Value, Value> {
4559    match value {
4560        Value::Number(d) => Ok(Value::Number(-d)),
4561        Value::NumberWithUnit(d, unit) => Ok(Value::NumberWithUnit(-d, unit)),
4562        other => Err(other),
4563    }
4564}
4565
4566fn is_comparison_operator(kind: &TokenKind) -> bool {
4567    matches!(
4568        kind,
4569        TokenKind::Gt | TokenKind::Lt | TokenKind::Gte | TokenKind::Lte | TokenKind::Is
4570    )
4571}