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, conversion_target_from_token, is_boolean_keyword,
6 is_calendar_unit_token, is_duration_unit, is_math_function, is_spec_body_keyword,
7 is_structural_keyword, is_type_keyword, token_kind_to_boolean_value,
8 token_kind_to_calendar_unit, token_kind_to_duration_unit, token_kind_to_primitive, Lexer,
9 Token, TokenKind,
10};
11use crate::parsing::source::Source;
12use rust_decimal::Decimal;
13use std::str::FromStr;
14use std::sync::Arc;
15
16type TypeArrowChain = (ParentType, Option<SpecRef>, Option<Vec<Constraint>>);
17
18pub struct ParseResult {
19 pub specs: Vec<LemmaSpec>,
20 pub expression_count: usize,
21}
22
23pub fn parse(
24 content: &str,
25 attribute: &str,
26 limits: &ResourceLimits,
27) -> Result<ParseResult, Error> {
28 if content.len() > limits.max_file_size_bytes {
29 return Err(Error::resource_limit_exceeded(
30 "max_file_size_bytes",
31 format!(
32 "{} bytes ({} MB)",
33 limits.max_file_size_bytes,
34 limits.max_file_size_bytes / (1024 * 1024)
35 ),
36 format!(
37 "{} bytes ({:.2} MB)",
38 content.len(),
39 content.len() as f64 / (1024.0 * 1024.0)
40 ),
41 "Reduce file size or split into multiple specs",
42 None,
43 None,
44 None,
45 ));
46 }
47
48 let mut parser = Parser::new(content, attribute, limits);
49 let specs = parser.parse_file()?;
50 Ok(ParseResult {
51 specs,
52 expression_count: parser.expression_count,
53 })
54}
55
56struct Parser {
57 lexer: Lexer,
58 depth_tracker: DepthTracker,
59 expression_count: usize,
60 max_expression_count: usize,
61 max_spec_name_length: usize,
62 max_data_name_length: usize,
63 max_rule_name_length: usize,
64}
65
66impl Parser {
67 fn new(content: &str, attribute: &str, limits: &ResourceLimits) -> Self {
68 Parser {
69 lexer: Lexer::new(content, attribute),
70 depth_tracker: DepthTracker::with_max_depth(limits.max_expression_depth),
71 expression_count: 0,
72 max_expression_count: limits.max_expression_count,
73 max_spec_name_length: crate::limits::MAX_SPEC_NAME_LENGTH,
74 max_data_name_length: crate::limits::MAX_DATA_NAME_LENGTH,
75 max_rule_name_length: crate::limits::MAX_RULE_NAME_LENGTH,
76 }
77 }
78
79 fn attribute(&self) -> String {
80 self.lexer.attribute().to_string()
81 }
82
83 fn peek(&mut self) -> Result<&Token, Error> {
84 self.lexer.peek()
85 }
86
87 fn next(&mut self) -> Result<Token, Error> {
88 self.lexer.next_token()
89 }
90
91 fn at(&mut self, kind: &TokenKind) -> Result<bool, Error> {
92 Ok(&self.peek()?.kind == kind)
93 }
94
95 fn at_any(&mut self, kinds: &[TokenKind]) -> Result<bool, Error> {
96 let current = &self.peek()?.kind;
97 Ok(kinds.contains(current))
98 }
99
100 fn expect(&mut self, kind: &TokenKind) -> Result<Token, Error> {
101 let token = self.next()?;
102 if &token.kind == kind {
103 Ok(token)
104 } else {
105 Err(self.error_at_token(&token, format!("Expected {}, found {}", kind, token.kind)))
106 }
107 }
108
109 fn error_at_token(&self, token: &Token, message: impl Into<String>) -> Error {
110 Error::parsing(
111 message,
112 Source::new(self.lexer.attribute(), token.span.clone()),
113 None::<String>,
114 )
115 }
116
117 fn error_at_token_with_suggestion(
118 &self,
119 token: &Token,
120 message: impl Into<String>,
121 suggestion: impl Into<String>,
122 ) -> Error {
123 Error::parsing(
124 message,
125 Source::new(self.lexer.attribute(), token.span.clone()),
126 Some(suggestion),
127 )
128 }
129
130 fn parse_spec_ref_trailing_effective(&mut self) -> Result<Option<DateTimeValue>, Error> {
131 let mut effective = None;
132 if self.at(&TokenKind::NumberLit)? {
133 let peeked = self.peek()?;
134 if peeked.text.len() == 4 && peeked.text.chars().all(|c| c.is_ascii_digit()) {
135 effective = self.try_parse_effective_from()?;
136 }
137 }
138 Ok(effective)
139 }
140
141 fn make_source(&self, span: Span) -> Source {
142 Source::new(self.lexer.attribute(), span)
143 }
144
145 fn span_from(&self, start: &Span) -> Span {
146 Span {
149 start: start.start,
150 end: start.end.max(start.start),
151 line: start.line,
152 col: start.col,
153 }
154 }
155
156 fn span_covering(&self, start: &Span, end: &Span) -> Span {
157 Span {
158 start: start.start,
159 end: end.end,
160 line: start.line,
161 col: start.col,
162 }
163 }
164
165 fn parse_file(&mut self) -> Result<Vec<LemmaSpec>, Error> {
170 let mut specs = Vec::new();
171 loop {
172 if self.at(&TokenKind::Eof)? {
173 break;
174 }
175 if self.at(&TokenKind::Spec)? {
176 specs.push(self.parse_spec()?);
177 } else {
178 let token = self.next()?;
179 return Err(self.error_at_token_with_suggestion(
180 &token,
181 format!(
182 "Expected a spec declaration (e.g. 'spec my_spec'), found {}",
183 token.kind
184 ),
185 "A Lemma file must start with 'spec <name>'",
186 ));
187 }
188 }
189 Ok(specs)
190 }
191
192 fn parse_spec(&mut self) -> Result<LemmaSpec, Error> {
193 let spec_token = self.expect(&TokenKind::Spec)?;
194 let start_line = spec_token.span.line;
195
196 let (name, name_span) = self.parse_spec_name()?;
197 crate::limits::check_max_length(
198 &name,
199 self.max_spec_name_length,
200 "spec",
201 Some(Source::new(self.lexer.attribute(), name_span)),
202 )?;
203
204 let effective_from = self.try_parse_effective_from()?;
205
206 let commentary = self.try_parse_commentary()?;
207
208 let attribute = self.attribute();
209 let mut spec = LemmaSpec::new(name.clone())
210 .with_attribute(attribute)
211 .with_start_line(start_line);
212 spec.effective_from = crate::parsing::ast::EffectiveDate::from_option(effective_from);
213
214 if let Some(commentary_text) = commentary {
215 spec = spec.set_commentary(commentary_text);
216 }
217
218 let mut data = Vec::new();
222 let mut rules = Vec::new();
223 let mut meta_fields = Vec::new();
224
225 loop {
226 let peek_kind = self.peek()?.kind.clone();
227 match peek_kind {
228 TokenKind::Data => {
229 let datum = self.parse_data()?;
230 data.push(datum);
231 }
232 TokenKind::Rule => {
233 let rule = self.parse_rule()?;
234 rules.push(rule);
235 }
236 TokenKind::Type => {
237 let token = self.next()?;
238 return Err(self.error_at_token_with_suggestion(
239 &token,
240 "'type' has been removed. Types are now declared as data",
241 "Use 'data' instead of 'type', e.g. 'data age: number -> minimum 0'",
242 ));
243 }
244 TokenKind::Meta => {
245 let meta = self.parse_meta()?;
246 meta_fields.push(meta);
247 }
248 TokenKind::With => {
249 let with_datas = self.parse_with_statement()?;
250 data.extend(with_datas);
251 }
252 TokenKind::Spec | TokenKind::Eof => break,
253 _ => {
254 let token = self.next()?;
255 return Err(self.error_at_token_with_suggestion(
256 &token,
257 format!(
258 "Expected 'data', 'rule', 'meta', 'with', or a new 'spec', found '{}'",
259 token.text
260 ),
261 "Check the spelling or add the appropriate keyword",
262 ));
263 }
264 }
265 }
266
267 for data in data {
268 spec = spec.add_data(data);
269 }
270 for rule in rules {
271 spec = spec.add_rule(rule);
272 }
273 for meta in meta_fields {
274 spec = spec.add_meta_field(meta);
275 }
276
277 Ok(spec)
278 }
279
280 fn parse_spec_name(&mut self) -> Result<(String, Span), Error> {
283 let mut name = String::new();
284 let start_span;
285
286 if self.at(&TokenKind::At)? {
287 let at_tok = self.next()?;
288 start_span = at_tok.span.clone();
289 name.push('@');
290 } else {
291 start_span = self.peek()?.span.clone();
292 }
293
294 let first = self.next()?;
296 if !first.kind.is_identifier_like() {
297 return Err(self.error_at_token(
298 &first,
299 format!("Expected a spec name, found {}", first.kind),
300 ));
301 }
302 name.push_str(&first.text);
303 let mut end_span = first.span.clone();
304
305 while self.at(&TokenKind::Slash)? {
307 self.next()?; name.push('/');
309 let seg = self.next()?;
310 if !seg.kind.is_identifier_like() {
311 return Err(self.error_at_token(
312 &seg,
313 format!(
314 "Expected identifier after '/' in spec name, found {}",
315 seg.kind
316 ),
317 ));
318 }
319 name.push_str(&seg.text);
320 end_span = seg.span.clone();
321 }
322
323 while self.at(&TokenKind::Minus)? {
325 let minus_span = self.peek()?.span.clone();
328 self.next()?; if let Ok(peeked) = self.peek() {
330 if peeked.kind.is_identifier_like() {
331 let seg = self.next()?;
332 name.push('-');
333 name.push_str(&seg.text);
334 end_span = seg.span.clone();
335 while self.at(&TokenKind::Slash)? {
337 self.next()?; name.push('/');
339 let seg2 = self.next()?;
340 if !seg2.kind.is_identifier_like() {
341 return Err(self.error_at_token(
342 &seg2,
343 format!(
344 "Expected identifier after '/' in spec name, found {}",
345 seg2.kind
346 ),
347 ));
348 }
349 name.push_str(&seg2.text);
350 end_span = seg2.span.clone();
351 }
352 } else {
353 let span = self.span_covering(&start_span, &minus_span);
355 return Err(Error::parsing(
356 "Trailing '-' after spec name",
357 self.make_source(span),
358 None::<String>,
359 ));
360 }
361 }
362 }
363
364 let full_span = self.span_covering(&start_span, &end_span);
365 Ok((name, full_span))
366 }
367
368 fn try_parse_effective_from(&mut self) -> Result<Option<DateTimeValue>, Error> {
369 if !self.at(&TokenKind::NumberLit)? {
374 return Ok(None);
375 }
376
377 let peeked = self.peek()?;
378 let peeked_text = peeked.text.clone();
379 let peeked_span = peeked.span.clone();
380
381 if peeked_text.len() == 4 && peeked_text.chars().all(|c| c.is_ascii_digit()) {
383 let mut dt_str = String::new();
385 let num_tok = self.next()?; dt_str.push_str(&num_tok.text);
387
388 while self.at(&TokenKind::Minus)? {
390 self.next()?; dt_str.push('-');
392 let part = self.next()?;
393 dt_str.push_str(&part.text);
394 }
395
396 if self.at(&TokenKind::Identifier)? {
398 let peeked = self.peek()?;
399 if peeked.text.starts_with('T') || peeked.text.starts_with('t') {
400 let time_part = self.next()?;
401 dt_str.push_str(&time_part.text);
402 while self.at(&TokenKind::Colon)? {
404 self.next()?;
405 dt_str.push(':');
406 let part = self.next()?;
407 dt_str.push_str(&part.text);
408 }
409 if self.at(&TokenKind::Plus)? {
411 self.next()?;
412 dt_str.push('+');
413 let tz_part = self.next()?;
414 dt_str.push_str(&tz_part.text);
415 if self.at(&TokenKind::Colon)? {
416 self.next()?;
417 dt_str.push(':');
418 let tz_min = self.next()?;
419 dt_str.push_str(&tz_min.text);
420 }
421 }
422 }
423 }
424
425 if let Ok(dtv) = dt_str.parse::<DateTimeValue>() {
427 return Ok(Some(dtv));
428 }
429
430 return Err(Error::parsing(
431 format!("Invalid date/time in spec declaration: '{}'", dt_str),
432 self.make_source(peeked_span),
433 None::<String>,
434 ));
435 }
436
437 Ok(None)
438 }
439
440 fn try_parse_commentary(&mut self) -> Result<Option<String>, Error> {
441 if !self.at(&TokenKind::Commentary)? {
442 return Ok(None);
443 }
444 let token = self.next()?;
445 let trimmed = token.text.trim().to_string();
446 if trimmed.is_empty() {
447 Ok(None)
448 } else {
449 Ok(Some(trimmed))
450 }
451 }
452
453 fn parse_data(&mut self) -> Result<LemmaData, Error> {
458 let data_token = self.expect(&TokenKind::Data)?;
459 let start_span = data_token.span.clone();
460
461 let reference = self.parse_reference()?;
462 for segment in reference
463 .segments
464 .iter()
465 .chain(std::iter::once(&reference.name))
466 {
467 crate::limits::check_max_length(
468 segment,
469 self.max_data_name_length,
470 "data",
471 Some(Source::new(self.lexer.attribute(), start_span.clone())),
472 )?;
473 }
474
475 if self.at(&TokenKind::From)? {
477 self.next()?; let (from_name, _from_span) = self.parse_spec_name()?;
479 let from_registry = from_name.starts_with('@');
480 let effective = self.parse_spec_ref_trailing_effective()?;
481 let from = SpecRef {
482 name: from_name,
483 from_registry,
484 effective,
485 };
486 let constraints = if self.at(&TokenKind::Arrow)? {
487 let (_, _, constraints) = self.parse_remaining_arrow_chain()?;
488 constraints
489 } else {
490 None
491 };
492 let end_span = self.peek()?.span.clone();
493 let span = self.span_covering(&start_span, &end_span);
494 let source = self.make_source(span);
495 let base = ParentType::Custom {
496 name: reference.name.clone(),
497 };
498 return Ok(LemmaData::new(
499 reference,
500 DataValue::TypeDeclaration {
501 base,
502 constraints,
503 from: Some(from),
504 },
505 source,
506 ));
507 }
508
509 self.expect(&TokenKind::Colon)?;
510
511 let is_binding = !reference.segments.is_empty();
512 let value = self.parse_data_value(is_binding)?;
513
514 let end_span = self.peek()?.span.clone();
515 let span = self.span_covering(&start_span, &end_span);
516 let source = self.make_source(span);
517
518 Ok(LemmaData::new(reference, value, source))
519 }
520
521 fn parse_reference(&mut self) -> Result<Reference, Error> {
522 let mut segments = Vec::new();
523
524 let first = self.next()?;
525 if is_structural_keyword(&first.kind) {
528 return Err(self.error_at_token_with_suggestion(
529 &first,
530 format!(
531 "'{}' is a reserved keyword and cannot be used as a name",
532 first.text
533 ),
534 "Choose a different name that is not a reserved keyword",
535 ));
536 }
537
538 if !can_be_reference_segment(&first.kind) {
539 return Err(self.error_at_token(
540 &first,
541 format!("Expected an identifier, found {}", first.kind),
542 ));
543 }
544
545 segments.push(first.text.clone());
546
547 while self.at(&TokenKind::Dot)? {
549 self.next()?; let seg = self.next()?;
551 if !can_be_reference_segment(&seg.kind) {
552 return Err(self.error_at_token(
553 &seg,
554 format!("Expected an identifier after '.', found {}", seg.kind),
555 ));
556 }
557 segments.push(seg.text.clone());
558 }
559
560 Ok(Reference::from_path(segments))
561 }
562
563 fn parse_data_value(&mut self, is_binding: bool) -> Result<DataValue, Error> {
564 if self.at(&TokenKind::Spec)? {
565 let token = self.next()?;
566 return Err(self.error_at_token_with_suggestion(
567 &token,
568 "'data ... : spec ...' syntax has been removed",
569 "Use 'with <spec_name>' or 'with <alias>: <spec_name>' instead",
570 ));
571 }
572
573 let peek_kind = self.peek()?.kind.clone();
574
575 if can_be_label(&peek_kind) {
584 let next_is_dot = self.lexer.peek_second()?.kind == TokenKind::Dot;
585 if next_is_dot || is_binding {
586 let target = self.parse_reference()?;
587 let (_, _, constraints) = self.parse_remaining_arrow_chain()?;
588 return Ok(DataValue::Reference {
589 target,
590 constraints,
591 });
592 }
593 }
594
595 if token_kind_to_primitive(&peek_kind).is_some() || can_be_label(&peek_kind) {
597 let (base, from_spec, constraints) = self.parse_type_arrow_chain()?;
598 if self.at(&TokenKind::Dot)? {
599 let dot_tok = self.peek()?.clone();
600 return Err(self.error_at_token_with_suggestion(
601 &dot_tok,
602 "Unexpected dot after type declaration",
603 "Typedef references must be a single identifier. To reference another data or rule by value, use a dotted path like 'other_spec.name'",
604 ));
605 }
606 return Ok(DataValue::TypeDeclaration {
607 base,
608 constraints,
609 from: from_spec,
610 });
611 }
612
613 let value = self.parse_literal_value()?;
615 Ok(DataValue::Literal(value))
616 }
617
618 fn parse_with_spec_name(&mut self) -> Result<(String, String), Error> {
619 if self.at(&TokenKind::At)? {
620 let (name, _) = self.parse_spec_name()?;
621 let alias = name.rsplit('/').next().unwrap_or(&name).to_string();
622 return Ok((name, alias));
623 }
624 let first = self.next()?;
625 if !can_be_reference_segment(&first.kind) {
626 return Err(self.error_at_token(
627 &first,
628 format!("Expected a spec name after 'with', found {}", first.kind),
629 ));
630 }
631 let mut name = first.text.clone();
632 while self.at(&TokenKind::Slash)? {
633 self.next()?;
634 name.push('/');
635 let seg = self.next()?;
636 if !seg.kind.is_identifier_like() {
637 return Err(self.error_at_token(
638 &seg,
639 format!(
640 "Expected identifier after '/' in spec name, found {}",
641 seg.kind
642 ),
643 ));
644 }
645 name.push_str(&seg.text);
646 }
647 while self.at(&TokenKind::Minus)? {
648 self.next()?;
649 let seg = self.next()?;
650 if !seg.kind.is_identifier_like() {
651 return Err(self.error_at_token(
652 &seg,
653 format!(
654 "Expected identifier after '-' in spec name, found {}",
655 seg.kind
656 ),
657 ));
658 }
659 name.push('-');
660 name.push_str(&seg.text);
661 while self.at(&TokenKind::Slash)? {
662 self.next()?;
663 name.push('/');
664 let seg2 = self.next()?;
665 if !seg2.kind.is_identifier_like() {
666 return Err(self.error_at_token(
667 &seg2,
668 format!(
669 "Expected identifier after '/' in spec name, found {}",
670 seg2.kind
671 ),
672 ));
673 }
674 name.push_str(&seg2.text);
675 }
676 }
677 let alias = name.rsplit('/').next().unwrap_or(&name).to_string();
678 Ok((name, alias))
679 }
680
681 fn make_with_data(
682 &self,
683 spec_name: String,
684 alias: String,
685 effective: Option<DateTimeValue>,
686 span: &Span,
687 ) -> LemmaData {
688 let from_registry = spec_name.starts_with('@');
689 let source = self.make_source(span.clone());
690 LemmaData::new(
691 Reference::local(alias),
692 DataValue::SpecReference(SpecRef {
693 name: spec_name,
694 from_registry,
695 effective,
696 }),
697 source,
698 )
699 }
700
701 fn parse_with_statement(&mut self) -> Result<Vec<LemmaData>, Error> {
702 let with_token = self.expect(&TokenKind::With)?;
703 let start_span = with_token.span.clone();
704
705 if !self.at(&TokenKind::At)? {
707 let first = self.peek()?;
708 if can_be_reference_segment(&first.kind) {
709 let first_text = first.text.clone();
710 let first_tok = self.next()?;
713 if self.at(&TokenKind::Colon)? {
714 self.next()?; let (spec_name, _) = self.parse_spec_name()?;
716 let effective = self.parse_spec_ref_trailing_effective()?;
717 let end_span = self.peek()?.span.clone();
718 let span = self.span_covering(&start_span, &end_span);
719 return Ok(vec![
720 self.make_with_data(spec_name, first_text, effective, &span)
721 ]);
722 }
723 let mut name = first_tok.text.clone();
726 while self.at(&TokenKind::Slash)? {
727 self.next()?;
728 name.push('/');
729 let seg = self.next()?;
730 if !seg.kind.is_identifier_like() {
731 return Err(self.error_at_token(
732 &seg,
733 format!(
734 "Expected identifier after '/' in spec name, found {}",
735 seg.kind
736 ),
737 ));
738 }
739 name.push_str(&seg.text);
740 }
741 while self.at(&TokenKind::Minus)? {
742 self.next()?;
743 let seg = self.next()?;
744 if !seg.kind.is_identifier_like() {
745 return Err(self.error_at_token(
746 &seg,
747 format!(
748 "Expected identifier after '-' in spec name, found {}",
749 seg.kind
750 ),
751 ));
752 }
753 name.push('-');
754 name.push_str(&seg.text);
755 while self.at(&TokenKind::Slash)? {
756 self.next()?;
757 name.push('/');
758 let seg2 = self.next()?;
759 if !seg2.kind.is_identifier_like() {
760 return Err(self.error_at_token(
761 &seg2,
762 format!(
763 "Expected identifier after '/' in spec name, found {}",
764 seg2.kind
765 ),
766 ));
767 }
768 name.push_str(&seg2.text);
769 }
770 }
771 let alias = name.rsplit('/').next().unwrap_or(&name).to_string();
772
773 if self.at(&TokenKind::Comma)? {
775 let mut results = Vec::new();
776 let end_span = self.peek()?.span.clone();
777 let span = self.span_covering(&start_span, &end_span);
778 results.push(self.make_with_data(name, alias, None, &span));
779 while self.at(&TokenKind::Comma)? {
780 self.next()?; let (next_name, next_alias) = self.parse_with_spec_name()?;
782 let end_span = self.peek()?.span.clone();
783 let span = self.span_covering(&start_span, &end_span);
784 results.push(self.make_with_data(next_name, next_alias, None, &span));
785 }
786 return Ok(results);
787 }
788
789 let effective = self.parse_spec_ref_trailing_effective()?;
791 let end_span = self.peek()?.span.clone();
792 let span = self.span_covering(&start_span, &end_span);
793 return Ok(vec![self.make_with_data(name, alias, effective, &span)]);
794 }
795 }
796
797 let (spec_name, alias) = self.parse_with_spec_name()?;
799
800 if self.at(&TokenKind::Comma)? {
801 let mut results = Vec::new();
802 let end_span = self.peek()?.span.clone();
803 let span = self.span_covering(&start_span, &end_span);
804 results.push(self.make_with_data(spec_name, alias, None, &span));
805 while self.at(&TokenKind::Comma)? {
806 self.next()?;
807 let (next_name, next_alias) = self.parse_with_spec_name()?;
808 let end_span = self.peek()?.span.clone();
809 let span = self.span_covering(&start_span, &end_span);
810 results.push(self.make_with_data(next_name, next_alias, None, &span));
811 }
812 return Ok(results);
813 }
814
815 let effective = self.parse_spec_ref_trailing_effective()?;
816 let end_span = self.peek()?.span.clone();
817 let span = self.span_covering(&start_span, &end_span);
818 Ok(vec![self.make_with_data(spec_name, alias, effective, &span)])
819 }
820
821 fn parse_rule(&mut self) -> Result<LemmaRule, Error> {
826 let rule_token = self.expect(&TokenKind::Rule)?;
827 let start_span = rule_token.span.clone();
828
829 let name_tok = self.next()?;
830 if is_structural_keyword(&name_tok.kind) {
831 return Err(self.error_at_token_with_suggestion(
832 &name_tok,
833 format!(
834 "'{}' is a reserved keyword and cannot be used as a rule name",
835 name_tok.text
836 ),
837 "Choose a different name that is not a reserved keyword",
838 ));
839 }
840 if !can_be_label(&name_tok.kind) && !is_type_keyword(&name_tok.kind) {
841 return Err(self.error_at_token(
842 &name_tok,
843 format!("Expected a rule name, found {}", name_tok.kind),
844 ));
845 }
846 let rule_name = name_tok.text.clone();
847 crate::limits::check_max_length(
848 &rule_name,
849 self.max_rule_name_length,
850 "rule",
851 Some(Source::new(self.lexer.attribute(), name_tok.span.clone())),
852 )?;
853
854 self.expect(&TokenKind::Colon)?;
855
856 let expression = if self.at(&TokenKind::Veto)? {
858 self.parse_veto_expression()?
859 } else {
860 self.parse_expression()?
861 };
862
863 let mut unless_clauses = Vec::new();
865 while self.at(&TokenKind::Unless)? {
866 unless_clauses.push(self.parse_unless_clause()?);
867 }
868
869 let end_span = if let Some(last_unless) = unless_clauses.last() {
870 last_unless.source_location.span.clone()
871 } else if let Some(ref loc) = expression.source_location {
872 loc.span.clone()
873 } else {
874 start_span.clone()
875 };
876
877 let span = self.span_covering(&start_span, &end_span);
878 Ok(LemmaRule {
879 name: rule_name,
880 expression,
881 unless_clauses,
882 source_location: self.make_source(span),
883 })
884 }
885
886 fn parse_veto_expression(&mut self) -> Result<Expression, Error> {
887 let veto_tok = self.expect(&TokenKind::Veto)?;
888 let start_span = veto_tok.span.clone();
889
890 let message = if self.at(&TokenKind::StringLit)? {
891 let str_tok = self.next()?;
892 let content = unquote_string(&str_tok.text);
893 Some(content)
894 } else {
895 None
896 };
897
898 let span = self.span_from(&start_span);
899 self.new_expression(
900 ExpressionKind::Veto(VetoExpression { message }),
901 self.make_source(span),
902 )
903 }
904
905 fn parse_unless_clause(&mut self) -> Result<UnlessClause, Error> {
906 let unless_tok = self.expect(&TokenKind::Unless)?;
907 let start_span = unless_tok.span.clone();
908
909 let condition = self.parse_expression()?;
910
911 self.expect(&TokenKind::Then)?;
912
913 let result = if self.at(&TokenKind::Veto)? {
914 self.parse_veto_expression()?
915 } else {
916 self.parse_expression()?
917 };
918
919 let end_span = result
920 .source_location
921 .as_ref()
922 .map(|s| s.span.clone())
923 .unwrap_or_else(|| start_span.clone());
924 let span = self.span_covering(&start_span, &end_span);
925
926 Ok(UnlessClause {
927 condition,
928 result,
929 source_location: self.make_source(span),
930 })
931 }
932
933 fn parse_type_arrow_chain(&mut self) -> Result<TypeArrowChain, Error> {
935 let name_tok = self.next()?;
936 let base = if let Some(kind) = token_kind_to_primitive(&name_tok.kind) {
937 ParentType::Primitive { primitive: kind }
938 } else if can_be_label(&name_tok.kind) {
939 ParentType::Custom {
940 name: name_tok.text.clone(),
941 }
942 } else {
943 return Err(self.error_at_token(
944 &name_tok,
945 format!("Expected a type name, found {}", name_tok.kind),
946 ));
947 };
948
949 let from_spec = if self.at(&TokenKind::From)? {
951 self.next()?; let (from_name, _) = self.parse_spec_name()?;
953 let from_registry = from_name.starts_with('@');
954 let effective = self.parse_spec_ref_trailing_effective()?;
955 Some(SpecRef {
956 name: from_name,
957 from_registry,
958 effective,
959 })
960 } else {
961 None
962 };
963
964 let mut commands = Vec::new();
966 while self.at(&TokenKind::Arrow)? {
967 self.next()?; let (cmd, cmd_args) = self.parse_command()?;
969 commands.push((cmd, cmd_args));
970 }
971
972 let constraints = if commands.is_empty() {
973 None
974 } else {
975 Some(commands)
976 };
977
978 Ok((base, from_spec, constraints))
979 }
980
981 fn parse_remaining_arrow_chain(&mut self) -> Result<TypeArrowChain, Error> {
982 let mut commands = Vec::new();
983 while self.at(&TokenKind::Arrow)? {
984 self.next()?; let (cmd, cmd_args) = self.parse_command()?;
986 commands.push((cmd, cmd_args));
987 }
988 let constraints = if commands.is_empty() {
989 None
990 } else {
991 Some(commands)
992 };
993 Ok((
994 ParentType::Custom {
995 name: String::new(),
996 },
997 None,
998 constraints,
999 ))
1000 }
1001
1002 fn parse_command(&mut self) -> Result<(TypeConstraintCommand, Vec<CommandArg>), Error> {
1003 let name_tok = self.next()?;
1004 if !can_be_label(&name_tok.kind) && !is_type_keyword(&name_tok.kind) {
1005 return Err(self.error_at_token(
1006 &name_tok,
1007 format!("Expected a command name, found {}", name_tok.kind),
1008 ));
1009 }
1010 let cmd = try_parse_type_constraint_command(&name_tok.text).ok_or_else(|| {
1011 self.error_at_token(
1012 &name_tok,
1013 format!(
1014 "Unknown constraint command '{}'. Valid commands: help, default, unit, minimum, maximum, decimals, precision, option, options, length",
1015 name_tok.text
1016 ),
1017 )
1018 })?;
1019
1020 let mut args = Vec::new();
1021 loop {
1022 if self.at(&TokenKind::Arrow)?
1023 || self.at(&TokenKind::Eof)?
1024 || is_spec_body_keyword(&self.peek()?.kind)
1025 || self.at(&TokenKind::Spec)?
1026 {
1027 break;
1028 }
1029
1030 let peek_kind = self.peek()?.kind.clone();
1031 match peek_kind {
1032 TokenKind::NumberLit
1033 | TokenKind::Minus
1034 | TokenKind::Plus
1035 | TokenKind::StringLit => {
1036 let value = self.parse_literal_value()?;
1037 args.push(CommandArg::Literal(value));
1038 }
1039 ref k if is_boolean_keyword(k) => {
1040 let value = self.parse_literal_value()?;
1041 args.push(CommandArg::Literal(value));
1042 }
1043 ref k if can_be_label(k) || is_type_keyword(k) => {
1044 let tok = self.next()?;
1045 args.push(CommandArg::Label(tok.text));
1046 }
1047 _ => break,
1048 }
1049 }
1050
1051 Ok((cmd, args))
1052 }
1053
1054 fn parse_meta(&mut self) -> Result<MetaField, Error> {
1059 let meta_tok = self.expect(&TokenKind::Meta)?;
1060 let start_span = meta_tok.span.clone();
1061
1062 let key_tok = self.next()?;
1063 let key = key_tok.text.clone();
1064
1065 self.expect(&TokenKind::Colon)?;
1066
1067 let value = self.parse_meta_value()?;
1068
1069 let end_span = self.peek()?.span.clone();
1070 let span = self.span_covering(&start_span, &end_span);
1071
1072 Ok(MetaField {
1073 key,
1074 value,
1075 source_location: self.make_source(span),
1076 })
1077 }
1078
1079 fn parse_meta_value(&mut self) -> Result<MetaValue, Error> {
1080 let peeked = self.peek()?;
1082 match &peeked.kind {
1083 TokenKind::StringLit => {
1084 let value = self.parse_literal_value()?;
1085 return Ok(MetaValue::Literal(value));
1086 }
1087 TokenKind::NumberLit => {
1088 let value = self.parse_literal_value()?;
1089 return Ok(MetaValue::Literal(value));
1090 }
1091 k if is_boolean_keyword(k) => {
1092 let value = self.parse_literal_value()?;
1093 return Ok(MetaValue::Literal(value));
1094 }
1095 _ => {}
1096 }
1097
1098 let mut ident = String::new();
1101 loop {
1102 let peeked = self.peek()?;
1103 match &peeked.kind {
1104 k if k.is_identifier_like() => {
1105 let tok = self.next()?;
1106 ident.push_str(&tok.text);
1107 }
1108 TokenKind::Dot => {
1109 self.next()?;
1110 ident.push('.');
1111 }
1112 TokenKind::Slash => {
1113 self.next()?;
1114 ident.push('/');
1115 }
1116 TokenKind::Minus => {
1117 self.next()?;
1118 ident.push('-');
1119 }
1120 TokenKind::NumberLit => {
1121 let tok = self.next()?;
1122 ident.push_str(&tok.text);
1123 }
1124 _ => break,
1125 }
1126 }
1127
1128 if ident.is_empty() {
1129 let tok = self.peek()?.clone();
1130 return Err(self.error_at_token(&tok, "Expected a meta value"));
1131 }
1132
1133 Ok(MetaValue::Unquoted(ident))
1134 }
1135
1136 fn parse_literal_value(&mut self) -> Result<Value, Error> {
1141 let peeked = self.peek()?;
1142 match &peeked.kind {
1143 TokenKind::StringLit => {
1144 let tok = self.next()?;
1145 let content = unquote_string(&tok.text);
1146 Ok(Value::Text(content))
1147 }
1148 k if is_boolean_keyword(k) => {
1149 let tok = self.next()?;
1150 Ok(Value::Boolean(token_kind_to_boolean_value(&tok.kind)))
1151 }
1152 TokenKind::NumberLit => self.parse_number_literal(),
1153 TokenKind::Minus | TokenKind::Plus => self.parse_signed_number_literal(),
1154 _ => {
1155 let tok = self.next()?;
1156 Err(self.error_at_token(
1157 &tok,
1158 format!(
1159 "Expected a value (number, text, boolean, date, etc.), found '{}'",
1160 tok.text
1161 ),
1162 ))
1163 }
1164 }
1165 }
1166
1167 fn parse_signed_number_literal(&mut self) -> Result<Value, Error> {
1168 let sign_tok = self.next()?;
1169 let sign_span = sign_tok.span.clone();
1170 let is_negative = sign_tok.kind == TokenKind::Minus;
1171
1172 if !self.at(&TokenKind::NumberLit)? {
1173 let tok = self.peek()?.clone();
1174 return Err(self.error_at_token(
1175 &tok,
1176 format!(
1177 "Expected a number after '{}', found '{}'",
1178 sign_tok.text, tok.text
1179 ),
1180 ));
1181 }
1182
1183 let value = self.parse_number_literal()?;
1184 if !is_negative {
1185 return Ok(value);
1186 }
1187 match value {
1188 Value::Number(d) => Ok(Value::Number(-d)),
1189 Value::Scale(d, unit) => Ok(Value::Scale(-d, unit)),
1190 Value::Duration(d, unit) => Ok(Value::Duration(-d, unit)),
1191 Value::Ratio(d, label) => Ok(Value::Ratio(-d, label)),
1192 other => Err(Error::parsing(
1193 format!("Cannot negate this value: {}", other),
1194 self.make_source(sign_span),
1195 None::<String>,
1196 )),
1197 }
1198 }
1199
1200 fn parse_number_literal(&mut self) -> Result<Value, Error> {
1201 let num_tok = self.next()?;
1202 let num_text = &num_tok.text;
1203 let num_span = num_tok.span.clone();
1204
1205 if num_text.len() == 4
1207 && num_text.chars().all(|c| c.is_ascii_digit())
1208 && self.at(&TokenKind::Minus)?
1209 {
1210 return self.parse_date_literal(num_text.clone(), num_span);
1211 }
1212
1213 let peeked = self.peek()?;
1215
1216 if num_text.len() == 2
1218 && num_text.chars().all(|c| c.is_ascii_digit())
1219 && peeked.kind == TokenKind::Colon
1220 {
1221 return self.try_parse_time_literal(num_text.clone(), num_span);
1228 }
1229
1230 if peeked.kind == TokenKind::PercentPercent {
1232 let pp_tok = self.next()?;
1233 if let Ok(next_peek) = self.peek() {
1235 if next_peek.kind == TokenKind::NumberLit {
1236 return Err(self.error_at_token(
1237 &pp_tok,
1238 "Permille literal cannot be followed by a digit",
1239 ));
1240 }
1241 }
1242 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1243 let ratio_value = decimal / Decimal::from(1000);
1244 return Ok(Value::Ratio(ratio_value, Some("permille".to_string())));
1245 }
1246
1247 if peeked.kind == TokenKind::Percent {
1249 let pct_tok = self.next()?;
1250 if let Ok(next_peek) = self.peek() {
1252 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1253 return Err(self.error_at_token(
1254 &pct_tok,
1255 "Percent literal cannot be followed by a digit",
1256 ));
1257 }
1258 }
1259 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1260 let ratio_value = decimal / Decimal::from(100);
1261 return Ok(Value::Ratio(ratio_value, Some("percent".to_string())));
1262 }
1263
1264 if peeked.kind == TokenKind::PercentKw {
1266 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1268 let ratio_value = decimal / Decimal::from(100);
1269 return Ok(Value::Ratio(ratio_value, Some("percent".to_string())));
1270 }
1271
1272 if peeked.kind == TokenKind::Permille {
1274 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1276 let ratio_value = decimal / Decimal::from(1000);
1277 return Ok(Value::Ratio(ratio_value, Some("permille".to_string())));
1278 }
1279
1280 if is_duration_unit(&peeked.kind) && peeked.kind != TokenKind::PercentKw {
1282 let unit_tok = self.next()?;
1283 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1284 let duration_unit = token_kind_to_duration_unit(&unit_tok.kind);
1285 return Ok(Value::Duration(decimal, duration_unit));
1286 }
1287
1288 if can_be_label(&peeked.kind) {
1290 let unit_tok = self.next()?;
1291 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1292 return Ok(Value::Scale(decimal, unit_tok.text.clone()));
1293 }
1294
1295 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1297 Ok(Value::Number(decimal))
1298 }
1299
1300 fn parse_date_literal(&mut self, year_text: String, start_span: Span) -> Result<Value, Error> {
1301 let mut dt_str = year_text;
1302
1303 self.expect(&TokenKind::Minus)?;
1305 dt_str.push('-');
1306 let month_tok = self.expect(&TokenKind::NumberLit)?;
1307 dt_str.push_str(&month_tok.text);
1308
1309 self.expect(&TokenKind::Minus)?;
1311 dt_str.push('-');
1312 let day_tok = self.expect(&TokenKind::NumberLit)?;
1313 dt_str.push_str(&day_tok.text);
1314
1315 if self.at(&TokenKind::Identifier)? {
1317 let peeked = self.peek()?;
1318 if peeked.text.len() >= 2
1319 && (peeked.text.starts_with('T') || peeked.text.starts_with('t'))
1320 {
1321 let t_tok = self.next()?;
1323 dt_str.push_str(&t_tok.text);
1324
1325 if self.at(&TokenKind::Colon)? {
1327 self.next()?;
1328 dt_str.push(':');
1329 let min_tok = self.next()?;
1330 dt_str.push_str(&min_tok.text);
1331
1332 if self.at(&TokenKind::Colon)? {
1334 self.next()?;
1335 dt_str.push(':');
1336 let sec_tok = self.next()?;
1337 dt_str.push_str(&sec_tok.text);
1338
1339 if self.at(&TokenKind::Dot)? {
1341 self.next()?;
1342 dt_str.push('.');
1343 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1344 dt_str.push_str(&frac_tok.text);
1345 }
1346 }
1347 }
1348
1349 self.try_consume_timezone(&mut dt_str)?;
1351 }
1352 }
1353
1354 if let Ok(dtv) = dt_str.parse::<crate::literals::DateTimeValue>() {
1355 return Ok(Value::Date(dtv));
1356 }
1357
1358 Err(Error::parsing(
1359 format!("Invalid date/time format: '{}'", dt_str),
1360 self.make_source(start_span),
1361 None::<String>,
1362 ))
1363 }
1364
1365 fn try_consume_timezone(&mut self, dt_str: &mut String) -> Result<(), Error> {
1366 if self.at(&TokenKind::Identifier)? {
1368 let peeked = self.peek()?;
1369 if peeked.text == "Z" || peeked.text == "z" {
1370 let z_tok = self.next()?;
1371 dt_str.push_str(&z_tok.text);
1372 return Ok(());
1373 }
1374 }
1375
1376 if self.at(&TokenKind::Plus)? || self.at(&TokenKind::Minus)? {
1378 let sign_tok = self.next()?;
1379 dt_str.push_str(&sign_tok.text);
1380 let hour_tok = self.expect(&TokenKind::NumberLit)?;
1381 dt_str.push_str(&hour_tok.text);
1382 if self.at(&TokenKind::Colon)? {
1383 self.next()?;
1384 dt_str.push(':');
1385 let min_tok = self.expect(&TokenKind::NumberLit)?;
1386 dt_str.push_str(&min_tok.text);
1387 }
1388 }
1389
1390 Ok(())
1391 }
1392
1393 fn try_parse_time_literal(
1394 &mut self,
1395 hour_text: String,
1396 start_span: Span,
1397 ) -> Result<Value, Error> {
1398 let mut time_str = hour_text;
1399
1400 self.expect(&TokenKind::Colon)?;
1402 time_str.push(':');
1403 let min_tok = self.expect(&TokenKind::NumberLit)?;
1404 time_str.push_str(&min_tok.text);
1405
1406 if self.at(&TokenKind::Colon)? {
1408 self.next()?;
1409 time_str.push(':');
1410 let sec_tok = self.expect(&TokenKind::NumberLit)?;
1411 time_str.push_str(&sec_tok.text);
1412 }
1413
1414 self.try_consume_timezone(&mut time_str)?;
1416
1417 if let Ok(t) = time_str.parse::<chrono::NaiveTime>() {
1418 use chrono::Timelike;
1419 return Ok(Value::Time(TimeValue {
1420 hour: t.hour() as u8,
1421 minute: t.minute() as u8,
1422 second: t.second() as u8,
1423 timezone: None,
1424 }));
1425 }
1426
1427 Err(Error::parsing(
1428 format!("Invalid time format: '{}'", time_str),
1429 self.make_source(start_span),
1430 None::<String>,
1431 ))
1432 }
1433
1434 fn new_expression(
1439 &mut self,
1440 kind: ExpressionKind,
1441 source: Source,
1442 ) -> Result<Expression, Error> {
1443 self.expression_count += 1;
1444 if self.expression_count > self.max_expression_count {
1445 return Err(Error::resource_limit_exceeded(
1446 "max_expression_count",
1447 self.max_expression_count.to_string(),
1448 self.expression_count.to_string(),
1449 "Split logic into multiple rules to reduce expression count",
1450 Some(source),
1451 None,
1452 None,
1453 ));
1454 }
1455 Ok(Expression::new(kind, source))
1456 }
1457
1458 fn check_depth(&mut self) -> Result<(), Error> {
1459 if let Err(actual) = self.depth_tracker.push_depth() {
1460 let span = self.peek()?.span.clone();
1461 self.depth_tracker.pop_depth();
1462 return Err(Error::resource_limit_exceeded(
1463 "max_expression_depth",
1464 self.depth_tracker.max_depth().to_string(),
1465 actual.to_string(),
1466 "Simplify nested expressions or break into separate rules",
1467 Some(self.make_source(span)),
1468 None,
1469 None,
1470 ));
1471 }
1472 Ok(())
1473 }
1474
1475 fn parse_expression(&mut self) -> Result<Expression, Error> {
1476 self.check_depth()?;
1477 let result = self.parse_and_expression();
1478 self.depth_tracker.pop_depth();
1479 result
1480 }
1481
1482 fn parse_and_expression(&mut self) -> Result<Expression, Error> {
1483 let start_span = self.peek()?.span.clone();
1484 let mut left = self.parse_and_operand()?;
1485
1486 while self.at(&TokenKind::And)? {
1487 self.next()?; let right = self.parse_and_operand()?;
1489 let span = self.span_covering(
1490 &start_span,
1491 &right
1492 .source_location
1493 .as_ref()
1494 .map(|s| s.span.clone())
1495 .unwrap_or_else(|| start_span.clone()),
1496 );
1497 left = self.new_expression(
1498 ExpressionKind::LogicalAnd(Arc::new(left), Arc::new(right)),
1499 self.make_source(span),
1500 )?;
1501 }
1502
1503 Ok(left)
1504 }
1505
1506 fn parse_and_operand(&mut self) -> Result<Expression, Error> {
1507 if self.at(&TokenKind::Not)? {
1509 return self.parse_not_expression();
1510 }
1511
1512 self.parse_base_with_suffix()
1514 }
1515
1516 fn parse_not_expression(&mut self) -> Result<Expression, Error> {
1517 let not_tok = self.expect(&TokenKind::Not)?;
1518 let start_span = not_tok.span.clone();
1519
1520 self.check_depth()?;
1521 let operand = self.parse_and_operand()?;
1522 self.depth_tracker.pop_depth();
1523
1524 let end_span = operand
1525 .source_location
1526 .as_ref()
1527 .map(|s| s.span.clone())
1528 .unwrap_or_else(|| start_span.clone());
1529 let span = self.span_covering(&start_span, &end_span);
1530
1531 self.new_expression(
1532 ExpressionKind::LogicalNegation(Arc::new(operand), NegationType::Not),
1533 self.make_source(span),
1534 )
1535 }
1536
1537 fn parse_base_with_suffix(&mut self) -> Result<Expression, Error> {
1538 let start_span = self.peek()?.span.clone();
1539 let base = self.parse_base_expression()?;
1540
1541 let peeked = self.peek()?;
1543
1544 if is_comparison_operator(&peeked.kind) {
1546 return self.parse_comparison_suffix(base, start_span);
1547 }
1548
1549 if peeked.kind == TokenKind::Not {
1553 return self.parse_not_in_calendar_suffix(base, start_span);
1554 }
1555
1556 if peeked.kind == TokenKind::In {
1558 return self.parse_in_suffix(base, start_span);
1559 }
1560
1561 Ok(base)
1562 }
1563
1564 fn parse_comparison_suffix(
1565 &mut self,
1566 left: Expression,
1567 start_span: Span,
1568 ) -> Result<Expression, Error> {
1569 let operator = self.parse_comparison_operator()?;
1570
1571 let right = if self.at(&TokenKind::Not)? {
1573 self.parse_not_expression()?
1574 } else {
1575 let rhs = self.parse_base_expression()?;
1576 if self.at(&TokenKind::In)? {
1578 self.parse_in_suffix(rhs, start_span.clone())?
1579 } else {
1580 rhs
1581 }
1582 };
1583
1584 let end_span = right
1585 .source_location
1586 .as_ref()
1587 .map(|s| s.span.clone())
1588 .unwrap_or_else(|| start_span.clone());
1589 let span = self.span_covering(&start_span, &end_span);
1590
1591 self.new_expression(
1592 ExpressionKind::Comparison(Arc::new(left), operator, Arc::new(right)),
1593 self.make_source(span),
1594 )
1595 }
1596
1597 fn parse_comparison_operator(&mut self) -> Result<ComparisonComputation, Error> {
1598 let tok = self.next()?;
1599 match tok.kind {
1600 TokenKind::Gt => Ok(ComparisonComputation::GreaterThan),
1601 TokenKind::Lt => Ok(ComparisonComputation::LessThan),
1602 TokenKind::Gte => Ok(ComparisonComputation::GreaterThanOrEqual),
1603 TokenKind::Lte => Ok(ComparisonComputation::LessThanOrEqual),
1604 TokenKind::Is => {
1605 if self.at(&TokenKind::Not)? {
1607 self.next()?; Ok(ComparisonComputation::IsNot)
1609 } else {
1610 Ok(ComparisonComputation::Is)
1611 }
1612 }
1613 _ => Err(self.error_at_token(
1614 &tok,
1615 format!("Expected a comparison operator, found {}", tok.kind),
1616 )),
1617 }
1618 }
1619
1620 fn parse_not_in_calendar_suffix(
1621 &mut self,
1622 base: Expression,
1623 start_span: Span,
1624 ) -> Result<Expression, Error> {
1625 self.expect(&TokenKind::Not)?;
1626 self.expect(&TokenKind::In)?;
1627 self.expect(&TokenKind::Calendar)?;
1628 let unit = self.parse_calendar_unit()?;
1629 let end = self.peek()?.span.clone();
1630 let span = self.span_covering(&start_span, &end);
1631 self.new_expression(
1632 ExpressionKind::DateCalendar(DateCalendarKind::NotIn, unit, Arc::new(base)),
1633 self.make_source(span),
1634 )
1635 }
1636
1637 fn parse_in_suffix(&mut self, base: Expression, start_span: Span) -> Result<Expression, Error> {
1638 self.expect(&TokenKind::In)?;
1639
1640 let peeked = self.peek()?;
1641
1642 if peeked.kind == TokenKind::Past || peeked.kind == TokenKind::Future {
1644 let direction = self.next()?;
1645 let rel_kind = if direction.kind == TokenKind::Past {
1646 DateRelativeKind::InPast
1647 } else {
1648 DateRelativeKind::InFuture
1649 };
1650
1651 if self.at(&TokenKind::Calendar)? {
1653 self.next()?; let cal_kind = if direction.kind == TokenKind::Past {
1655 DateCalendarKind::Past
1656 } else {
1657 DateCalendarKind::Future
1658 };
1659 let unit = self.parse_calendar_unit()?;
1660 let end = self.peek()?.span.clone();
1661 let span = self.span_covering(&start_span, &end);
1662 return self.new_expression(
1663 ExpressionKind::DateCalendar(cal_kind, unit, Arc::new(base)),
1664 self.make_source(span),
1665 );
1666 }
1667
1668 let tolerance = if !self.at(&TokenKind::And)?
1670 && !self.at(&TokenKind::Unless)?
1671 && !self.at(&TokenKind::Then)?
1672 && !self.at(&TokenKind::Eof)?
1673 && !is_comparison_operator(&self.peek()?.kind)
1674 {
1675 let peek_kind = self.peek()?.kind.clone();
1676 if peek_kind == TokenKind::NumberLit
1677 || peek_kind == TokenKind::LParen
1678 || can_be_reference_segment(&peek_kind)
1679 || is_math_function(&peek_kind)
1680 {
1681 Some(Arc::new(self.parse_base_expression()?))
1682 } else {
1683 None
1684 }
1685 } else {
1686 None
1687 };
1688
1689 let end = self.peek()?.span.clone();
1690 let span = self.span_covering(&start_span, &end);
1691 return self.new_expression(
1692 ExpressionKind::DateRelative(rel_kind, Arc::new(base), tolerance),
1693 self.make_source(span),
1694 );
1695 }
1696
1697 if peeked.kind == TokenKind::Calendar {
1699 self.next()?; let unit = self.parse_calendar_unit()?;
1701 let end = self.peek()?.span.clone();
1702 let span = self.span_covering(&start_span, &end);
1703 return self.new_expression(
1704 ExpressionKind::DateCalendar(DateCalendarKind::Current, unit, Arc::new(base)),
1705 self.make_source(span),
1706 );
1707 }
1708
1709 let target_tok = self.next()?;
1711 let target = conversion_target_from_token(&target_tok.kind, &target_tok.text);
1712
1713 let converted = self.new_expression(
1714 ExpressionKind::UnitConversion(Arc::new(base), target),
1715 self.make_source(self.span_covering(&start_span, &target_tok.span)),
1716 )?;
1717
1718 if is_comparison_operator(&self.peek()?.kind) {
1720 return self.parse_comparison_suffix(converted, start_span);
1721 }
1722
1723 Ok(converted)
1724 }
1725
1726 fn parse_calendar_unit(&mut self) -> Result<CalendarUnit, Error> {
1727 let tok = self.next()?;
1728 if !is_calendar_unit_token(&tok.kind) {
1729 return Err(self.error_at_token(
1730 &tok,
1731 format!("Expected 'year', 'month', or 'week', found '{}'", tok.text),
1732 ));
1733 }
1734 Ok(token_kind_to_calendar_unit(&tok.kind))
1735 }
1736
1737 fn parse_base_expression(&mut self) -> Result<Expression, Error> {
1742 let start_span = self.peek()?.span.clone();
1743 let mut left = self.parse_term()?;
1744
1745 while self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
1746 let op_tok = self.next()?;
1749 let operation = match op_tok.kind {
1750 TokenKind::Plus => ArithmeticComputation::Add,
1751 TokenKind::Minus => ArithmeticComputation::Subtract,
1752 _ => unreachable!("BUG: only + and - should reach here"),
1753 };
1754
1755 let right = self.parse_term()?;
1756 let end_span = right
1757 .source_location
1758 .as_ref()
1759 .map(|s| s.span.clone())
1760 .unwrap_or_else(|| start_span.clone());
1761 let span = self.span_covering(&start_span, &end_span);
1762
1763 left = self.new_expression(
1764 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
1765 self.make_source(span),
1766 )?;
1767 }
1768
1769 Ok(left)
1770 }
1771
1772 fn parse_term(&mut self) -> Result<Expression, Error> {
1773 let start_span = self.peek()?.span.clone();
1774 let mut left = self.parse_power()?;
1775
1776 while self.at_any(&[TokenKind::Star, TokenKind::Slash, TokenKind::Percent])? {
1777 let op_tok = self.next()?;
1780 let operation = match op_tok.kind {
1781 TokenKind::Star => ArithmeticComputation::Multiply,
1782 TokenKind::Slash => ArithmeticComputation::Divide,
1783 TokenKind::Percent => ArithmeticComputation::Modulo,
1784 _ => unreachable!("BUG: only *, /, % should reach here"),
1785 };
1786
1787 let right = self.parse_power()?;
1788 let end_span = right
1789 .source_location
1790 .as_ref()
1791 .map(|s| s.span.clone())
1792 .unwrap_or_else(|| start_span.clone());
1793 let span = self.span_covering(&start_span, &end_span);
1794
1795 left = self.new_expression(
1796 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
1797 self.make_source(span),
1798 )?;
1799 }
1800
1801 Ok(left)
1802 }
1803
1804 fn parse_power(&mut self) -> Result<Expression, Error> {
1805 let start_span = self.peek()?.span.clone();
1806 let left = self.parse_factor()?;
1807
1808 if self.at(&TokenKind::Caret)? {
1809 self.next()?;
1810 self.check_depth()?;
1811 let right = self.parse_power()?;
1812 self.depth_tracker.pop_depth();
1813 let end_span = right
1814 .source_location
1815 .as_ref()
1816 .map(|s| s.span.clone())
1817 .unwrap_or_else(|| start_span.clone());
1818 let span = self.span_covering(&start_span, &end_span);
1819
1820 return self.new_expression(
1821 ExpressionKind::Arithmetic(
1822 Arc::new(left),
1823 ArithmeticComputation::Power,
1824 Arc::new(right),
1825 ),
1826 self.make_source(span),
1827 );
1828 }
1829
1830 Ok(left)
1831 }
1832
1833 fn parse_factor(&mut self) -> Result<Expression, Error> {
1834 let peeked = self.peek()?;
1835 let start_span = peeked.span.clone();
1836
1837 if peeked.kind == TokenKind::Minus {
1838 self.next()?;
1839 let operand = self.parse_primary_or_math()?;
1840 let end_span = operand
1841 .source_location
1842 .as_ref()
1843 .map(|s| s.span.clone())
1844 .unwrap_or_else(|| start_span.clone());
1845 let span = self.span_covering(&start_span, &end_span);
1846
1847 let zero = self.new_expression(
1848 ExpressionKind::Literal(Value::Number(Decimal::ZERO)),
1849 self.make_source(start_span),
1850 )?;
1851 return self.new_expression(
1852 ExpressionKind::Arithmetic(
1853 Arc::new(zero),
1854 ArithmeticComputation::Subtract,
1855 Arc::new(operand),
1856 ),
1857 self.make_source(span),
1858 );
1859 }
1860
1861 if peeked.kind == TokenKind::Plus {
1862 self.next()?;
1863 return self.parse_primary_or_math();
1864 }
1865
1866 self.parse_primary_or_math()
1867 }
1868
1869 fn parse_primary_or_math(&mut self) -> Result<Expression, Error> {
1870 let peeked = self.peek()?;
1871
1872 if is_math_function(&peeked.kind) {
1874 return self.parse_math_function();
1875 }
1876
1877 self.parse_primary()
1878 }
1879
1880 fn parse_math_function(&mut self) -> Result<Expression, Error> {
1881 let func_tok = self.next()?;
1882 let start_span = func_tok.span.clone();
1883
1884 let operator = match func_tok.kind {
1885 TokenKind::Sqrt => MathematicalComputation::Sqrt,
1886 TokenKind::Sin => MathematicalComputation::Sin,
1887 TokenKind::Cos => MathematicalComputation::Cos,
1888 TokenKind::Tan => MathematicalComputation::Tan,
1889 TokenKind::Asin => MathematicalComputation::Asin,
1890 TokenKind::Acos => MathematicalComputation::Acos,
1891 TokenKind::Atan => MathematicalComputation::Atan,
1892 TokenKind::Log => MathematicalComputation::Log,
1893 TokenKind::Exp => MathematicalComputation::Exp,
1894 TokenKind::Abs => MathematicalComputation::Abs,
1895 TokenKind::Floor => MathematicalComputation::Floor,
1896 TokenKind::Ceil => MathematicalComputation::Ceil,
1897 TokenKind::Round => MathematicalComputation::Round,
1898 _ => unreachable!("BUG: only math functions should reach here"),
1899 };
1900
1901 self.check_depth()?;
1902 let operand = self.parse_base_expression()?;
1903 self.depth_tracker.pop_depth();
1904
1905 let end_span = operand
1906 .source_location
1907 .as_ref()
1908 .map(|s| s.span.clone())
1909 .unwrap_or_else(|| start_span.clone());
1910 let span = self.span_covering(&start_span, &end_span);
1911
1912 self.new_expression(
1913 ExpressionKind::MathematicalComputation(operator, Arc::new(operand)),
1914 self.make_source(span),
1915 )
1916 }
1917
1918 fn parse_primary(&mut self) -> Result<Expression, Error> {
1919 let peeked = self.peek()?;
1920 let start_span = peeked.span.clone();
1921
1922 match &peeked.kind {
1923 TokenKind::LParen => {
1925 self.next()?; let inner = self.parse_expression()?;
1927 self.expect(&TokenKind::RParen)?;
1928 Ok(inner)
1929 }
1930
1931 TokenKind::Now => {
1933 let tok = self.next()?;
1934 self.new_expression(ExpressionKind::Now, self.make_source(tok.span))
1935 }
1936
1937 TokenKind::StringLit => {
1939 let tok = self.next()?;
1940 let content = unquote_string(&tok.text);
1941 self.new_expression(
1942 ExpressionKind::Literal(Value::Text(content)),
1943 self.make_source(tok.span),
1944 )
1945 }
1946
1947 k if is_boolean_keyword(k) => {
1949 let tok = self.next()?;
1950 self.new_expression(
1951 ExpressionKind::Literal(Value::Boolean(token_kind_to_boolean_value(&tok.kind))),
1952 self.make_source(tok.span),
1953 )
1954 }
1955
1956 TokenKind::NumberLit => self.parse_number_expression(),
1958
1959 k if can_be_reference_segment(k) => {
1961 let reference = self.parse_expression_reference()?;
1962 let end_span = self.peek()?.span.clone();
1963 let span = self.span_covering(&start_span, &end_span);
1964 self.new_expression(ExpressionKind::Reference(reference), self.make_source(span))
1965 }
1966
1967 _ => {
1968 let tok = self.next()?;
1969 Err(self.error_at_token(
1970 &tok,
1971 format!("Expected an expression, found '{}'", tok.text),
1972 ))
1973 }
1974 }
1975 }
1976
1977 fn parse_number_expression(&mut self) -> Result<Expression, Error> {
1978 let num_tok = self.next()?;
1979 let num_text = num_tok.text.clone();
1980 let start_span = num_tok.span.clone();
1981
1982 if num_text.len() == 4
1984 && num_text.chars().all(|c| c.is_ascii_digit())
1985 && self.at(&TokenKind::Minus)?
1986 {
1987 let minus_span = self.peek()?.span.clone();
1994 if minus_span.start == start_span.end {
1996 let value = self.parse_date_literal(num_text, start_span.clone())?;
1997 return self
1998 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
1999 }
2000 }
2001
2002 if num_text.len() == 2
2004 && num_text.chars().all(|c| c.is_ascii_digit())
2005 && self.at(&TokenKind::Colon)?
2006 {
2007 let colon_span = self.peek()?.span.clone();
2008 if colon_span.start == start_span.end {
2009 let value = self.try_parse_time_literal(num_text, start_span.clone())?;
2010 return self
2011 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
2012 }
2013 }
2014
2015 if self.at(&TokenKind::PercentPercent)? {
2017 let pp_tok = self.next()?;
2018 if let Ok(next_peek) = self.peek() {
2019 if next_peek.kind == TokenKind::NumberLit {
2020 return Err(self.error_at_token(
2021 &pp_tok,
2022 "Permille literal cannot be followed by a digit",
2023 ));
2024 }
2025 }
2026 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2027 let ratio_value = decimal / Decimal::from(1000);
2028 return self.new_expression(
2029 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("permille".to_string()))),
2030 self.make_source(start_span),
2031 );
2032 }
2033
2034 if self.at(&TokenKind::Percent)? {
2036 let pct_span = self.peek()?.span.clone();
2037 let pct_tok = self.next()?;
2040 if let Ok(next_peek) = self.peek() {
2041 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
2042 return Err(self.error_at_token(
2043 &pct_tok,
2044 "Percent literal cannot be followed by a digit",
2045 ));
2046 }
2047 }
2048 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2049 let ratio_value = decimal / Decimal::from(100);
2050 return self.new_expression(
2051 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("percent".to_string()))),
2052 self.make_source(self.span_covering(&start_span, &pct_span)),
2053 );
2054 }
2055
2056 if self.at(&TokenKind::PercentKw)? {
2058 self.next()?;
2059 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2060 let ratio_value = decimal / Decimal::from(100);
2061 return self.new_expression(
2062 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("percent".to_string()))),
2063 self.make_source(start_span),
2064 );
2065 }
2066
2067 if self.at(&TokenKind::Permille)? {
2069 self.next()?;
2070 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2071 let ratio_value = decimal / Decimal::from(1000);
2072 return self.new_expression(
2073 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("permille".to_string()))),
2074 self.make_source(start_span),
2075 );
2076 }
2077
2078 if is_duration_unit(&self.peek()?.kind) && self.peek()?.kind != TokenKind::PercentKw {
2080 let unit_tok = self.next()?;
2081 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2082 let duration_unit = token_kind_to_duration_unit(&unit_tok.kind);
2083 return self.new_expression(
2084 ExpressionKind::Literal(Value::Duration(decimal, duration_unit)),
2085 self.make_source(self.span_covering(&start_span, &unit_tok.span)),
2086 );
2087 }
2088
2089 if can_be_label(&self.peek()?.kind) {
2091 let unit_tok = self.next()?;
2092 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2093 return self.new_expression(
2094 ExpressionKind::UnresolvedUnitLiteral(decimal, unit_tok.text.clone()),
2095 self.make_source(self.span_covering(&start_span, &unit_tok.span)),
2096 );
2097 }
2098
2099 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
2101 self.new_expression(
2102 ExpressionKind::Literal(Value::Number(decimal)),
2103 self.make_source(start_span),
2104 )
2105 }
2106
2107 fn parse_expression_reference(&mut self) -> Result<Reference, Error> {
2108 let mut segments = Vec::new();
2109
2110 let first = self.next()?;
2111 segments.push(first.text.clone());
2112
2113 while self.at(&TokenKind::Dot)? {
2114 self.next()?; let seg = self.next()?;
2116 if !can_be_reference_segment(&seg.kind) {
2117 return Err(self.error_at_token(
2118 &seg,
2119 format!("Expected an identifier after '.', found {}", seg.kind),
2120 ));
2121 }
2122 segments.push(seg.text.clone());
2123 }
2124
2125 Ok(Reference::from_path(segments))
2126 }
2127}
2128
2129fn unquote_string(s: &str) -> String {
2134 if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
2135 s[1..s.len() - 1].to_string()
2136 } else {
2137 s.to_string()
2138 }
2139}
2140
2141fn parse_decimal_string(text: &str, span: &Span, parser: &Parser) -> Result<Decimal, Error> {
2142 let clean = text.replace(['_', ','], "");
2143 Decimal::from_str(&clean).map_err(|_| {
2144 Error::parsing(
2145 format!(
2146 "Invalid number: '{}'. Expected a valid decimal number (e.g., 42, 3.14, 1_000_000)",
2147 text
2148 ),
2149 parser.make_source(span.clone()),
2150 None::<String>,
2151 )
2152 })
2153}
2154
2155fn is_comparison_operator(kind: &TokenKind) -> bool {
2156 matches!(
2157 kind,
2158 TokenKind::Gt | TokenKind::Lt | TokenKind::Gte | TokenKind::Lte | TokenKind::Is
2159 )
2160}
2161
2162impl TokenKind {
2164 fn is_identifier_like(&self) -> bool {
2165 matches!(self, TokenKind::Identifier)
2166 || can_be_label(self)
2167 || is_type_keyword(self)
2168 || is_boolean_keyword(self)
2169 || is_duration_unit(self)
2170 || is_math_function(self)
2171 }
2172}