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_repository_qualifier_segment, is_boolean_keyword, is_keyword,
6 is_math_function, is_spec_body_keyword, token_is_calendar_period_marker,
7 token_kind_to_boolean_value, token_kind_to_primitive, Lexer, LexerCheckpoint, Token, TokenKind,
8};
9use crate::parsing::source::Source;
10use indexmap::IndexMap;
11use rust_decimal::Decimal;
12use std::sync::Arc;
13
14#[derive(Debug)]
15struct DeprecatedStandaloneWith {
16 path: Reference,
17 rhs: WithRhs,
18 source_location: Source,
19}
20
21fn merge_deprecated_standalone_with(
22 data: &mut [LemmaData],
23 pending: Vec<DeprecatedStandaloneWith>,
24) -> Result<(), Error> {
25 for item in pending {
26 let alias = item.path.segments[0].clone();
27 let relative = Reference {
28 segments: item.path.segments[1..].to_vec(),
29 name: item.path.name.clone(),
30 };
31 let import = data.iter_mut().find(|datum| {
32 datum.reference.is_local()
33 && datum.reference.name == alias
34 && matches!(&datum.value, DataValue::Import { .. })
35 });
36 if import.is_none() {
37 return Err(Error::parsing(
38 format!("`uses {alias}: …` is required for standalone `with {alias}.…`"),
39 item.source_location,
40 Some(format!("Add `uses {alias}: <spec_name>` in this spec")),
41 ));
42 }
43 let datum = import.expect("BUG: import row exists after is_none check");
44 let DataValue::Import { bindings, .. } = &mut datum.value else {
45 unreachable!("BUG: matched Import arm in find predicate");
46 };
47 bindings.push(UsesBinding {
48 path: relative,
49 rhs: item.rhs,
50 source_location: item.source_location,
51 deprecated_standalone_with: true,
52 });
53 }
54 Ok(())
55}
56
57#[derive(Debug)]
58pub struct ParseResult {
59 pub repositories: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>>,
60 pub expression_count: usize,
61}
62
63impl ParseResult {
64 #[must_use]
66 pub fn flatten_specs(&self) -> Vec<&LemmaSpec> {
67 self.repositories
68 .values()
69 .flat_map(|specs| specs.iter())
70 .collect()
71 }
72
73 #[must_use]
74 pub fn into_flattened_specs(self) -> Vec<LemmaSpec> {
75 self.repositories.into_values().flatten().collect()
76 }
77}
78
79pub fn parse_repository_qualifier_str(
82 input: &str,
83) -> Result<crate::parsing::ast::RepositoryQualifier, Error> {
84 let limits = ResourceLimits::default();
85 let mut parser = Parser::new(
86 input.trim(),
87 crate::parsing::source::SourceType::Volatile,
88 &limits,
89 );
90 let (qualifier, _) = parser.parse_repository_qualifier()?;
91 if !parser.at(&TokenKind::Eof)? {
92 let tok = parser.peek()?.clone();
93 return Err(parser.error_at_token(
94 &tok,
95 format!(
96 "Unexpected {} after repository qualifier '{}'",
97 tok.kind, qualifier.name
98 ),
99 ));
100 }
101 Ok(qualifier)
102}
103
104pub fn parse(
105 content: &str,
106 source_type: crate::parsing::source::SourceType,
107 limits: &ResourceLimits,
108) -> Result<ParseResult, Error> {
109 if content.len() > limits.max_source_size_bytes {
110 return Err(Error::resource_limit_exceeded(
111 "max_source_size_bytes",
112 format!(
113 "{} bytes ({} MB)",
114 limits.max_source_size_bytes,
115 limits.max_source_size_bytes / (1024 * 1024)
116 ),
117 format!(
118 "{} bytes ({:.2} MB)",
119 content.len(),
120 content.len() as f64 / (1024.0 * 1024.0)
121 ),
122 "Reduce source size or split into multiple specs",
123 None,
124 None,
125 None,
126 ));
127 }
128
129 let mut parser = Parser::new(content, source_type, limits);
130 let repositories = parser.parse_file()?;
131 let mut result = ParseResult {
132 repositories,
133 expression_count: parser.expression_count,
134 };
135 canonicalize_parse_result(&mut result);
136 Ok(result)
137}
138
139fn canonicalize_parse_result(result: &mut ParseResult) {
140 let old = std::mem::take(&mut result.repositories);
141 let mut new_map: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>> = IndexMap::new();
142 for (repo, mut specs) in old {
143 let mut canonical_repo = (*repo).clone();
144 canonicalize_repository(&mut canonical_repo);
145 for spec in &mut specs {
146 canonicalize_lemma_spec(spec);
147 }
148 new_map
149 .entry(Arc::new(canonical_repo))
150 .or_default()
151 .extend(specs);
152 }
153 result.repositories = new_map;
154}
155
156struct Parser {
157 lexer: Lexer,
158 source_type: crate::parsing::source::SourceType,
159 depth_tracker: DepthTracker,
160 expression_count: usize,
161 max_expression_count: usize,
162 max_spec_name_length: usize,
163 max_data_name_length: usize,
164 max_rule_name_length: usize,
165 last_span: Span,
166}
167
168impl Parser {
169 fn new(
170 content: &str,
171 source_type: crate::parsing::source::SourceType,
172 limits: &ResourceLimits,
173 ) -> Self {
174 Parser {
175 lexer: Lexer::new(content, &source_type),
176 source_type,
177 depth_tracker: DepthTracker::with_max_depth(limits.max_expression_depth),
178 expression_count: 0,
179 max_expression_count: limits.max_expression_count,
180 max_spec_name_length: crate::limits::MAX_SPEC_NAME_LENGTH,
181 max_data_name_length: crate::limits::MAX_DATA_NAME_LENGTH,
182 max_rule_name_length: crate::limits::MAX_RULE_NAME_LENGTH,
183 last_span: Span {
184 start: 0,
185 end: 0,
186 line: 1,
187 col: 0,
188 },
189 }
190 }
191
192 fn source_type(&self) -> crate::parsing::source::SourceType {
193 self.source_type.clone()
194 }
195
196 fn peek(&mut self) -> Result<&Token, Error> {
197 self.lexer.peek()
198 }
199
200 fn next(&mut self) -> Result<Token, Error> {
201 let token = self.lexer.next_token()?;
202 self.last_span = token.span.clone();
203 Ok(token)
204 }
205
206 fn at(&mut self, kind: &TokenKind) -> Result<bool, Error> {
207 Ok(&self.peek()?.kind == kind)
208 }
209
210 fn at_any(&mut self, kinds: &[TokenKind]) -> Result<bool, Error> {
211 let current = &self.peek()?.kind;
212 Ok(kinds.contains(current))
213 }
214
215 fn checkpoint(&self) -> (LexerCheckpoint, usize) {
216 (self.lexer.checkpoint(), self.expression_count)
217 }
218
219 fn restore(&mut self, checkpoint: (LexerCheckpoint, usize)) {
220 self.lexer.restore(checkpoint.0);
221 self.expression_count = checkpoint.1;
222 }
223
224 fn expect(&mut self, kind: &TokenKind) -> Result<Token, Error> {
225 let token = self.next()?;
226 if &token.kind == kind {
227 Ok(token)
228 } else {
229 Err(self.error_at_token(&token, format!("Expected {}, found {}", kind, token.kind)))
230 }
231 }
232
233 fn at_calendar_period_marker(&mut self) -> Result<bool, Error> {
234 Ok(token_is_calendar_period_marker(self.peek()?))
235 }
236
237 fn expect_calendar_period_marker(&mut self) -> Result<Token, Error> {
238 let token = self.next()?;
239 if token_is_calendar_period_marker(&token) {
240 Ok(token)
241 } else {
242 Err(self.error_at_token(&token, "Expected 'calendar' (date-period predicate marker)"))
243 }
244 }
245
246 fn next_calendar_period_marker(&mut self) -> Result<Token, Error> {
247 self.expect_calendar_period_marker()
248 }
249
250 fn error_at_token(&self, token: &Token, message: impl Into<String>) -> Error {
251 Error::parsing(
252 message,
253 Source::new(self.source_type(), token.span.clone()),
254 None::<String>,
255 )
256 }
257
258 fn error_at_token_with_suggestion(
259 &self,
260 token: &Token,
261 message: impl Into<String>,
262 suggestion: impl Into<String>,
263 ) -> Error {
264 Error::parsing(
265 message,
266 Source::new(self.source_type(), token.span.clone()),
267 Some(suggestion),
268 )
269 }
270
271 fn parse_spec_ref_trailing_effective(&mut self) -> Result<Option<DateTimeValue>, Error> {
272 let mut effective = None;
273 if self.at(&TokenKind::NumberLit)? {
274 let peeked = self.peek()?;
275 if peeked.text.len() == 4 && peeked.text.chars().all(|c| c.is_ascii_digit()) {
276 effective = self.try_parse_effective_from()?;
277 }
278 }
279 Ok(effective)
280 }
281
282 fn make_source(&self, span: Span) -> Source {
283 Source::new(self.source_type(), span)
284 }
285
286 fn span_from(&self, start: &Span) -> Span {
287 Span {
290 start: start.start,
291 end: start.end.max(start.start),
292 line: start.line,
293 col: start.col,
294 }
295 }
296
297 fn span_covering(&self, start: &Span, end: &Span) -> Span {
298 Span {
299 start: start.start,
300 end: end.end,
301 line: start.line,
302 col: start.col,
303 }
304 }
305
306 fn parse_file(&mut self) -> Result<IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>>, Error> {
311 let mut map: IndexMap<Arc<LemmaRepository>, Vec<LemmaSpec>> = IndexMap::new();
312 let mut current_repo = Arc::new(LemmaRepository::new(None));
313
314 loop {
315 if self.at(&TokenKind::Eof)? {
316 break;
317 }
318
319 if self.at(&TokenKind::Repo)? {
320 let repo_token = self.expect(&TokenKind::Repo)?;
321 let start_line = repo_token.span.line;
322 let (qualifier, _) = self.parse_repository_qualifier()?;
323 crate::limits::check_max_length(
324 &qualifier.name,
325 self.max_spec_name_length,
326 "repository name",
327 Some(Source::new(self.source_type(), repo_token.span)),
328 )?;
329 current_repo = Arc::new(
330 LemmaRepository::new(Some(qualifier.name)).with_start_line(start_line),
331 );
332 map.entry(Arc::clone(¤t_repo)).or_default();
333 continue;
334 }
335
336 if self.at(&TokenKind::Spec)? {
337 let spec = self.parse_spec()?;
338 map.entry(Arc::clone(¤t_repo)).or_default().push(spec);
339 continue;
340 }
341
342 let token = self.next()?;
343 return Err(self.error_at_token_with_suggestion(
344 &token,
345 format!(
346 "Expected a top-level `repo` or `spec` declaration, found {}",
347 token.kind
348 ),
349 "Each Lemma file is a sequence of optional `repo <name>` sections followed by `spec <name>` blocks",
350 ));
351 }
352
353 Ok(map)
354 }
355
356 fn parse_spec(&mut self) -> Result<LemmaSpec, Error> {
357 let spec_token = self.expect(&TokenKind::Spec)?;
358 let start_line = spec_token.span.line;
359
360 let (name, name_span) = self.parse_spec_name()?;
361 crate::limits::check_max_length(
362 &name,
363 self.max_spec_name_length,
364 "spec",
365 Some(Source::new(self.source_type(), name_span)),
366 )?;
367
368 let effective_from = self.try_parse_effective_from()?;
369
370 let commentary = self.try_parse_commentary()?;
371
372 let mut spec = LemmaSpec::new(name.clone())
373 .with_source_type(self.source_type())
374 .with_start_line(start_line);
375 spec.effective_from = crate::parsing::ast::EffectiveDate::from_option(effective_from);
376
377 if let Some(commentary_text) = commentary {
378 spec = spec.set_commentary(commentary_text);
379 }
380
381 let mut data = Vec::new();
385 let mut rules = Vec::new();
386 let mut meta_fields = Vec::new();
387 let mut pending_deprecated_with: Vec<DeprecatedStandaloneWith> = Vec::new();
388
389 loop {
390 let peek_kind = self.peek()?.kind.clone();
391 match peek_kind {
392 TokenKind::Data => {
393 let datum = self.parse_data()?;
394 data.push(datum);
395 }
396 TokenKind::With => {
397 pending_deprecated_with.push(self.parse_deprecated_standalone_with()?);
398 }
399 TokenKind::Rule => {
400 let rule = self.parse_rule()?;
401 rules.push(rule);
402 }
403 TokenKind::Meta => {
404 let meta = self.parse_meta()?;
405 meta_fields.push(meta);
406 }
407 TokenKind::Uses => {
408 let uses_data = self.parse_uses_statement()?;
409 data.push(uses_data);
410 }
411 TokenKind::Spec | TokenKind::Repo | TokenKind::Eof => break,
412 _ => {
413 let token = self.next()?;
414 return Err(self.error_at_token_with_suggestion(
415 &token,
416 format!(
417 "Expected 'data', 'rule', 'meta', 'uses', or a new 'spec', found '{}'",
418 token.text
419 ),
420 "Check the spelling or add the appropriate keyword",
421 ));
422 }
423 }
424 }
425
426 merge_deprecated_standalone_with(&mut data, pending_deprecated_with)?;
427
428 for data in data {
429 spec = spec.add_data(data);
430 }
431 for rule in rules {
432 spec = spec.add_rule(rule);
433 }
434 for meta in meta_fields {
435 spec = spec.add_meta_field(meta);
436 }
437
438 Ok(spec)
439 }
440
441 fn parse_spec_name(&mut self) -> Result<(String, Span), Error> {
442 if self.at(&TokenKind::At)? {
443 let at_tok = self.next()?;
444 return Err(Error::parsing(
445 "'@' is not allowed in spec names; it is valid for repository names (`repo @org/name`) and qualifiers (`uses @org/name`)",
446 self.make_source(at_tok.span),
447 Some(
448 "Write `spec my_spec`, then reference registry specs as `uses alias: @org/repo spec_name` or `data x: alias.TypeName` after importing with `uses`.",
449 ),
450 ));
451 }
452
453 let first = self.next()?;
454 if !can_be_label(&first.kind) {
455 return Err(self.error_at_token(
456 &first,
457 format!("Expected a spec name, found {}", first.kind),
458 ));
459 }
460 let mut name = first.text.clone();
461 let start_span = first.span.clone();
462 let mut end_span = first.span.clone();
463
464 loop {
465 if self.at(&TokenKind::Slash)? {
466 self.next()?;
467 let seg = self.next()?;
468 if !can_be_label(&seg.kind) {
469 return Err(self.error_at_token(
470 &seg,
471 format!(
472 "Expected identifier after '/' in spec name, found {}",
473 seg.kind
474 ),
475 ));
476 }
477 name.push('/');
478 name.push_str(&seg.text);
479 end_span = seg.span.clone();
480 } else if self.at(&TokenKind::Dot)? {
481 self.next()?;
482 let seg = self.next()?;
483 if !can_be_label(&seg.kind) {
484 return Err(self.error_at_token(
485 &seg,
486 format!(
487 "Expected identifier after '.' in spec name, found {}",
488 seg.kind
489 ),
490 ));
491 }
492 name.push('.');
493 name.push_str(&seg.text);
494 end_span = seg.span.clone();
495 } else if self.at(&TokenKind::Minus)? {
496 let minus_span = self.peek()?.span.clone();
497 self.next()?;
498 let peeked = self.peek()?;
499 if !can_be_label(&peeked.kind) {
500 let span = self.span_covering(&start_span, &minus_span);
501 return Err(Error::parsing(
502 "Trailing '-' after spec name",
503 self.make_source(span),
504 None::<String>,
505 ));
506 }
507 let seg = self.next()?;
508 name.push('-');
509 name.push_str(&seg.text);
510 end_span = seg.span.clone();
511 } else {
512 break;
513 }
514 }
515
516 let full_span = self.span_covering(&start_span, &end_span);
517 Ok((name, full_span))
518 }
519
520 fn parse_repository_qualifier(&mut self) -> Result<(RepositoryQualifier, Span), Error> {
528 let has_at = self.at(&TokenKind::At)?;
529 let start_span = if has_at {
530 let at_tok = self.next()?;
531 at_tok.span.clone()
532 } else {
533 Span {
534 start: 0,
535 end: 0,
536 line: 0,
537 col: 0,
538 }
539 };
540
541 let first = self.next()?;
542 if !can_be_repository_qualifier_segment(&first.kind) {
543 return Err(self.error_at_token(
544 &first,
545 format!(
546 "Expected a repository qualifier segment, found {}",
547 first.kind
548 ),
549 ));
550 }
551 if !has_at && is_keyword(&first.kind) {
552 return Err(self.error_at_token(
553 &first,
554 format!(
555 "'{}' is a reserved keyword and cannot be used as a repository name",
556 first.text
557 ),
558 ));
559 }
560 let start_span = if has_at {
561 start_span
562 } else {
563 first.span.clone()
564 };
565 let mut name = first.text.clone();
566
567 loop {
568 let next_kind = self.peek()?.kind.clone();
569 match next_kind {
570 TokenKind::Slash => {
571 self.next()?;
572 name.push('/');
573 let seg = self.next()?;
574 if !can_be_repository_qualifier_segment(&seg.kind) {
575 return Err(self.error_at_token(
576 &seg,
577 format!(
578 "Expected identifier after '/' in repository qualifier segment, found {}",
579 seg.kind
580 ),
581 ));
582 }
583 name.push_str(&seg.text);
584 }
585 TokenKind::Dot => {
586 self.next()?;
587 name.push('.');
588 let seg = self.next()?;
589 if !can_be_repository_qualifier_segment(&seg.kind) {
590 return Err(self.error_at_token(
591 &seg,
592 format!(
593 "Expected identifier after '.' in repository qualifier segment, found {}",
594 seg.kind
595 ),
596 ));
597 }
598 name.push_str(&seg.text);
599 }
600 TokenKind::Minus => {
601 let minus_text_peek = self.lexer.peek_second()?;
602 if !can_be_repository_qualifier_segment(&minus_text_peek.kind) {
603 break;
604 }
605 self.next()?;
606 name.push('-');
607 let seg = self.next()?;
608 name.push_str(&seg.text);
609 }
610 _ => break,
611 }
612 }
613
614 if has_at {
615 name.insert(0, '@');
616 }
617
618 let full_span = self.span_covering(&start_span, &self.last_span);
619 Ok((RepositoryQualifier { name }, full_span))
620 }
621
622 pub fn parse_spec_ref_target(&mut self) -> Result<SpecRef, Error> {
624 let mut repository = None;
625 let mut repository_span = None;
626
627 if self.at(&TokenKind::At)? {
628 let (q, span) = self.parse_repository_qualifier()?;
629 repository = Some(q);
630 repository_span = Some(span);
631 } else {
632 let saved_state = self.lexer.clone();
636 if let Ok((potential_name, span)) = self.parse_spec_name() {
637 if can_be_label(&self.peek()?.kind) {
638 repository = Some(RepositoryQualifier {
639 name: potential_name,
640 });
641 repository_span = Some(span);
642 } else {
643 self.lexer = saved_state;
644 }
645 } else {
646 self.lexer = saved_state;
647 }
648 }
649
650 let (spec_name, spec_name_span) = self.parse_spec_name()?;
651 let effective = self.parse_spec_ref_trailing_effective()?;
652 let target_span = self.span_covering(&spec_name_span, &self.last_span);
653
654 let has_repository = repository.is_some();
655 Ok(SpecRef {
656 name: spec_name,
657 repository,
658 effective,
659 repository_span: if has_repository {
660 repository_span
661 } else {
662 None
663 },
664 target_span: Some(target_span),
665 })
666 }
667
668 fn try_parse_effective_from(&mut self) -> Result<Option<DateTimeValue>, Error> {
669 if !self.at(&TokenKind::NumberLit)? {
674 return Ok(None);
675 }
676
677 let peeked = self.peek()?;
678 let peeked_text = peeked.text.clone();
679 let peeked_span = peeked.span.clone();
680
681 if peeked_text.len() == 4 && peeked_text.chars().all(|c| c.is_ascii_digit()) {
683 let mut dt_str = String::new();
685 let num_tok = self.next()?; dt_str.push_str(&num_tok.text);
687
688 while self.at(&TokenKind::Minus)? {
690 self.next()?; dt_str.push('-');
692 let part = self.next()?;
693 dt_str.push_str(&part.text);
694 }
695
696 if self.at(&TokenKind::Identifier)? {
698 let peeked = self.peek()?;
699 if peeked.text.starts_with('T') || peeked.text.starts_with('t') {
700 let time_part = self.next()?;
701 dt_str.push_str(&time_part.text);
702 while self.at(&TokenKind::Colon)? {
704 self.next()?;
705 dt_str.push(':');
706 let part = self.next()?;
707 dt_str.push_str(&part.text);
708 }
709 if self.at(&TokenKind::Plus)? {
711 self.next()?;
712 dt_str.push('+');
713 let tz_part = self.next()?;
714 dt_str.push_str(&tz_part.text);
715 if self.at(&TokenKind::Colon)? {
716 self.next()?;
717 dt_str.push(':');
718 let tz_min = self.next()?;
719 dt_str.push_str(&tz_min.text);
720 }
721 }
722 }
723 }
724
725 if let Ok(dtv) = dt_str.parse::<DateTimeValue>() {
727 return Ok(Some(dtv));
728 }
729
730 return Err(Error::parsing(
731 format!("Invalid date/time in spec declaration: '{}'", dt_str),
732 self.make_source(peeked_span),
733 None::<String>,
734 ));
735 }
736
737 Ok(None)
738 }
739
740 fn try_parse_commentary(&mut self) -> Result<Option<String>, Error> {
741 if !self.at(&TokenKind::Commentary)? {
742 return Ok(None);
743 }
744 let token = self.next()?;
745 let trimmed = token.text.trim().to_string();
746 if trimmed.is_empty() {
747 Ok(None)
748 } else {
749 Ok(Some(trimmed))
750 }
751 }
752
753 fn parse_data(&mut self) -> Result<LemmaData, Error> {
758 let data_token = self.expect(&TokenKind::Data)?;
759 let start_span = data_token.span.clone();
760
761 let reference = self.parse_reference()?;
762 for segment in reference
763 .segments
764 .iter()
765 .chain(std::iter::once(&reference.name))
766 {
767 crate::limits::check_max_length(
768 segment,
769 self.max_data_name_length,
770 "data",
771 Some(Source::new(self.source_type(), start_span.clone())),
772 )?;
773 }
774
775 self.expect(&TokenKind::Colon)?;
776
777 if !reference.segments.is_empty() {
778 let tok = self.peek()?.clone();
779 return Err(self.error_at_token_with_suggestion(
780 &tok,
781 "Dotted paths require `uses` with `-> with`; `data` declares types and values on local names only.",
782 "Use `uses alias: spec` with ` -> with path.to.slot: <value or reference>`.",
783 ));
784 }
785
786 let value = self.parse_data_value()?;
787
788 let span = self.span_covering(&start_span, &self.last_span);
789 let source = self.make_source(span);
790
791 Ok(LemmaData::new(reference, value, source))
792 }
793
794 fn with_rhs_starts_as_literal(kind: &TokenKind) -> bool {
795 matches!(
796 kind,
797 TokenKind::StringLit | TokenKind::NumberLit | TokenKind::Minus | TokenKind::Plus
798 ) || is_boolean_keyword(kind)
799 }
800
801 fn parse_deprecated_standalone_with(&mut self) -> Result<DeprecatedStandaloneWith, Error> {
802 let with_token = self.expect(&TokenKind::With)?;
803 let start_span = with_token.span.clone();
804 let path = self.parse_reference()?;
805 if path.segments.is_empty() {
806 return Err(self.error_at_token_with_suggestion(
807 &with_token,
808 "Standalone `with` must target an imported spec path (`with alias.field: …`).",
809 "Use `data name: …` for local slots, or nest under `uses` with ` -> with path: …`.",
810 ));
811 }
812 let (rhs, _) =
813 self.parse_assignment_after_key(|parser| parser.parse_with_rhs(), "with", false)?;
814 let span = self.span_covering(&start_span, &self.last_span);
815 Ok(DeprecatedStandaloneWith {
816 path,
817 rhs,
818 source_location: self.make_source(span),
819 })
820 }
821
822 fn parse_with_rhs(&mut self) -> Result<WithRhs, Error> {
823 let peek_kind = self.peek()?.kind.clone();
824
825 if Self::with_rhs_starts_as_literal(&peek_kind) {
826 let value = self.parse_literal_value()?;
827 return Ok(WithRhs::Literal(value));
828 }
829
830 if can_be_label(&peek_kind) {
831 let target = self.parse_reference()?;
832 if self.at(&TokenKind::Arrow)? && self.lexer.peek_second()?.kind != TokenKind::With {
833 let tok = self.peek()?.clone();
834 return Err(self.error_at_token_with_suggestion(
835 &tok,
836 "Constraint chains (`-> ...`) are not allowed on a `uses` binding reference.",
837 "Use `data name: <type> -> ...` for type constraints on local slots.",
838 ));
839 }
840 return Ok(WithRhs::Reference { target });
841 }
842
843 let tok = self.peek()?.clone();
844 Err(self.error_at_token(
845 &tok,
846 format!(
847 "Expected a reference or literal after `-> with ...:`, found {}",
848 tok.kind
849 ),
850 ))
851 }
852
853 fn parse_reference(&mut self) -> Result<Reference, Error> {
854 let mut segments = Vec::new();
855
856 let first = self.next()?;
857 if is_keyword(&first.kind) {
859 return Err(self.error_at_token_with_suggestion(
860 &first,
861 format!(
862 "'{}' is a reserved keyword and cannot be used as a name",
863 first.text
864 ),
865 "Choose a different name that is not a reserved keyword",
866 ));
867 }
868
869 if !can_be_label(&first.kind) {
870 return Err(self.error_at_token(
871 &first,
872 format!("Expected an identifier, found {}", first.kind),
873 ));
874 }
875
876 segments.push(first.text.clone());
877
878 while self.at(&TokenKind::Dot)? {
880 self.next()?; let seg = self.next()?;
882 if !can_be_label(&seg.kind) {
883 return Err(self.error_at_token(
884 &seg,
885 format!("Expected an identifier after '.', found {}", seg.kind),
886 ));
887 }
888 segments.push(seg.text.clone());
889 }
890
891 Ok(Reference::from_path(segments))
892 }
893
894 fn parse_data_value(&mut self) -> Result<DataValue, Error> {
895 if self.at(&TokenKind::Spec)? {
896 let token = self.next()?;
897 return Err(self.error_at_token_with_suggestion(
898 &token,
899 "Cannot import a spec with `data`; use `uses`",
900 "Use `uses <spec_name>` or `uses <alias>: <spec_name>`",
901 ));
902 }
903
904 let peek_kind = self.peek()?.kind.clone();
905
906 if token_kind_to_primitive(&peek_kind).is_some() || can_be_label(&peek_kind) {
907 let (base, constraints) = self.parse_type_arrow_chain()?;
908 return Ok(DataValue::Definition {
909 base: Some(base),
910 constraints,
911 value: None,
912 });
913 }
914
915 let value = self.parse_literal_value()?;
917 Ok(DataValue::Definition {
918 base: None,
919 constraints: None,
920 value: Some(value),
921 })
922 }
923
924 fn parse_uses_item(&mut self, start_span: &Span) -> Result<LemmaData, Error> {
927 let explicit_alias = if can_be_label(&self.peek()?.kind)
928 && self.lexer.peek_second()?.kind == TokenKind::Colon
929 {
930 let alias_tok = self.next()?;
931 self.expect(&TokenKind::Colon)?;
932 Some(alias_tok)
933 } else {
934 None
935 };
936
937 let spec_ref = self.parse_spec_ref_target()?;
938
939 let spec_name_source = spec_ref
940 .target_span
941 .as_ref()
942 .map(|sp| Source::new(self.source_type(), sp.clone()));
943
944 crate::limits::check_max_length(
945 &spec_ref.name,
946 self.max_spec_name_length,
947 "spec",
948 spec_name_source.clone(),
949 )?;
950
951 let alias = if let Some(ref alias_tok) = explicit_alias {
952 crate::limits::check_max_length(
953 &alias_tok.text,
954 self.max_data_name_length,
955 "data",
956 Some(Source::new(self.source_type(), alias_tok.span.clone())),
957 )?;
958 alias_tok.text.clone()
959 } else {
960 let implicit = spec_ref.name.clone();
961 crate::limits::check_max_length(
962 &implicit,
963 self.max_data_name_length,
964 "data",
965 spec_name_source,
966 )?;
967 implicit
968 };
969
970 let mut bindings = Vec::new();
971 while self.at(&TokenKind::Arrow)? {
972 if self.lexer.peek_second()?.kind != TokenKind::With {
973 let tok = self.peek()?.clone();
974 return Err(self.error_at_token_with_suggestion(
975 &tok,
976 "Expected `with` after `->` in a `uses` block.",
977 "Write ` -> with path.to.slot: <value or reference>` under the `uses` line.",
978 ));
979 }
980 self.next()?; let with_token = self.expect(&TokenKind::With)?;
982 let binding_start_span = with_token.span.clone();
983
984 let path = self.parse_reference()?;
985 for segment in path.segments.iter().chain(std::iter::once(&path.name)) {
986 crate::limits::check_max_length(
987 segment,
988 self.max_data_name_length,
989 "uses binding",
990 Some(Source::new(self.source_type(), binding_start_span.clone())),
991 )?;
992 }
993 if path
994 .segments
995 .first()
996 .is_some_and(|segment| segment == &alias)
997 {
998 return Err(self.error_at_token_with_suggestion(
999 &with_token,
1000 format!(
1001 "Binding path must be relative to the imported spec, not prefixed with import alias `{alias}`."
1002 ),
1003 format!(
1004 "Use `-> with {}: …` without the `{alias}.` prefix.",
1005 path.name
1006 ),
1007 ));
1008 }
1009
1010 let (rhs, _) =
1011 self.parse_assignment_after_key(|parser| parser.parse_with_rhs(), "with", false)?;
1012
1013 let binding_span = self.span_covering(&binding_start_span, &self.last_span);
1014 bindings.push(crate::parsing::ast::UsesBinding {
1015 path,
1016 rhs,
1017 source_location: self.make_source(binding_span),
1018 deprecated_standalone_with: false,
1019 });
1020 }
1021
1022 let span = self.span_covering(start_span, &self.last_span);
1023 Ok(LemmaData::new(
1024 Reference::local(alias),
1025 DataValue::Import { spec_ref, bindings },
1026 self.make_source(span),
1027 ))
1028 }
1029
1030 fn parse_uses_statement(&mut self) -> Result<LemmaData, Error> {
1031 let uses_token = self.expect(&TokenKind::Uses)?;
1032 let start_span = uses_token.span.clone();
1033 self.parse_uses_item(&start_span)
1034 }
1035
1036 fn parse_rule(&mut self) -> Result<LemmaRule, Error> {
1041 let rule_token = self.expect(&TokenKind::Rule)?;
1042 let start_span = rule_token.span.clone();
1043
1044 let name_tok = self.next()?;
1045 if is_keyword(&name_tok.kind) {
1046 return Err(self.error_at_token_with_suggestion(
1047 &name_tok,
1048 format!(
1049 "'{}' is a reserved keyword and cannot be used as a rule name",
1050 name_tok.text
1051 ),
1052 "Choose a different name that is not a reserved keyword",
1053 ));
1054 }
1055 if !can_be_label(&name_tok.kind) {
1056 return Err(self.error_at_token(
1057 &name_tok,
1058 format!("Expected a rule name, found {}", name_tok.kind),
1059 ));
1060 }
1061 let rule_name = name_tok.text.clone();
1062 crate::limits::check_max_length(
1063 &rule_name,
1064 self.max_rule_name_length,
1065 "rule",
1066 Some(Source::new(self.source_type(), name_tok.span.clone())),
1067 )?;
1068
1069 self.expect(&TokenKind::Colon)?;
1070
1071 let expression = if self.at(&TokenKind::Veto)? && !self.at_bare_veto_followed_by_is()? {
1073 self.parse_veto_expression()?
1074 } else {
1075 self.parse_expression()?
1076 };
1077
1078 let mut unless_clauses = Vec::new();
1080 while self.at(&TokenKind::Unless)? {
1081 unless_clauses.push(self.parse_unless_clause()?);
1082 }
1083
1084 let end_span = if let Some(last_unless) = unless_clauses.last() {
1085 last_unless.source_location.span.clone()
1086 } else if let Some(ref loc) = expression.source_location {
1087 loc.span.clone()
1088 } else {
1089 start_span.clone()
1090 };
1091
1092 let span = self.span_covering(&start_span, &end_span);
1093 Ok(LemmaRule {
1094 name: rule_name,
1095 expression,
1096 unless_clauses,
1097 source_location: self.make_source(span),
1098 })
1099 }
1100
1101 fn parse_veto_expression(&mut self) -> Result<Expression, Error> {
1102 let veto_tok = self.expect(&TokenKind::Veto)?;
1103 let start_span = veto_tok.span.clone();
1104
1105 let message = if self.at(&TokenKind::StringLit)? {
1106 let str_tok = self.next()?;
1107 Some(str_tok.text)
1108 } else {
1109 None
1110 };
1111
1112 let span = self.span_from(&start_span);
1113 self.new_expression(
1114 ExpressionKind::Veto(VetoExpression { message }),
1115 self.make_source(span),
1116 )
1117 }
1118
1119 fn parse_unless_clause(&mut self) -> Result<UnlessClause, Error> {
1120 let unless_tok = self.expect(&TokenKind::Unless)?;
1121 let start_span = unless_tok.span.clone();
1122
1123 let condition = self.parse_expression()?;
1124
1125 self.expect(&TokenKind::Then)?;
1126
1127 let result = if self.at(&TokenKind::Veto)? {
1128 self.parse_veto_expression()?
1129 } else {
1130 self.parse_expression()?
1131 };
1132
1133 let end_span = result
1134 .source_location
1135 .as_ref()
1136 .map(|s| s.span.clone())
1137 .unwrap_or_else(|| start_span.clone());
1138 let span = self.span_covering(&start_span, &end_span);
1139
1140 Ok(UnlessClause {
1141 condition,
1142 result,
1143 source_location: self.make_source(span),
1144 })
1145 }
1146
1147 fn parse_leaf_parent_type(&mut self) -> Result<ParentType, Error> {
1148 let name_tok = self.next()?;
1149 self.parse_leaf_parent_type_from_first_token(name_tok)
1150 }
1151
1152 fn parse_leaf_parent_type_from_first_token(
1153 &mut self,
1154 name_tok: Token,
1155 ) -> Result<ParentType, Error> {
1156 if let Some(kind) = token_kind_to_primitive(&name_tok.kind) {
1157 Ok(ParentType::Primitive { primitive: kind })
1158 } else if can_be_label(&name_tok.kind) {
1159 Ok(ParentType::Custom {
1160 name: name_tok.text.clone(),
1161 })
1162 } else {
1163 Err(self.error_at_token(
1164 &name_tok,
1165 format!("Expected a type name, found {}", name_tok.kind),
1166 ))
1167 }
1168 }
1169
1170 fn parse_type_arrow_chain(&mut self) -> Result<(ParentType, Option<Vec<Constraint>>), Error> {
1172 let peek_kind = self.peek()?.kind.clone();
1173 let base = if let Some(primitive) = token_kind_to_primitive(&peek_kind) {
1174 let _ = self.next()?;
1175 if self.at(&TokenKind::Dot)? {
1176 let dot_tok = self.peek()?.clone();
1177 return Err(self.error_at_token_with_suggestion(
1178 &dot_tok,
1179 "A primitive type cannot be the left segment of a qualified parent path",
1180 "Use `data name: alias.typename` where `alias` is the `uses` import name and `typename` is the parent type.",
1181 ));
1182 }
1183 ParentType::Primitive { primitive }
1184 } else {
1185 let (name, _) = self.parse_spec_name()?;
1186 if self.at(&TokenKind::Dot)? {
1187 self.next()?;
1189 let inner = self.parse_leaf_parent_type()?;
1190 ParentType::Qualified {
1191 spec_alias: name,
1192 inner: Box::new(inner),
1193 }
1194 } else if let Some((alias, type_name)) = name.rsplit_once('.') {
1195 ParentType::Qualified {
1196 spec_alias: alias.to_string(),
1197 inner: Box::new(ParentType::Custom {
1198 name: type_name.to_string(),
1199 }),
1200 }
1201 } else {
1202 ParentType::Custom { name }
1203 }
1204 };
1205
1206 let base = if self.at(&TokenKind::Identifier)? && self.peek()?.text == "range" {
1207 self.next()?;
1208 ParentType::Ranged {
1209 inner: Box::new(base),
1210 }
1211 } else {
1212 base
1213 };
1214
1215 let constraints = self.parse_trailing_constraints()?;
1216
1217 Ok((base, constraints))
1218 }
1219
1220 fn parse_trailing_constraints(&mut self) -> Result<Option<Vec<Constraint>>, Error> {
1221 let mut commands = Vec::new();
1222 while self.at(&TokenKind::Arrow)? {
1223 let arrow_token = self.next()?;
1224 let constraint_start_span = arrow_token.span.clone();
1225 let (cmd, cmd_args, deprecated_without_colon) = self.parse_constraint_command()?;
1226 let constraint_span = self.span_covering(&constraint_start_span, &self.last_span);
1227 commands.push(Constraint {
1228 command: cmd,
1229 args: cmd_args,
1230 source_location: self.make_source(constraint_span),
1231 deprecated_without_colon,
1232 });
1233 }
1234 let constraints = if commands.is_empty() {
1235 None
1236 } else {
1237 Some(commands)
1238 };
1239 Ok(constraints)
1240 }
1241
1242 fn parse_constraint_command(
1243 &mut self,
1244 ) -> Result<(TypeConstraintCommand, Vec<CommandArg>, bool), Error> {
1245 let name_tok = self.next()?;
1246 if !can_be_label(&name_tok.kind)
1247 && !matches!(name_tok.kind, TokenKind::Lower | TokenKind::Upper)
1248 {
1249 return Err(self.error_at_token(
1250 &name_tok,
1251 format!("Expected a command name, found {}", name_tok.kind),
1252 ));
1253 }
1254 let cmd = try_parse_type_constraint_command(&name_tok.text).ok_or_else(|| {
1255 self.error_at_token(
1256 &name_tok,
1257 format!(
1258 "Unknown constraint command '{}'. Valid commands: help, suggest, fill, unit, trait, minimum, maximum, lower, upper, decimals, option, options, length",
1259 name_tok.text
1260 ),
1261 )
1262 })?;
1263
1264 match cmd.continuation_shape() {
1265 ContinuationShape::Assignment => {
1266 let (args, deprecated_without_colon) = self.parse_unit_as_assignment()?;
1267 Ok((cmd, args, deprecated_without_colon))
1268 }
1269 ContinuationShape::SpaceSeparated => {
1270 Ok((cmd, self.parse_generic_command_args()?, false))
1271 }
1272 }
1273 }
1274
1275 fn parse_assignment_after_key<T>(
1277 &mut self,
1278 parse_value: impl FnOnce(&mut Self) -> Result<T, Error>,
1279 assignment_context: &str,
1280 allow_deprecated_without_colon: bool,
1281 ) -> Result<(T, bool), Error> {
1282 if self.at(&TokenKind::Colon)? {
1283 self.next()?;
1284 let value = parse_value(self)?;
1285 return Ok((value, false));
1286 }
1287 if allow_deprecated_without_colon {
1288 let value = parse_value(self)?;
1289 return Ok((value, true));
1290 }
1291 let tok = self.peek()?.clone();
1292 Err(self.error_at_token_with_suggestion(
1293 &tok,
1294 format!("Expected `:` after `{assignment_context}` key."),
1295 format!("Use `-> {assignment_context} <key>: <value>`."),
1296 ))
1297 }
1298
1299 fn parse_unit_as_assignment(&mut self) -> Result<(Vec<CommandArg>, bool), Error> {
1300 if self.at_command_terminator()? {
1301 return Ok((Vec::new(), false));
1302 }
1303
1304 let peek_kind = self.peek()?.kind.clone();
1305 if !can_be_label(&peek_kind) {
1306 return Ok((Vec::new(), false));
1307 }
1308
1309 let unit_name_tok = self.next()?;
1310 let unit_name_arg = CommandArg::Label(unit_name_tok.text.clone());
1311
1312 let (unit_arg, deprecated_without_colon) =
1313 self.parse_assignment_after_key(|parser| parser.parse_unit_payload(), "unit", true)?;
1314
1315 Ok((
1316 vec![unit_name_arg, CommandArg::UnitExpr(unit_arg)],
1317 deprecated_without_colon,
1318 ))
1319 }
1320
1321 fn parse_unit_payload(&mut self) -> Result<UnitArg, Error> {
1323 if self.at_command_terminator()? {
1324 let tok = self.peek()?.clone();
1325 return Err(self.error_at_token_with_suggestion(
1326 &tok,
1327 "Expected a unit conversion factor or compound unit expression after `:`.",
1328 "Use `-> unit <name>: <factor>` or `-> unit <name>: <compound>` (e.g. `unit eur: 1.00`, `unit eur_per_hour: eur/hour`).",
1329 ));
1330 }
1331
1332 let numeric_prefix: Option<Decimal> = if self.at(&TokenKind::NumberLit)? {
1333 let num_tok = self.next()?;
1334 Some(parse_decimal_string(&num_tok.text, &num_tok.span, self)?)
1335 } else {
1336 None
1337 };
1338
1339 let peek_kind_after_prefix = self.peek()?.kind.clone();
1340 let has_compound_expr =
1341 can_be_label(&peek_kind_after_prefix) && !self.at_command_terminator()?;
1342
1343 if has_compound_expr {
1344 let factors = self.parse_unit_factors()?;
1345 let prefix = numeric_prefix.unwrap_or(Decimal::ONE);
1346 Ok(UnitArg::Expr(prefix, factors))
1347 } else if let Some(factor) = numeric_prefix {
1348 Ok(UnitArg::Factor(factor))
1349 } else {
1350 let tok = self.peek()?.clone();
1351 Err(self.error_at_token_with_suggestion(
1352 &tok,
1353 "Expected a unit conversion factor or compound unit expression after `:`.",
1354 "Use `-> unit <name>: <factor>` or `-> unit <name>: <compound>` (e.g. `unit eur: 1.00`, `unit eur_per_hour: eur/hour`).",
1355 ))
1356 }
1357 }
1358
1359 fn parse_generic_command_args(&mut self) -> Result<Vec<CommandArg>, Error> {
1361 let mut args = Vec::new();
1362 loop {
1363 if self.at(&TokenKind::Arrow)?
1364 || self.at(&TokenKind::Eof)?
1365 || is_spec_body_keyword(&self.peek()?.kind)
1366 || self.at(&TokenKind::Spec)?
1367 {
1368 break;
1369 }
1370
1371 let peek_kind = self.peek()?.kind.clone();
1372 match peek_kind {
1373 TokenKind::NumberLit
1374 | TokenKind::Minus
1375 | TokenKind::Plus
1376 | TokenKind::StringLit => {
1377 let value = self.parse_literal_value()?;
1378 args.push(CommandArg::Literal(value));
1379 }
1380 ref k if is_boolean_keyword(k) => {
1381 let value = self.parse_literal_value()?;
1382 args.push(CommandArg::Literal(value));
1383 }
1384 ref k if can_be_label(k) => {
1385 let tok = self.next()?;
1386 args.push(CommandArg::Label(tok.text));
1387 }
1388 _ => break,
1389 }
1390 }
1391 Ok(args)
1392 }
1393
1394 fn parse_scalar_literal_value(&mut self) -> Result<Value, Error> {
1395 self.parse_scalar_literal_value_with_context("value (number, text, boolean, date, etc.)")
1396 }
1397
1398 fn parse_scalar_literal_value_with_context(&mut self, expected: &str) -> Result<Value, Error> {
1399 let peeked = self.peek()?;
1400 match &peeked.kind {
1401 TokenKind::StringLit => {
1402 let tok = self.next()?;
1403 Ok(Value::Text(tok.text))
1404 }
1405 k if is_boolean_keyword(k) => {
1406 let tok = self.next()?;
1407 Ok(Value::Boolean(token_kind_to_boolean_value(&tok.kind)))
1408 }
1409 TokenKind::NumberLit => self.parse_number_literal(),
1410 TokenKind::Minus | TokenKind::Plus => self.parse_signed_number_literal(),
1411 _ => {
1412 let tok = self.next()?;
1413 Err(self
1414 .error_at_token(&tok, format!("Expected a {expected}, found '{}'", tok.text)))
1415 }
1416 }
1417 }
1418
1419 fn at_command_terminator(&mut self) -> Result<bool, Error> {
1421 if self.at(&TokenKind::Arrow)? || self.at(&TokenKind::Eof)? || self.at(&TokenKind::Spec)? {
1422 return Ok(true);
1423 }
1424 Ok(is_spec_body_keyword(&self.peek()?.kind))
1425 }
1426
1427 fn parse_unit_factors(&mut self) -> Result<Vec<UnitFactor>, Error> {
1444 let mut factors: Vec<UnitFactor> = Vec::new();
1445 let mut denominator_mode = false;
1446 let mut operator_just_consumed = true;
1450
1451 loop {
1452 if self.at_command_terminator()? {
1453 if !operator_just_consumed {
1454 break;
1455 }
1456 break;
1460 }
1461
1462 if self.at(&TokenKind::Star)? {
1464 if operator_just_consumed && !factors.is_empty() {
1465 let bad_tok = self.next()?;
1466 return Err(self.error_at_token(
1467 &bad_tok,
1468 "Unexpected '*' in unit expression: two consecutive operators".to_string(),
1469 ));
1470 }
1471 self.next()?;
1472 denominator_mode = false;
1473 operator_just_consumed = true;
1474 continue;
1475 }
1476
1477 if self.at(&TokenKind::Slash)? {
1479 if operator_just_consumed && !factors.is_empty() {
1480 let bad_tok = self.next()?;
1481 return Err(self.error_at_token(
1482 &bad_tok,
1483 "Unexpected '/' in unit expression: two consecutive operators".to_string(),
1484 ));
1485 }
1486 self.next()?;
1487 denominator_mode = true;
1488 operator_just_consumed = true;
1489 continue;
1490 }
1491
1492 let peek_kind = self.peek()?.kind.clone();
1494 if !can_be_label(&peek_kind) {
1495 break;
1496 }
1497
1498 if !operator_just_consumed {
1501 break;
1502 }
1503 operator_just_consumed = false;
1504
1505 let (measure_ref, _end_span) = self.parse_unit_path()?;
1506
1507 let explicit_exp: Option<i32> = if self.at(&TokenKind::Caret)? {
1509 self.next()?; let negative = if self.at(&TokenKind::Minus)? {
1512 self.next()?; true
1514 } else {
1515 false
1516 };
1517
1518 if !self.at(&TokenKind::NumberLit)? {
1519 let bad_tok = self.next()?;
1520 return Err(self.error_at_token(
1521 &bad_tok,
1522 format!(
1523 "Expected an integer exponent after '^' in unit expression, found {}",
1524 bad_tok.kind
1525 ),
1526 ));
1527 }
1528
1529 let exp_tok = self.next()?;
1530 let raw: i32 = exp_tok.text.parse::<i32>().map_err(|_| {
1531 self.error_at_token(
1532 &exp_tok,
1533 format!(
1534 "Exponent '{}' is not a valid integer in unit expression",
1535 exp_tok.text
1536 ),
1537 )
1538 })?;
1539
1540 if raw == 0 {
1541 return Err(self.error_at_token(
1542 &exp_tok,
1543 "Exponent cannot be zero in a unit expression".to_string(),
1544 ));
1545 }
1546
1547 Some(if negative { -raw } else { raw })
1548 } else {
1549 None
1550 };
1551
1552 let final_exp = match (explicit_exp, denominator_mode) {
1554 (Some(exponent), true) => -exponent,
1555 (Some(exponent), false) => exponent,
1556 (None, true) => -1,
1557 (None, false) => 1,
1558 };
1559
1560 factors.push(UnitFactor {
1561 measure_ref,
1562 exp: final_exp,
1563 });
1564 }
1565
1566 Ok(factors)
1567 }
1568
1569 fn parse_meta(&mut self) -> Result<MetaField, Error> {
1574 let meta_tok = self.expect(&TokenKind::Meta)?;
1575 let start_span = meta_tok.span.clone();
1576
1577 let key_tok = self.next()?;
1578 let key = key_tok.text.clone();
1579
1580 self.expect(&TokenKind::Colon)?;
1581
1582 let value = self.parse_meta_literal_value()?;
1583
1584 let span = self.span_covering(&start_span, &self.last_span);
1585
1586 Ok(MetaField {
1587 key,
1588 value,
1589 source_location: self.make_source(span),
1590 })
1591 }
1592
1593 fn parse_meta_literal_value(&mut self) -> Result<Value, Error> {
1598 if self.at(&TokenKind::Ellipsis)? {
1599 let tok = self.peek()?.clone();
1600 return Err(self.error_at_token(&tok, "Expected a meta literal, found a range"));
1601 }
1602 self.parse_scalar_literal_value_with_context("meta literal")
1603 }
1604
1605 fn parse_literal_value(&mut self) -> Result<Value, Error> {
1606 let left = self.parse_scalar_literal_value()?;
1607 if self.at(&TokenKind::Ellipsis)? {
1608 self.next()?;
1609 let right = self.parse_scalar_literal_value()?;
1610 Ok(Value::Range(Box::new(left), Box::new(right)))
1611 } else {
1612 Ok(left)
1613 }
1614 }
1615
1616 fn parse_signed_number_literal(&mut self) -> Result<Value, Error> {
1617 let sign_tok = self.next()?;
1618 let sign_span = sign_tok.span.clone();
1619 let is_negative = sign_tok.kind == TokenKind::Minus;
1620
1621 if !self.at(&TokenKind::NumberLit)? {
1622 let tok = self.peek()?.clone();
1623 return Err(self.error_at_token(
1624 &tok,
1625 format!(
1626 "Expected a number after '{}', found '{}'",
1627 sign_tok.text, tok.text
1628 ),
1629 ));
1630 }
1631
1632 let value = self.parse_number_literal()?;
1633 if !is_negative {
1634 return Ok(value);
1635 }
1636 match try_negate_numeric_literal(value) {
1637 Ok(negated) => Ok(negated),
1638 Err(other) => Err(Error::parsing(
1639 format!("Cannot negate this value: {}", other),
1640 self.make_source(sign_span),
1641 None::<String>,
1642 )),
1643 }
1644 }
1645
1646 fn parse_number_literal(&mut self) -> Result<Value, Error> {
1647 let num_tok = self.next()?;
1648 let num_text = &num_tok.text;
1649 let num_span = num_tok.span.clone();
1650
1651 if num_text.len() == 4
1653 && num_text.chars().all(|c| c.is_ascii_digit())
1654 && self.at(&TokenKind::Minus)?
1655 {
1656 return self.parse_date_literal(num_text.clone(), num_span);
1657 }
1658
1659 let peeked = self.peek()?;
1661
1662 if num_text.len() == 2
1664 && num_text.chars().all(|c| c.is_ascii_digit())
1665 && peeked.kind == TokenKind::Colon
1666 {
1667 return self.try_parse_time_literal(num_text.clone(), num_span);
1674 }
1675
1676 if peeked.kind == TokenKind::PercentPercent {
1678 let pp_tok = self.next()?;
1679 if let Ok(next_peek) = self.peek() {
1681 if next_peek.kind == TokenKind::NumberLit {
1682 return Err(self.error_at_token(
1683 &pp_tok,
1684 "Permille literal cannot be followed by a digit",
1685 ));
1686 }
1687 }
1688 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1689 return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1690 }
1691
1692 if peeked.kind == TokenKind::Percent {
1694 let pct_tok = self.next()?;
1695 if let Ok(next_peek) = self.peek() {
1697 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1698 return Err(self.error_at_token(
1699 &pct_tok,
1700 "Percent literal cannot be followed by a digit",
1701 ));
1702 }
1703 }
1704 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1705 return Ok(Value::NumberWithUnit(decimal, "percent".to_string()));
1706 }
1707
1708 if peeked.kind == TokenKind::Permille {
1710 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1712 return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1713 }
1714
1715 if can_be_label(&peeked.kind) {
1716 let (unit_path, _end_span) = self.parse_unit_path()?;
1717 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1718 return Ok(Value::NumberWithUnit(decimal, unit_path));
1719 }
1720
1721 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1723 Ok(Value::Number(decimal))
1724 }
1725
1726 fn parse_unit_path(&mut self) -> Result<(String, Span), Error> {
1728 let first = self.next()?;
1729 if !can_be_label(&first.kind) {
1730 return Err(self.error_at_token(
1731 &first,
1732 format!("Expected a unit name, found {}", first.kind),
1733 ));
1734 }
1735 let mut path = first.text.clone();
1736 let mut end_span = first.span.clone();
1737 while self.at(&TokenKind::Dot)? {
1738 self.next()?;
1739 let seg = self.next()?;
1740 if !can_be_label(&seg.kind) {
1741 return Err(self.error_at_token(
1742 &seg,
1743 format!("Expected a unit path segment after '.', found {}", seg.kind),
1744 ));
1745 }
1746 path.push('.');
1747 path.push_str(&seg.text);
1748 end_span = seg.span.clone();
1749 }
1750 Ok((path, end_span))
1751 }
1752
1753 fn parse_date_literal(&mut self, year_text: String, start_span: Span) -> Result<Value, Error> {
1754 let mut dt_str = year_text;
1755
1756 self.expect(&TokenKind::Minus)?;
1758 dt_str.push('-');
1759 let month_tok = self.expect(&TokenKind::NumberLit)?;
1760 dt_str.push_str(&month_tok.text);
1761
1762 self.expect(&TokenKind::Minus)?;
1764 dt_str.push('-');
1765 let day_tok = self.expect(&TokenKind::NumberLit)?;
1766 dt_str.push_str(&day_tok.text);
1767
1768 if self.at(&TokenKind::Identifier)? {
1770 let peeked = self.peek()?;
1771 if peeked.text.len() >= 2
1772 && (peeked.text.starts_with('T') || peeked.text.starts_with('t'))
1773 {
1774 let t_tok = self.next()?;
1776 dt_str.push_str(&t_tok.text);
1777
1778 if self.at(&TokenKind::Colon)? {
1780 self.next()?;
1781 dt_str.push(':');
1782 let min_tok = self.next()?;
1783 dt_str.push_str(&min_tok.text);
1784
1785 if self.at(&TokenKind::Colon)? {
1787 self.next()?;
1788 dt_str.push(':');
1789 let sec_tok = self.next()?;
1790 dt_str.push_str(&sec_tok.text);
1791
1792 if self.at(&TokenKind::Dot)? {
1794 self.next()?;
1795 dt_str.push('.');
1796 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1797 dt_str.push_str(&frac_tok.text);
1798 }
1799 }
1800 }
1801
1802 self.try_consume_timezone(&mut dt_str)?;
1804 }
1805 }
1806
1807 if let Ok(dtv) = dt_str.parse::<crate::literals::DateTimeValue>() {
1808 return Ok(Value::Date(dtv));
1809 }
1810
1811 Err(Error::parsing(
1812 format!("Invalid date/time format: '{}'", dt_str),
1813 self.make_source(start_span),
1814 None::<String>,
1815 ))
1816 }
1817
1818 fn try_consume_timezone(&mut self, dt_str: &mut String) -> Result<(), Error> {
1819 if self.at(&TokenKind::Identifier)? {
1821 let peeked = self.peek()?;
1822 if (peeked.text == "Z" || peeked.text == "z") && peeked.span.start == self.last_span.end
1823 {
1824 let z_tok = self.next()?;
1825 dt_str.push_str(&z_tok.text);
1826 return Ok(());
1827 }
1828 }
1829
1830 if self.at(&TokenKind::Plus)? || self.at(&TokenKind::Minus)? {
1832 let mut lookahead = self.lexer.clone();
1833 let sign_tok = lookahead.next_token()?;
1834 let hour_tok = lookahead.next_token()?;
1835 let colon_tok = lookahead.next_token()?;
1836 let minute_tok = lookahead.next_token()?;
1837
1838 let attached = sign_tok.span.start == self.last_span.end;
1839 let is_timezone_shape = hour_tok.kind == TokenKind::NumberLit
1840 && colon_tok.kind == TokenKind::Colon
1841 && minute_tok.kind == TokenKind::NumberLit;
1842
1843 if attached && is_timezone_shape {
1844 let sign_tok = self.next()?;
1845 dt_str.push_str(&sign_tok.text);
1846 let hour_tok = self.expect(&TokenKind::NumberLit)?;
1847 dt_str.push_str(&hour_tok.text);
1848 self.expect(&TokenKind::Colon)?;
1849 dt_str.push(':');
1850 let min_tok = self.expect(&TokenKind::NumberLit)?;
1851 dt_str.push_str(&min_tok.text);
1852 }
1853 }
1854
1855 Ok(())
1856 }
1857
1858 fn try_parse_time_literal(
1859 &mut self,
1860 hour_text: String,
1861 start_span: Span,
1862 ) -> Result<Value, Error> {
1863 let mut time_str = hour_text;
1864
1865 self.expect(&TokenKind::Colon)?;
1867 time_str.push(':');
1868 let min_tok = self.expect(&TokenKind::NumberLit)?;
1869 time_str.push_str(&min_tok.text);
1870
1871 if self.at(&TokenKind::Colon)? {
1873 self.next()?;
1874 time_str.push(':');
1875 let sec_tok = self.expect(&TokenKind::NumberLit)?;
1876 time_str.push_str(&sec_tok.text);
1877
1878 if self.at(&TokenKind::Dot)? {
1880 self.next()?;
1881 time_str.push('.');
1882 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1883 time_str.push_str(&frac_tok.text);
1884 }
1885 }
1886
1887 self.try_consume_timezone(&mut time_str)?;
1889
1890 if let Ok(t) = time_str.parse::<TimeValue>() {
1891 return Ok(Value::Time(TimeValue {
1892 hour: t.hour,
1893 minute: t.minute,
1894 second: t.second,
1895 microsecond: t.microsecond,
1896 timezone: t.timezone,
1897 }));
1898 }
1899
1900 Err(Error::parsing(
1901 format!("Invalid time format: '{}'", time_str),
1902 self.make_source(start_span),
1903 None::<String>,
1904 ))
1905 }
1906
1907 fn new_expression(
1912 &mut self,
1913 kind: ExpressionKind,
1914 source: Source,
1915 ) -> Result<Expression, Error> {
1916 self.expression_count += 1;
1917 if self.expression_count > self.max_expression_count {
1918 return Err(Error::resource_limit_exceeded(
1919 "max_expression_count",
1920 self.max_expression_count.to_string(),
1921 self.expression_count.to_string(),
1922 "Split logic into multiple rules to reduce expression count",
1923 Some(source),
1924 None,
1925 None,
1926 ));
1927 }
1928 Ok(Expression::new(kind, source))
1929 }
1930
1931 fn check_depth(&mut self) -> Result<(), Error> {
1932 if let Err(actual) = self.depth_tracker.push_depth() {
1933 let span = self.peek()?.span.clone();
1934 self.depth_tracker.pop_depth();
1935 return Err(Error::resource_limit_exceeded(
1936 "max_expression_depth",
1937 self.depth_tracker.max_depth().to_string(),
1938 actual.to_string(),
1939 "Simplify nested expressions or break into separate rules",
1940 Some(self.make_source(span)),
1941 None,
1942 None,
1943 ));
1944 }
1945 Ok(())
1946 }
1947
1948 fn parse_expression(&mut self) -> Result<Expression, Error> {
1949 self.check_depth()?;
1950 let result = self.parse_and_expression();
1951 self.depth_tracker.pop_depth();
1952 result
1953 }
1954
1955 fn parse_and_expression(&mut self) -> Result<Expression, Error> {
1956 let start_span = self.peek()?.span.clone();
1957 let mut left = self.parse_and_operand()?;
1958
1959 while self.at(&TokenKind::And)? {
1960 self.next()?; let right = self.parse_and_operand()?;
1962 let span = self.span_covering(
1963 &start_span,
1964 &right
1965 .source_location
1966 .as_ref()
1967 .map(|s| s.span.clone())
1968 .unwrap_or_else(|| start_span.clone()),
1969 );
1970 left = self.new_expression(
1971 ExpressionKind::LogicalAnd(Arc::new(left), Arc::new(right)),
1972 self.make_source(span),
1973 )?;
1974 }
1975
1976 Ok(left)
1977 }
1978
1979 fn at_bare_veto_token(&mut self) -> Result<bool, Error> {
1980 if !self.at(&TokenKind::Veto)? {
1981 return Ok(false);
1982 }
1983 let checkpoint = self.checkpoint();
1984 self.next()?;
1985 let bare = !self.at(&TokenKind::StringLit)?;
1986 self.restore(checkpoint);
1987 Ok(bare)
1988 }
1989
1990 fn at_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
1991 if !self.at_bare_veto_token()? {
1992 return Ok(false);
1993 }
1994 let checkpoint = self.checkpoint();
1995 self.next()?;
1996 let followed = self.at(&TokenKind::Is)?;
1997 self.restore(checkpoint);
1998 Ok(followed)
1999 }
2000
2001 fn at_not_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
2002 if !self.at(&TokenKind::Not)? {
2003 return Ok(false);
2004 }
2005 let checkpoint = self.checkpoint();
2006 self.next()?;
2007 if !self.at(&TokenKind::Veto)? {
2008 self.restore(checkpoint);
2009 return Ok(false);
2010 }
2011 self.next()?;
2012 if self.at(&TokenKind::StringLit)? {
2013 self.restore(checkpoint);
2014 return Ok(false);
2015 }
2016 let followed = self.at(&TokenKind::Is)?;
2017 self.restore(checkpoint);
2018 Ok(followed)
2019 }
2020
2021 fn wrap_result_is_veto_expression(
2022 &mut self,
2023 operand: Expression,
2024 operator_is_not: bool,
2025 keyword_was_negated: bool,
2026 start_span: Span,
2027 ) -> Result<Expression, Error> {
2028 let negate = operator_is_not ^ keyword_was_negated;
2029 let end_span = operand
2030 .source_location
2031 .as_ref()
2032 .map(|source| source.span.clone())
2033 .unwrap_or_else(|| start_span.clone());
2034 let span = self.span_covering(&start_span, &end_span);
2035 let core = self.new_expression(
2036 ExpressionKind::ResultIsVeto(Arc::new(operand)),
2037 self.make_source(span.clone()),
2038 )?;
2039 if negate {
2040 self.new_expression(
2041 ExpressionKind::LogicalNegation(Arc::new(core), NegationType::Not),
2042 self.make_source(span),
2043 )
2044 } else {
2045 Ok(core)
2046 }
2047 }
2048
2049 fn parse_veto_status_lhs_is_comparison(&mut self) -> Result<Expression, Error> {
2050 let start_span = self.peek()?.span.clone();
2051 let keyword_was_negated = if self.at(&TokenKind::Not)? {
2052 self.next()?;
2053 true
2054 } else {
2055 false
2056 };
2057 self.expect(&TokenKind::Veto)?;
2058 if self.at(&TokenKind::StringLit)? {
2059 let tok = self.peek()?.clone();
2060 return Err(self.error_at_token(
2061 &tok,
2062 "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
2063 ));
2064 }
2065 let operator = self.parse_comparison_operator()?;
2066 let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
2067 if !matches!(
2068 operator,
2069 ComparisonComputation::Is | ComparisonComputation::IsNot
2070 ) {
2071 let tok = self.peek()?.clone();
2072 return Err(self.error_at_token(
2073 &tok,
2074 "Expected `is` or `is not` after `veto` in a veto-status comparison",
2075 ));
2076 }
2077 let operand = self.parse_range_expression()?;
2078 self.wrap_result_is_veto_expression(
2079 operand,
2080 operator_is_not,
2081 keyword_was_negated,
2082 start_span,
2083 )
2084 }
2085
2086 fn parse_and_operand(&mut self) -> Result<Expression, Error> {
2087 if self.at_not_bare_veto_followed_by_is()? || self.at_bare_veto_followed_by_is()? {
2088 return self.parse_veto_status_lhs_is_comparison();
2089 }
2090
2091 if self.at(&TokenKind::Not)? {
2093 return self.parse_not_expression();
2094 }
2095
2096 self.parse_repository_with_suffix()
2098 }
2099
2100 fn parse_not_expression(&mut self) -> Result<Expression, Error> {
2101 let not_tok = self.expect(&TokenKind::Not)?;
2102 let start_span = not_tok.span.clone();
2103
2104 self.check_depth()?;
2105 let operand = self.parse_and_operand()?;
2106 self.depth_tracker.pop_depth();
2107
2108 let end_span = operand
2109 .source_location
2110 .as_ref()
2111 .map(|s| s.span.clone())
2112 .unwrap_or_else(|| start_span.clone());
2113 let span = self.span_covering(&start_span, &end_span);
2114
2115 self.new_expression(
2116 ExpressionKind::LogicalNegation(Arc::new(operand), NegationType::Not),
2117 self.make_source(span),
2118 )
2119 }
2120
2121 fn parse_repository_with_suffix(&mut self) -> Result<Expression, Error> {
2122 let start_span = self.peek()?.span.clone();
2123 let repository = self.parse_range_expression()?;
2124 self.continue_repository_operand(repository, start_span)
2125 }
2126
2127 fn continue_repository_operand(
2129 &mut self,
2130 mut expr: Expression,
2131 start_span: Span,
2132 ) -> Result<Expression, Error> {
2133 loop {
2134 let peeked = self.peek()?;
2135
2136 if is_comparison_operator(&peeked.kind) {
2137 return self.parse_comparison_suffix(expr, start_span);
2138 }
2139
2140 if peeked.kind == TokenKind::Not {
2141 expr = self.parse_not_in_calendar_suffix(expr, start_span.clone())?;
2142 continue;
2143 }
2144
2145 if peeked.kind == TokenKind::In {
2146 expr = self.parse_in_suffix(expr, start_span.clone())?;
2147 continue;
2148 }
2149
2150 if peeked.kind == TokenKind::As {
2151 expr = self.parse_as_chain(expr, start_span.clone())?;
2152 continue;
2153 }
2154
2155 break;
2156 }
2157
2158 if self.at_expression_suffix_end()? {
2159 return Ok(expr);
2160 }
2161
2162 let tok = self.peek()?.clone();
2163 Err(self.error_at_token(
2164 &tok,
2165 format!("Unexpected token '{}' after expression", tok.text),
2166 ))
2167 }
2168
2169 fn parse_comparison_suffix(
2170 &mut self,
2171 left: Expression,
2172 start_span: Span,
2173 ) -> Result<Expression, Error> {
2174 let operator = self.parse_comparison_operator()?;
2175 let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
2176
2177 if matches!(
2178 operator,
2179 ComparisonComputation::Is | ComparisonComputation::IsNot
2180 ) && self.at_bare_veto_token()?
2181 {
2182 self.expect(&TokenKind::Veto)?;
2183 if self.at(&TokenKind::StringLit)? {
2184 let tok = self.peek()?.clone();
2185 return Err(self.error_at_token(
2186 &tok,
2187 "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
2188 ));
2189 }
2190 return self.wrap_result_is_veto_expression(left, operator_is_not, false, start_span);
2191 }
2192
2193 let right = if self.at(&TokenKind::Not)? {
2195 self.parse_not_expression()?
2196 } else {
2197 self.parse_range_expression()?
2198 };
2199
2200 let end_span = right
2201 .source_location
2202 .as_ref()
2203 .map(|s| s.span.clone())
2204 .unwrap_or_else(|| start_span.clone());
2205 let span = self.span_covering(&start_span, &end_span);
2206
2207 self.new_expression(
2208 ExpressionKind::Comparison(Arc::new(left), operator, Arc::new(right)),
2209 self.make_source(span),
2210 )
2211 }
2212
2213 fn parse_comparison_operator(&mut self) -> Result<ComparisonComputation, Error> {
2214 let tok = self.next()?;
2215 match tok.kind {
2216 TokenKind::Gt => Ok(ComparisonComputation::GreaterThan),
2217 TokenKind::Lt => Ok(ComparisonComputation::LessThan),
2218 TokenKind::Gte => Ok(ComparisonComputation::GreaterThanOrEqual),
2219 TokenKind::Lte => Ok(ComparisonComputation::LessThanOrEqual),
2220 TokenKind::Is => {
2221 if self.at(&TokenKind::Not)? {
2223 self.next()?; Ok(ComparisonComputation::IsNot)
2225 } else {
2226 Ok(ComparisonComputation::Is)
2227 }
2228 }
2229 _ => Err(self.error_at_token(
2230 &tok,
2231 format!("Expected a comparison operator, found {}", tok.kind),
2232 )),
2233 }
2234 }
2235
2236 fn parse_not_in_calendar_suffix(
2237 &mut self,
2238 repository: Expression,
2239 start_span: Span,
2240 ) -> Result<Expression, Error> {
2241 self.expect(&TokenKind::Not)?;
2242 self.expect(&TokenKind::In)?;
2243 self.expect_calendar_period_marker()?;
2244 let unit = self.parse_calendar_unit()?;
2245 let end = self.peek()?.span.clone();
2246 let span = self.span_covering(&start_span, &end);
2247 self.new_expression(
2248 ExpressionKind::DateCalendar(DateCalendarKind::NotIn, unit, Arc::new(repository)),
2249 self.make_source(span),
2250 )
2251 }
2252
2253 fn parse_in_suffix(
2254 &mut self,
2255 repository: Expression,
2256 start_span: Span,
2257 ) -> Result<Expression, Error> {
2258 self.expect(&TokenKind::In)?;
2259
2260 let peeked = self.peek()?;
2261
2262 if peeked.kind == TokenKind::Past || peeked.kind == TokenKind::Future {
2264 let direction = self.next()?;
2265 let rel_kind = if direction.kind == TokenKind::Past {
2266 DateRelativeKind::InPast
2267 } else {
2268 DateRelativeKind::InFuture
2269 };
2270
2271 if self.at_calendar_period_marker()? {
2273 self.next_calendar_period_marker()?;
2274 let cal_kind = if direction.kind == TokenKind::Past {
2275 DateCalendarKind::Past
2276 } else {
2277 DateCalendarKind::Future
2278 };
2279 let unit = self.parse_calendar_unit()?;
2280 let end = self.peek()?.span.clone();
2281 let span = self.span_covering(&start_span, &end);
2282 return self.new_expression(
2283 ExpressionKind::DateCalendar(cal_kind, unit, Arc::new(repository)),
2284 self.make_source(span),
2285 );
2286 }
2287
2288 if self.at(&TokenKind::And)?
2289 || self.at(&TokenKind::Unless)?
2290 || self.at(&TokenKind::Then)?
2291 || self.at(&TokenKind::RParen)?
2292 || self.at(&TokenKind::Eof)?
2293 || is_comparison_operator(&self.peek()?.kind)
2294 {
2295 let end = self.peek()?.span.clone();
2296 let span = self.span_covering(&start_span, &end);
2297 return self.new_expression(
2298 ExpressionKind::DateRelative(rel_kind, Arc::new(repository)),
2299 self.make_source(span),
2300 );
2301 }
2302
2303 let offset = self.parse_repository_expression()?;
2304 let offset_end_span = offset
2305 .source_location
2306 .as_ref()
2307 .map(|s| s.span.clone())
2308 .unwrap_or_else(|| start_span.clone());
2309 let range = self.new_expression(
2310 ExpressionKind::PastFutureRange(rel_kind, Arc::new(offset)),
2311 self.make_source(self.span_covering(&direction.span, &offset_end_span)),
2312 )?;
2313 let span = self.span_covering(&start_span, &offset_end_span);
2314 return self.new_expression(
2315 ExpressionKind::RangeContainment(Arc::new(repository), Arc::new(range)),
2316 self.make_source(span),
2317 );
2318 }
2319
2320 if token_is_calendar_period_marker(peeked) {
2322 self.next_calendar_period_marker()?;
2323 let unit = self.parse_calendar_unit()?;
2324 let end = self.peek()?.span.clone();
2325 let span = self.span_covering(&start_span, &end);
2326 return self.new_expression(
2327 ExpressionKind::DateCalendar(DateCalendarKind::Current, unit, Arc::new(repository)),
2328 self.make_source(span),
2329 );
2330 }
2331
2332 let range = self.parse_range_expression()?;
2333 let end_span = range
2334 .source_location
2335 .as_ref()
2336 .map(|s| s.span.clone())
2337 .unwrap_or_else(|| start_span.clone());
2338 let span = self.span_covering(&start_span, &end_span);
2339 self.new_expression(
2340 ExpressionKind::RangeContainment(Arc::new(repository), Arc::new(range)),
2341 self.make_source(span),
2342 )
2343 }
2344
2345 fn parse_as_chain(
2346 &mut self,
2347 mut expr: Expression,
2348 start_span: Span,
2349 ) -> Result<Expression, Error> {
2350 while self.at(&TokenKind::As)? {
2351 self.expect(&TokenKind::As)?;
2352 let target_tok = self.next()?;
2353 let target = if matches!(target_tok.kind, TokenKind::Permille) {
2354 ConversionTarget::Unit {
2355 unit_name: "permille".to_string(),
2356 }
2357 } else if let Some(primitive) = token_kind_to_primitive(&target_tok.kind) {
2358 ConversionTarget::Type(primitive)
2359 } else if can_be_label(&target_tok.kind) {
2360 let mut unit_path = target_tok.text.clone();
2361 let mut end_span = target_tok.span.clone();
2362 while self.at(&TokenKind::Dot)? {
2363 self.next()?;
2364 let seg = self.next()?;
2365 if !can_be_label(&seg.kind) {
2366 return Err(self.error_at_token(
2367 &seg,
2368 format!("Expected a unit path segment after '.', found {}", seg.kind),
2369 ));
2370 }
2371 unit_path.push('.');
2372 unit_path.push_str(&seg.text);
2373 end_span = seg.span.clone();
2374 }
2375 let target = ConversionTarget::Unit {
2376 unit_name: unit_path,
2377 };
2378 expr = self.new_expression(
2379 ExpressionKind::UnitConversion(Arc::new(expr), target),
2380 self.make_source(self.span_covering(&start_span, &end_span)),
2381 )?;
2382 continue;
2383 } else {
2384 return Err(self.error_at_token(
2385 &target_tok,
2386 format!(
2387 "Expected a type keyword or unit name after 'as', found {}",
2388 target_tok.kind
2389 ),
2390 ));
2391 };
2392 expr = self.new_expression(
2393 ExpressionKind::UnitConversion(Arc::new(expr), target),
2394 self.make_source(self.span_covering(&start_span, &target_tok.span)),
2395 )?;
2396 }
2397 Ok(expr)
2398 }
2399
2400 fn is_plain_number_literal(expr: &Expression) -> bool {
2401 matches!(expr.kind, ExpressionKind::Literal(Value::Number(_)))
2402 }
2403
2404 fn is_unit_conversion(expr: &Expression) -> bool {
2405 matches!(expr.kind, ExpressionKind::UnitConversion(..))
2406 }
2407
2408 fn at_expression_suffix_end(&mut self) -> Result<bool, Error> {
2413 Ok(self.at(&TokenKind::And)?
2414 || self.at(&TokenKind::Unless)?
2415 || self.at(&TokenKind::Then)?
2416 || self.at(&TokenKind::RParen)?
2417 || self.at(&TokenKind::Eof)?
2418 || self.at(&TokenKind::Spec)?
2419 || self.at(&TokenKind::Repo)?
2420 || self.at(&TokenKind::Uses)?
2421 || is_spec_body_keyword(&self.peek()?.kind))
2422 }
2423
2424 fn parse_calendar_unit(&mut self) -> Result<CalendarPeriodUnit, Error> {
2425 let tok = self.next()?;
2426 if let Some(unit) = CalendarPeriodUnit::from_keyword(&tok.text) {
2427 return Ok(unit);
2428 }
2429 Err(self.error_at_token(
2430 &tok,
2431 format!("Expected 'year', 'month', or 'week', found '{}'", tok.text),
2432 ))
2433 }
2434
2435 fn parse_range_expression(&mut self) -> Result<Expression, Error> {
2440 self.parse_repository_expression()
2441 }
2442
2443 fn parse_range_operand(&mut self) -> Result<Expression, Error> {
2448 let start_span = self.peek()?.span.clone();
2449 let checkpoint = self.checkpoint();
2450 let left = self.parse_range_ellipsis_bound()?;
2451 if !self.at(&TokenKind::Ellipsis)? {
2452 self.restore(checkpoint);
2453 return self.parse_factor();
2454 }
2455
2456 self.next()?;
2457 let right = self.parse_range_ellipsis_bound()?;
2458 let end_span = right
2459 .source_location
2460 .as_ref()
2461 .map(|s| s.span.clone())
2462 .unwrap_or_else(|| start_span.clone());
2463 let span = self.span_covering(&start_span, &end_span);
2464 self.new_expression(
2465 ExpressionKind::RangeLiteral(Arc::new(left), Arc::new(right)),
2466 self.make_source(span),
2467 )
2468 }
2469
2470 fn parse_range_ellipsis_bound(&mut self) -> Result<Expression, Error> {
2472 let start_span = self.peek()?.span.clone();
2473 let mut left = self.parse_power_for_range_bound()?;
2474
2475 while self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
2476 let op_tok = self.next()?;
2477 let operation = match op_tok.kind {
2478 TokenKind::Plus => ArithmeticComputation::Add,
2479 TokenKind::Minus => ArithmeticComputation::Subtract,
2480 _ => unreachable!("BUG: only + and - should reach here"),
2481 };
2482
2483 let right = self.parse_power_for_range_bound()?;
2484 let end_span = right
2485 .source_location
2486 .as_ref()
2487 .map(|s| s.span.clone())
2488 .unwrap_or_else(|| start_span.clone());
2489 let span = self.span_covering(&start_span, &end_span);
2490
2491 left = self.new_expression(
2492 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
2493 self.make_source(span),
2494 )?;
2495 }
2496
2497 Ok(left)
2498 }
2499
2500 fn parse_power_for_range_bound(&mut self) -> Result<Expression, Error> {
2501 let start_span = self.peek()?.span.clone();
2502 let left = self.parse_factor()?;
2503
2504 if self.at(&TokenKind::Caret)? {
2505 self.next()?;
2506 self.check_depth()?;
2507 let right = self.parse_power_for_range_bound()?;
2508 self.depth_tracker.pop_depth();
2509 let end_span = right
2510 .source_location
2511 .as_ref()
2512 .map(|s| s.span.clone())
2513 .unwrap_or_else(|| start_span.clone());
2514 let span = self.span_covering(&start_span, &end_span);
2515
2516 return self.new_expression(
2517 ExpressionKind::Arithmetic(
2518 Arc::new(left),
2519 ArithmeticComputation::Power,
2520 Arc::new(right),
2521 ),
2522 self.make_source(span),
2523 );
2524 }
2525
2526 Ok(left)
2527 }
2528
2529 fn parse_repository_expression(&mut self) -> Result<Expression, Error> {
2530 let start_span = self.peek()?.span.clone();
2531 let mut left = self.parse_term()?;
2532
2533 while self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
2534 let op_tok = self.next()?;
2537 let operation = match op_tok.kind {
2538 TokenKind::Plus => ArithmeticComputation::Add,
2539 TokenKind::Minus => ArithmeticComputation::Subtract,
2540 _ => unreachable!("BUG: only + and - should reach here"),
2541 };
2542
2543 let right = self.parse_term()?;
2544 if Self::is_plain_number_literal(&left) && Self::is_unit_conversion(&right) {
2545 let source = right
2546 .source_location
2547 .clone()
2548 .unwrap_or_else(|| self.make_source(start_span.clone()));
2549 return Err(Error::parsing(
2550 "Cannot add a plain number to a converted value; convert each operand before \
2551 '+' (e.g. '5 as usd + c as usd')",
2552 source,
2553 None::<String>,
2554 ));
2555 }
2556
2557 let end_span = right
2558 .source_location
2559 .as_ref()
2560 .map(|s| s.span.clone())
2561 .unwrap_or_else(|| start_span.clone());
2562 let span = self.span_covering(&start_span, &end_span);
2563
2564 left = self.new_expression(
2565 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
2566 self.make_source(span),
2567 )?;
2568 }
2569
2570 Ok(left)
2571 }
2572
2573 fn parse_term(&mut self) -> Result<Expression, Error> {
2574 self.parse_term_with_as(true)
2575 }
2576
2577 fn parse_term_with_as(&mut self, allow_as: bool) -> Result<Expression, Error> {
2578 let start_span = self.peek()?.span.clone();
2579 let mut left = self.parse_power()?;
2580 if allow_as {
2581 left = self.parse_as_chain(left, start_span.clone())?;
2582 }
2583
2584 while self.at_any(&[TokenKind::Star, TokenKind::Slash, TokenKind::Percent])? {
2585 let op_tok = self.next()?;
2588 let operation = match op_tok.kind {
2589 TokenKind::Star => ArithmeticComputation::Multiply,
2590 TokenKind::Slash => ArithmeticComputation::Divide,
2591 TokenKind::Percent => ArithmeticComputation::Modulo,
2592 _ => unreachable!("BUG: only *, /, % should reach here"),
2593 };
2594
2595 let right_start_span = self.peek()?.span.clone();
2596 let mut right = self.parse_power()?;
2597 if allow_as {
2598 right = self.parse_as_chain(right, right_start_span)?;
2599 }
2600 let end_span = right
2601 .source_location
2602 .as_ref()
2603 .map(|s| s.span.clone())
2604 .unwrap_or_else(|| start_span.clone());
2605 let span = self.span_covering(&start_span, &end_span);
2606
2607 left = self.new_expression(
2608 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
2609 self.make_source(span),
2610 )?;
2611 }
2612
2613 Ok(left)
2614 }
2615
2616 fn parse_power(&mut self) -> Result<Expression, Error> {
2617 let start_span = self.peek()?.span.clone();
2618 let left = self.parse_range_operand()?;
2619
2620 if self.at(&TokenKind::Caret)? {
2621 self.next()?;
2622 self.check_depth()?;
2623 let right = self.parse_power()?;
2624 self.depth_tracker.pop_depth();
2625 let end_span = right
2626 .source_location
2627 .as_ref()
2628 .map(|s| s.span.clone())
2629 .unwrap_or_else(|| start_span.clone());
2630 let span = self.span_covering(&start_span, &end_span);
2631
2632 return self.new_expression(
2633 ExpressionKind::Arithmetic(
2634 Arc::new(left),
2635 ArithmeticComputation::Power,
2636 Arc::new(right),
2637 ),
2638 self.make_source(span),
2639 );
2640 }
2641
2642 Ok(left)
2643 }
2644
2645 fn parse_factor(&mut self) -> Result<Expression, Error> {
2646 let peeked = self.peek()?;
2647 let start_span = peeked.span.clone();
2648
2649 if peeked.kind == TokenKind::Minus {
2650 self.next()?;
2651 let operand = self.parse_primary_or_math()?;
2652 let end_span = operand
2653 .source_location
2654 .as_ref()
2655 .map(|s| s.span.clone())
2656 .unwrap_or_else(|| start_span.clone());
2657 let span = self.span_covering(&start_span, &end_span);
2658
2659 if let ExpressionKind::Literal(value) = &operand.kind {
2660 if let Ok(negated) = try_negate_numeric_literal(value.clone()) {
2661 return self
2662 .new_expression(ExpressionKind::Literal(negated), self.make_source(span));
2663 }
2664 }
2665
2666 let zero = self.new_expression(
2667 ExpressionKind::Literal(Value::Number(Decimal::ZERO)),
2668 self.make_source(start_span),
2669 )?;
2670 return self.new_expression(
2671 ExpressionKind::Arithmetic(
2672 Arc::new(zero),
2673 ArithmeticComputation::Subtract,
2674 Arc::new(operand),
2675 ),
2676 self.make_source(span),
2677 );
2678 }
2679
2680 if peeked.kind == TokenKind::Plus {
2681 self.next()?;
2682 return self.parse_primary_or_math();
2683 }
2684
2685 self.parse_primary_or_math()
2686 }
2687
2688 fn parse_primary_or_math(&mut self) -> Result<Expression, Error> {
2689 let peeked = self.peek()?;
2690
2691 if is_math_function(&peeked.kind) {
2692 return self.parse_math_function();
2693 }
2694 if matches!(peeked.kind, TokenKind::Lower | TokenKind::Upper) {
2695 return self.parse_range_bound();
2696 }
2697
2698 self.parse_primary()
2699 }
2700
2701 fn parse_math_function(&mut self) -> Result<Expression, Error> {
2702 let func_tok = self.next()?;
2703 let start_span = func_tok.span.clone();
2704
2705 let operator = match func_tok.kind {
2706 TokenKind::Sqrt => MathematicalComputation::Sqrt,
2707 TokenKind::Sin => MathematicalComputation::Sin,
2708 TokenKind::Cos => MathematicalComputation::Cos,
2709 TokenKind::Tan => MathematicalComputation::Tan,
2710 TokenKind::Asin => MathematicalComputation::Asin,
2711 TokenKind::Acos => MathematicalComputation::Acos,
2712 TokenKind::Atan => MathematicalComputation::Atan,
2713 TokenKind::Log => MathematicalComputation::Log,
2714 TokenKind::Exp => MathematicalComputation::Exp,
2715 TokenKind::Abs => MathematicalComputation::Abs,
2716 TokenKind::Floor => MathematicalComputation::Floor,
2717 TokenKind::Ceil => MathematicalComputation::Ceil,
2718 TokenKind::Round => MathematicalComputation::Round,
2719 _ => unreachable!("BUG: only math functions should reach here"),
2720 };
2721
2722 self.check_depth()?;
2723 let operand = self.parse_range_operand()?;
2724 self.depth_tracker.pop_depth();
2725
2726 let end_span = operand
2727 .source_location
2728 .as_ref()
2729 .map(|s| s.span.clone())
2730 .unwrap_or_else(|| start_span.clone());
2731 let span = self.span_covering(&start_span, &end_span);
2732
2733 self.new_expression(
2734 ExpressionKind::MathematicalComputation(operator, Arc::new(operand)),
2735 self.make_source(span),
2736 )
2737 }
2738
2739 fn parse_range_bound(&mut self) -> Result<Expression, Error> {
2740 let bound_tok = self.next()?;
2741 let start_span = bound_tok.span.clone();
2742 let bound = match bound_tok.kind {
2743 TokenKind::Lower => RangeBound::Lower,
2744 TokenKind::Upper => RangeBound::Upper,
2745 _ => unreachable!("BUG: only lower and upper should reach parse_range_bound"),
2746 };
2747
2748 self.check_depth()?;
2749 let operand = self.parse_range_operand()?;
2750 self.depth_tracker.pop_depth();
2751
2752 let end_span = operand
2753 .source_location
2754 .as_ref()
2755 .map(|s| s.span.clone())
2756 .unwrap_or_else(|| start_span.clone());
2757 let span = self.span_covering(&start_span, &end_span);
2758
2759 self.new_expression(
2760 ExpressionKind::RangeBound(bound, Arc::new(operand)),
2761 self.make_source(span),
2762 )
2763 }
2764
2765 fn parse_primary(&mut self) -> Result<Expression, Error> {
2766 let peeked = self.peek()?;
2767 let start_span = peeked.span.clone();
2768
2769 match &peeked.kind {
2770 TokenKind::LParen => {
2772 self.next()?; let inner = self.parse_expression()?;
2774 self.expect(&TokenKind::RParen)?;
2775 Ok(inner)
2776 }
2777
2778 TokenKind::Now => {
2780 let tok = self.next()?;
2781 self.new_expression(ExpressionKind::Now, self.make_source(tok.span))
2782 }
2783
2784 TokenKind::Past | TokenKind::Future => {
2785 let tok = self.next()?;
2786 let kind = if tok.kind == TokenKind::Past {
2787 DateRelativeKind::InPast
2788 } else {
2789 DateRelativeKind::InFuture
2790 };
2791 let offset = self.parse_repository_expression()?;
2792 let span = self.span_covering(
2793 &start_span,
2794 &offset
2795 .source_location
2796 .as_ref()
2797 .map(|s| s.span.clone())
2798 .unwrap_or(start_span.clone()),
2799 );
2800 self.new_expression(
2801 ExpressionKind::PastFutureRange(kind, Arc::new(offset)),
2802 self.make_source(span),
2803 )
2804 }
2805
2806 TokenKind::StringLit => {
2808 let tok = self.next()?;
2809 self.new_expression(
2810 ExpressionKind::Literal(Value::Text(tok.text)),
2811 self.make_source(tok.span),
2812 )
2813 }
2814
2815 k if is_boolean_keyword(k) => {
2817 let tok = self.next()?;
2818 self.new_expression(
2819 ExpressionKind::Literal(Value::Boolean(token_kind_to_boolean_value(&tok.kind))),
2820 self.make_source(tok.span),
2821 )
2822 }
2823
2824 TokenKind::NumberLit => self.parse_number_expression(),
2826
2827 k if can_be_label(k) => {
2829 let reference = self.parse_expression_reference()?;
2830 let span = self.span_covering(&start_span, &self.last_span);
2831 self.new_expression(ExpressionKind::Reference(reference), self.make_source(span))
2832 }
2833
2834 _ => {
2835 let tok = self.next()?;
2836 Err(self.error_at_token(
2837 &tok,
2838 format!("Expected an expression, found '{}'", tok.text),
2839 ))
2840 }
2841 }
2842 }
2843
2844 fn parse_number_expression(&mut self) -> Result<Expression, Error> {
2845 let num_tok = self.next()?;
2846 let num_text = num_tok.text.clone();
2847 let start_span = num_tok.span.clone();
2848
2849 if num_text.len() == 4
2851 && num_text.chars().all(|c| c.is_ascii_digit())
2852 && self.at(&TokenKind::Minus)?
2853 {
2854 let minus_span = self.peek()?.span.clone();
2861 if minus_span.start == start_span.end {
2863 let value = self.parse_date_literal(num_text, start_span.clone())?;
2864 return self
2865 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
2866 }
2867 }
2868
2869 if num_text.len() == 2
2871 && num_text.chars().all(|c| c.is_ascii_digit())
2872 && self.at(&TokenKind::Colon)?
2873 {
2874 let colon_span = self.peek()?.span.clone();
2875 if colon_span.start == start_span.end {
2876 let value = self.try_parse_time_literal(num_text, start_span.clone())?;
2877 return self
2878 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
2879 }
2880 }
2881
2882 if self.at(&TokenKind::PercentPercent)? {
2884 let pp_tok = self.next()?;
2885 if let Ok(next_peek) = self.peek() {
2886 if next_peek.kind == TokenKind::NumberLit {
2887 return Err(self.error_at_token(
2888 &pp_tok,
2889 "Permille literal cannot be followed by a digit",
2890 ));
2891 }
2892 }
2893 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2894 return self.new_expression(
2895 ExpressionKind::Literal(Value::NumberWithUnit(decimal, "permille".to_string())),
2896 self.make_source(start_span),
2897 );
2898 }
2899
2900 if self.at(&TokenKind::Percent)? {
2902 let pct_span = self.peek()?.span.clone();
2903 let pct_tok = self.next()?;
2906 if let Ok(next_peek) = self.peek() {
2907 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
2908 return Err(self.error_at_token(
2909 &pct_tok,
2910 "Percent literal cannot be followed by a digit",
2911 ));
2912 }
2913 }
2914 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2915 return self.new_expression(
2916 ExpressionKind::Literal(Value::NumberWithUnit(decimal, "percent".to_string())),
2917 self.make_source(self.span_covering(&start_span, &pct_span)),
2918 );
2919 }
2920
2921 if self.at(&TokenKind::Permille)? {
2923 self.next()?;
2924 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2925 return self.new_expression(
2926 ExpressionKind::Literal(Value::NumberWithUnit(decimal, "permille".to_string())),
2927 self.make_source(start_span),
2928 );
2929 }
2930
2931 if can_be_label(&self.peek()?.kind) {
2932 let (unit_path, end_span) = self.parse_unit_path()?;
2933 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2934 return self.new_expression(
2935 ExpressionKind::Literal(Value::NumberWithUnit(decimal, unit_path)),
2936 self.make_source(self.span_covering(&start_span, &end_span)),
2937 );
2938 }
2939
2940 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2942 self.new_expression(
2943 ExpressionKind::Literal(Value::Number(decimal)),
2944 self.make_source(start_span),
2945 )
2946 }
2947
2948 fn parse_expression_reference(&mut self) -> Result<Reference, Error> {
2949 let mut segments = Vec::new();
2950
2951 let first = self.next()?;
2952 segments.push(first.text.clone());
2953
2954 while self.at(&TokenKind::Dot)? {
2955 self.next()?; let seg = self.next()?;
2957 if !can_be_label(&seg.kind) {
2958 return Err(self.error_at_token(
2959 &seg,
2960 format!("Expected an identifier after '.', found {}", seg.kind),
2961 ));
2962 }
2963 segments.push(seg.text.clone());
2964 }
2965
2966 Ok(Reference::from_path(segments))
2967 }
2968}
2969
2970fn parse_decimal_string(text: &str, span: &Span, parser: &Parser) -> Result<Decimal, Error> {
2975 text.parse::<crate::literals::NumberLiteral>()
2976 .map(|parsed| parsed.0)
2977 .map_err(|message| {
2978 Error::parsing(message, parser.make_source(span.clone()), None::<String>)
2979 })
2980}
2981
2982fn try_negate_numeric_literal(value: Value) -> Result<Value, Value> {
2984 match value {
2985 Value::Number(d) => Ok(Value::Number(-d)),
2986 Value::NumberWithUnit(d, unit) => Ok(Value::NumberWithUnit(-d, unit)),
2987 other => Err(other),
2988 }
2989}
2990
2991fn is_comparison_operator(kind: &TokenKind) -> bool {
2992 matches!(
2993 kind,
2994 TokenKind::Gt | TokenKind::Lt | TokenKind::Gte | TokenKind::Lte | TokenKind::Is
2995 )
2996}