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