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