1use crate::error::Error;
2use crate::limits::ResourceLimits;
3use crate::parsing::ast::*;
4use crate::parsing::lexer::{
5 can_be_label, can_be_reference_segment, is_boolean_keyword, is_duration_unit, is_math_function,
6 is_spec_body_keyword, is_structural_keyword, is_type_keyword, Lexer, Token, TokenKind,
7};
8use crate::parsing::source::Source;
9use rust_decimal::Decimal;
10use std::str::FromStr;
11use std::sync::Arc;
12
13type TypeArrowChain = (String, Option<SpecRef>, Option<Vec<Constraint>>);
14
15pub struct ParseResult {
16 pub specs: Vec<LemmaSpec>,
17 pub expression_count: usize,
18}
19
20pub fn parse(
21 content: &str,
22 attribute: &str,
23 limits: &ResourceLimits,
24) -> Result<ParseResult, Error> {
25 if content.len() > limits.max_file_size_bytes {
26 return Err(Error::resource_limit_exceeded(
27 "max_file_size_bytes",
28 format!(
29 "{} bytes ({} MB)",
30 limits.max_file_size_bytes,
31 limits.max_file_size_bytes / (1024 * 1024)
32 ),
33 format!(
34 "{} bytes ({:.2} MB)",
35 content.len(),
36 content.len() as f64 / (1024.0 * 1024.0)
37 ),
38 "Reduce file size or split into multiple specs",
39 None,
40 ));
41 }
42
43 let mut parser = Parser::new(content, attribute, limits);
44 let specs = parser.parse_file()?;
45 Ok(ParseResult {
46 specs,
47 expression_count: parser.expression_count,
48 })
49}
50
51struct Parser {
52 lexer: Lexer,
53 depth_tracker: DepthTracker,
54 expression_count: usize,
55 max_expression_count: usize,
56}
57
58impl Parser {
59 fn new(content: &str, attribute: &str, limits: &ResourceLimits) -> Self {
60 Parser {
61 lexer: Lexer::new(content, attribute),
62 depth_tracker: DepthTracker::with_max_depth(limits.max_expression_depth),
63 expression_count: 0,
64 max_expression_count: limits.max_expression_count,
65 }
66 }
67
68 fn source_text(&self) -> Arc<str> {
69 self.lexer.source_text()
70 }
71
72 fn attribute(&self) -> String {
73 self.lexer.attribute().to_string()
74 }
75
76 fn peek(&mut self) -> Result<&Token, Error> {
77 self.lexer.peek()
78 }
79
80 fn next(&mut self) -> Result<Token, Error> {
81 self.lexer.next_token()
82 }
83
84 fn at(&mut self, kind: &TokenKind) -> Result<bool, Error> {
85 Ok(&self.peek()?.kind == kind)
86 }
87
88 fn at_any(&mut self, kinds: &[TokenKind]) -> Result<bool, Error> {
89 let current = &self.peek()?.kind;
90 Ok(kinds.contains(current))
91 }
92
93 fn expect(&mut self, kind: &TokenKind) -> Result<Token, Error> {
94 let token = self.next()?;
95 if &token.kind == kind {
96 Ok(token)
97 } else {
98 Err(self.error_at_token(&token, format!("Expected {}, found {}", kind, token.kind)))
99 }
100 }
101
102 fn error_at_token(&self, token: &Token, message: impl Into<String>) -> Error {
103 Error::parsing(
104 message,
105 Source::new(
106 self.lexer.attribute(),
107 token.span.clone(),
108 self.source_text(),
109 ),
110 None::<String>,
111 )
112 }
113
114 fn error_at_token_with_suggestion(
115 &self,
116 token: &Token,
117 message: impl Into<String>,
118 suggestion: impl Into<String>,
119 ) -> Error {
120 Error::parsing(
121 message,
122 Source::new(
123 self.lexer.attribute(),
124 token.span.clone(),
125 self.source_text(),
126 ),
127 Some(suggestion),
128 )
129 }
130
131 fn parse_content_hash(&mut self) -> Result<String, Error> {
135 let hash_span = self.lexer.current_span();
136 let hash = self.lexer.scan_raw_alphanumeric()?;
137 if hash.len() != 8 {
138 return Err(Error::parsing(
139 format!(
140 "Expected an 8-character alphanumeric content hash after '~', found '{}'",
141 hash
142 ),
143 self.make_source(hash_span),
144 None::<String>,
145 ));
146 }
147 Ok(hash)
148 }
149
150 fn make_source(&self, span: Span) -> Source {
151 Source::new(self.lexer.attribute(), span, self.source_text())
152 }
153
154 fn span_from(&self, start: &Span) -> Span {
155 Span {
158 start: start.start,
159 end: start.end.max(start.start),
160 line: start.line,
161 col: start.col,
162 }
163 }
164
165 fn span_covering(&self, start: &Span, end: &Span) -> Span {
166 Span {
167 start: start.start,
168 end: end.end,
169 line: start.line,
170 col: start.col,
171 }
172 }
173
174 fn parse_file(&mut self) -> Result<Vec<LemmaSpec>, Error> {
179 let mut specs = Vec::new();
180 loop {
181 if self.at(&TokenKind::Eof)? {
182 break;
183 }
184 if self.at(&TokenKind::Spec)? {
185 specs.push(self.parse_spec()?);
186 } else {
187 let token = self.next()?;
188 return Err(self.error_at_token_with_suggestion(
189 &token,
190 format!(
191 "Expected a spec declaration (e.g. 'spec my_spec'), found {}",
192 token.kind
193 ),
194 "A Lemma file must start with 'spec <name>'",
195 ));
196 }
197 }
198 Ok(specs)
199 }
200
201 fn parse_spec(&mut self) -> Result<LemmaSpec, Error> {
202 let spec_token = self.expect(&TokenKind::Spec)?;
203 let start_line = spec_token.span.line;
204
205 let (name, _name_span) = self.parse_spec_name()?;
206
207 let effective_from = self.try_parse_effective_from()?;
208
209 let commentary = self.try_parse_commentary()?;
210
211 let attribute = self.attribute();
212 let mut spec = LemmaSpec::new(name.clone())
213 .with_attribute(attribute)
214 .with_start_line(start_line);
215 spec.effective_from = effective_from;
216
217 if let Some(commentary_text) = commentary {
218 spec = spec.set_commentary(commentary_text);
219 }
220
221 let mut facts = Vec::new();
225 let mut rules = Vec::new();
226 let mut types = Vec::new();
227 let mut meta_fields = Vec::new();
228
229 loop {
230 let peek_kind = self.peek()?.kind.clone();
231 match peek_kind {
232 TokenKind::Fact => {
233 let fact = self.parse_fact()?;
234 facts.push(fact);
235 }
236 TokenKind::Rule => {
237 let rule = self.parse_rule()?;
238 rules.push(rule);
239 }
240 TokenKind::Type => {
241 let type_def = self.parse_type_def()?;
242 types.push(type_def);
243 }
244 TokenKind::Meta => {
245 let meta = self.parse_meta()?;
246 meta_fields.push(meta);
247 }
248 TokenKind::Spec | TokenKind::Eof => break,
249 _ => {
250 let token = self.next()?;
251 return Err(self.error_at_token_with_suggestion(
252 &token,
253 format!(
254 "Expected 'fact', 'rule', 'type', 'meta', or a new 'spec', found '{}'",
255 token.text
256 ),
257 "Check the spelling or add the appropriate keyword",
258 ));
259 }
260 }
261 }
262
263 for type_def in types {
264 spec = spec.add_type(type_def);
265 }
266 for fact in facts {
267 spec = spec.add_fact(fact);
268 }
269 for rule in rules {
270 spec = spec.add_rule(rule);
271 }
272 for meta in meta_fields {
273 spec = spec.add_meta_field(meta);
274 }
275
276 Ok(spec)
277 }
278
279 fn parse_spec_name(&mut self) -> Result<(String, Span), Error> {
282 let mut name = String::new();
283 let start_span;
284
285 if self.at(&TokenKind::At)? {
286 let at_tok = self.next()?;
287 start_span = at_tok.span.clone();
288 name.push('@');
289 } else {
290 start_span = self.peek()?.span.clone();
291 }
292
293 let first = self.next()?;
295 if !first.kind.is_identifier_like() {
296 return Err(self.error_at_token(
297 &first,
298 format!("Expected a spec name, found {}", first.kind),
299 ));
300 }
301 name.push_str(&first.text);
302 let mut end_span = first.span.clone();
303
304 while self.at(&TokenKind::Slash)? {
306 self.next()?; name.push('/');
308 let seg = self.next()?;
309 if !seg.kind.is_identifier_like() {
310 return Err(self.error_at_token(
311 &seg,
312 format!(
313 "Expected identifier after '/' in spec name, found {}",
314 seg.kind
315 ),
316 ));
317 }
318 name.push_str(&seg.text);
319 end_span = seg.span.clone();
320 }
321
322 while self.at(&TokenKind::Minus)? {
324 let minus_span = self.peek()?.span.clone();
327 self.next()?; if let Ok(peeked) = self.peek() {
329 if peeked.kind.is_identifier_like() {
330 let seg = self.next()?;
331 name.push('-');
332 name.push_str(&seg.text);
333 end_span = seg.span.clone();
334 while self.at(&TokenKind::Slash)? {
336 self.next()?; name.push('/');
338 let seg2 = self.next()?;
339 if !seg2.kind.is_identifier_like() {
340 return Err(self.error_at_token(
341 &seg2,
342 format!(
343 "Expected identifier after '/' in spec name, found {}",
344 seg2.kind
345 ),
346 ));
347 }
348 name.push_str(&seg2.text);
349 end_span = seg2.span.clone();
350 }
351 } else {
352 let span = self.span_covering(&start_span, &minus_span);
354 return Err(Error::parsing(
355 "Trailing '-' after spec name",
356 self.make_source(span),
357 None::<String>,
358 ));
359 }
360 }
361 }
362
363 let full_span = self.span_covering(&start_span, &end_span);
364 Ok((name, full_span))
365 }
366
367 fn try_parse_effective_from(&mut self) -> Result<Option<DateTimeValue>, Error> {
368 if !self.at(&TokenKind::NumberLit)? {
373 return Ok(None);
374 }
375
376 let peeked = self.peek()?;
377 let peeked_text = peeked.text.clone();
378 let peeked_span = peeked.span.clone();
379
380 if peeked_text.len() == 4 && peeked_text.chars().all(|c| c.is_ascii_digit()) {
382 let mut dt_str = String::new();
384 let num_tok = self.next()?; dt_str.push_str(&num_tok.text);
386
387 while self.at(&TokenKind::Minus)? {
389 self.next()?; dt_str.push('-');
391 let part = self.next()?;
392 dt_str.push_str(&part.text);
393 }
394
395 if self.at(&TokenKind::Identifier)? {
397 let peeked = self.peek()?;
398 if peeked.text.starts_with('T') || peeked.text.starts_with('t') {
399 let time_part = self.next()?;
400 dt_str.push_str(&time_part.text);
401 while self.at(&TokenKind::Colon)? {
403 self.next()?;
404 dt_str.push(':');
405 let part = self.next()?;
406 dt_str.push_str(&part.text);
407 }
408 if self.at(&TokenKind::Plus)? {
410 self.next()?;
411 dt_str.push('+');
412 let tz_part = self.next()?;
413 dt_str.push_str(&tz_part.text);
414 if self.at(&TokenKind::Colon)? {
415 self.next()?;
416 dt_str.push(':');
417 let tz_min = self.next()?;
418 dt_str.push_str(&tz_min.text);
419 }
420 }
421 }
422 }
423
424 if let Some(dtv) = DateTimeValue::parse(&dt_str) {
426 return Ok(Some(dtv));
427 }
428
429 return Err(Error::parsing(
430 format!("Invalid date/time in spec declaration: '{}'", dt_str),
431 self.make_source(peeked_span),
432 None::<String>,
433 ));
434 }
435
436 Ok(None)
437 }
438
439 fn try_parse_commentary(&mut self) -> Result<Option<String>, Error> {
440 if !self.at(&TokenKind::Commentary)? {
441 return Ok(None);
442 }
443 let token = self.next()?;
444 let trimmed = token.text.trim().to_string();
445 if trimmed.is_empty() {
446 Ok(None)
447 } else {
448 Ok(Some(trimmed))
449 }
450 }
451
452 fn parse_fact(&mut self) -> Result<LemmaFact, Error> {
457 let fact_token = self.expect(&TokenKind::Fact)?;
458 let start_span = fact_token.span.clone();
459
460 let reference = self.parse_reference()?;
462
463 self.expect(&TokenKind::Colon)?;
464
465 let value = self.parse_fact_value()?;
466
467 let end_span = self.peek()?.span.clone();
468 let span = self.span_covering(&start_span, &end_span);
469 let source = self.make_source(span);
470
471 Ok(LemmaFact::new(reference, value, source))
472 }
473
474 fn parse_reference(&mut self) -> Result<Reference, Error> {
475 let mut segments = Vec::new();
476
477 let first = self.next()?;
478 if is_structural_keyword(&first.kind) {
481 return Err(self.error_at_token_with_suggestion(
482 &first,
483 format!(
484 "'{}' is a reserved keyword and cannot be used as a name",
485 first.text
486 ),
487 "Choose a different name that is not a reserved keyword",
488 ));
489 }
490
491 if !can_be_reference_segment(&first.kind) {
492 return Err(self.error_at_token(
493 &first,
494 format!("Expected an identifier, found {}", first.kind),
495 ));
496 }
497
498 segments.push(first.text.clone());
499
500 while self.at(&TokenKind::Dot)? {
502 self.next()?; let seg = self.next()?;
504 if !can_be_reference_segment(&seg.kind) {
505 return Err(self.error_at_token(
506 &seg,
507 format!("Expected an identifier after '.', found {}", seg.kind),
508 ));
509 }
510 segments.push(seg.text.clone());
511 }
512
513 Ok(Reference::from_path(segments))
514 }
515
516 fn parse_fact_value(&mut self) -> Result<FactValue, Error> {
517 if self.at(&TokenKind::LBracket)? {
519 return self.parse_type_declaration_or_inline();
520 }
521
522 if self.at(&TokenKind::Spec)? {
524 return self.parse_fact_spec_reference();
525 }
526
527 let value = self.parse_literal_value()?;
529 Ok(FactValue::Literal(value))
530 }
531
532 fn parse_type_declaration_or_inline(&mut self) -> Result<FactValue, Error> {
533 self.expect(&TokenKind::LBracket)?;
534
535 let (base_name, from_spec, constraints) = self.parse_type_arrow_chain()?;
537
538 self.expect(&TokenKind::RBracket)?;
539
540 Ok(FactValue::TypeDeclaration {
541 base: base_name,
542 constraints,
543 from: from_spec,
544 })
545 }
546
547 fn parse_fact_spec_reference(&mut self) -> Result<FactValue, Error> {
548 self.expect(&TokenKind::Spec)?;
549
550 let (name, _name_span) = self.parse_spec_name()?;
551 let is_registry = name.starts_with('@');
552
553 let mut hash_pin = None;
554 if self.at(&TokenKind::Tilde)? {
555 self.next()?; hash_pin = Some(self.parse_content_hash()?);
557 }
558
559 let mut effective = None;
560 if self.at(&TokenKind::NumberLit)? {
562 let peeked = self.peek()?;
563 if peeked.text.len() == 4 && peeked.text.chars().all(|c| c.is_ascii_digit()) {
564 effective = self.try_parse_effective_from()?;
566 }
567 }
568
569 Ok(FactValue::SpecReference(SpecRef {
570 name,
571 is_registry,
572 hash_pin,
573 effective,
574 }))
575 }
576
577 fn parse_rule(&mut self) -> Result<LemmaRule, Error> {
582 let rule_token = self.expect(&TokenKind::Rule)?;
583 let start_span = rule_token.span.clone();
584
585 let name_tok = self.next()?;
586 if is_structural_keyword(&name_tok.kind) {
587 return Err(self.error_at_token_with_suggestion(
588 &name_tok,
589 format!(
590 "'{}' is a reserved keyword and cannot be used as a rule name",
591 name_tok.text
592 ),
593 "Choose a different name that is not a reserved keyword",
594 ));
595 }
596 if !can_be_label(&name_tok.kind) && !is_type_keyword(&name_tok.kind) {
597 return Err(self.error_at_token(
598 &name_tok,
599 format!("Expected a rule name, found {}", name_tok.kind),
600 ));
601 }
602 let rule_name = name_tok.text.clone();
603
604 self.expect(&TokenKind::Colon)?;
605
606 let expression = if self.at(&TokenKind::Veto)? {
608 self.parse_veto_expression()?
609 } else {
610 self.parse_expression()?
611 };
612
613 let mut unless_clauses = Vec::new();
615 while self.at(&TokenKind::Unless)? {
616 unless_clauses.push(self.parse_unless_clause()?);
617 }
618
619 let end_span = if let Some(last_unless) = unless_clauses.last() {
620 last_unless.source_location.span.clone()
621 } else if let Some(ref loc) = expression.source_location {
622 loc.span.clone()
623 } else {
624 start_span.clone()
625 };
626
627 let span = self.span_covering(&start_span, &end_span);
628 Ok(LemmaRule {
629 name: rule_name,
630 expression,
631 unless_clauses,
632 source_location: self.make_source(span),
633 })
634 }
635
636 fn parse_veto_expression(&mut self) -> Result<Expression, Error> {
637 let veto_tok = self.expect(&TokenKind::Veto)?;
638 let start_span = veto_tok.span.clone();
639
640 let message = if self.at(&TokenKind::StringLit)? {
641 let str_tok = self.next()?;
642 let content = unquote_string(&str_tok.text);
643 Some(content)
644 } else {
645 None
646 };
647
648 let span = self.span_from(&start_span);
649 self.new_expression(
650 ExpressionKind::Veto(VetoExpression { message }),
651 self.make_source(span),
652 )
653 }
654
655 fn parse_unless_clause(&mut self) -> Result<UnlessClause, Error> {
656 let unless_tok = self.expect(&TokenKind::Unless)?;
657 let start_span = unless_tok.span.clone();
658
659 let condition = self.parse_expression()?;
660
661 self.expect(&TokenKind::Then)?;
662
663 let result = if self.at(&TokenKind::Veto)? {
664 self.parse_veto_expression()?
665 } else {
666 self.parse_expression()?
667 };
668
669 let end_span = result
670 .source_location
671 .as_ref()
672 .map(|s| s.span.clone())
673 .unwrap_or_else(|| start_span.clone());
674 let span = self.span_covering(&start_span, &end_span);
675
676 Ok(UnlessClause {
677 condition,
678 result,
679 source_location: self.make_source(span),
680 })
681 }
682
683 fn parse_type_def(&mut self) -> Result<TypeDef, Error> {
688 let type_tok = self.expect(&TokenKind::Type)?;
689 let start_span = type_tok.span.clone();
690
691 let name_tok = self.next()?;
693 let type_name = name_tok.text.clone();
694
695 if self.at(&TokenKind::From)? {
697 return self.parse_type_import(type_name, start_span);
698 }
699
700 if self.at(&TokenKind::Colon)? {
702 self.next()?; } else {
704 let peek = self.peek()?.clone();
707 return Err(self.error_at_token(
708 &peek,
709 format!(
710 "Expected ':' or 'from' after type name '{}', found {}",
711 type_name, peek.kind
712 ),
713 ));
714 }
715
716 let (parent, _from, constraints) = self.parse_type_arrow_chain()?;
717
718 let end_span = self.peek()?.span.clone();
719 let span = self.span_covering(&start_span, &end_span);
720 Ok(TypeDef::Regular {
721 source_location: self.make_source(span),
722 name: type_name,
723 parent,
724 constraints,
725 })
726 }
727
728 fn parse_type_import(&mut self, type_name: String, start_span: Span) -> Result<TypeDef, Error> {
729 self.expect(&TokenKind::From)?;
730
731 let (from_name, _from_span) = self.parse_spec_name()?;
732 let is_registry = from_name.starts_with('@');
733
734 let mut hash_pin = None;
735 if self.at(&TokenKind::Identifier)? || self.at(&TokenKind::NumberLit)? {
737 let peeked = self.peek()?;
738 let peeked_text = peeked.text.clone();
739 if peeked_text.len() == 8 && peeked_text.chars().all(|c| c.is_ascii_alphanumeric()) {
740 let hash_tok = self.next()?;
741 hash_pin = Some(hash_tok.text.clone());
742 }
743 }
744
745 let from = SpecRef {
746 name: from_name,
747 is_registry,
748 hash_pin,
749 effective: None,
750 };
751
752 let constraints = if self.at(&TokenKind::Arrow)? {
754 let (_, _, constraints) = self.parse_remaining_arrow_chain()?;
755 constraints
756 } else {
757 None
758 };
759
760 let end_span = self.peek()?.span.clone();
761 let span = self.span_covering(&start_span, &end_span);
762
763 let source_type = type_name.clone();
764
765 Ok(TypeDef::Import {
766 source_location: self.make_source(span),
767 name: type_name,
768 source_type,
769 from,
770 constraints,
771 })
772 }
773
774 fn parse_type_arrow_chain(&mut self) -> Result<TypeArrowChain, Error> {
776 let name_tok = self.next()?;
778 let base_name = if is_type_keyword(&name_tok.kind) {
779 name_tok.text.to_lowercase()
780 } else if can_be_label(&name_tok.kind) {
781 name_tok.text.clone()
782 } else {
783 return Err(self.error_at_token(
784 &name_tok,
785 format!("Expected a type name, found {}", name_tok.kind),
786 ));
787 };
788
789 let from_spec = if self.at(&TokenKind::From)? {
791 self.next()?; let (from_name, _) = self.parse_spec_name()?;
793 let is_registry = from_name.starts_with('@');
794 let mut hash_pin = None;
795 if self.at(&TokenKind::Identifier)? || self.at(&TokenKind::NumberLit)? {
797 let peeked = self.peek()?;
798 let peeked_text = peeked.text.clone();
799 if peeked_text.len() == 8 && peeked_text.chars().all(|c| c.is_ascii_alphanumeric())
800 {
801 let hash_tok = self.next()?;
802 hash_pin = Some(hash_tok.text.clone());
803 }
804 }
805 Some(SpecRef {
806 name: from_name,
807 is_registry,
808 hash_pin,
809 effective: None,
810 })
811 } else {
812 None
813 };
814
815 let mut commands = Vec::new();
817 while self.at(&TokenKind::Arrow)? {
818 self.next()?; let (cmd_name, cmd_args) = self.parse_command()?;
820 commands.push((cmd_name, cmd_args));
821 }
822
823 let constraints = if commands.is_empty() {
824 None
825 } else {
826 Some(commands)
827 };
828
829 Ok((base_name, from_spec, constraints))
830 }
831
832 fn parse_remaining_arrow_chain(&mut self) -> Result<TypeArrowChain, Error> {
833 let mut commands = Vec::new();
834 while self.at(&TokenKind::Arrow)? {
835 self.next()?; let (cmd_name, cmd_args) = self.parse_command()?;
837 commands.push((cmd_name, cmd_args));
838 }
839 let constraints = if commands.is_empty() {
840 None
841 } else {
842 Some(commands)
843 };
844 Ok((String::new(), None, constraints))
845 }
846
847 fn parse_command(&mut self) -> Result<(String, Vec<CommandArg>), Error> {
848 let name_tok = self.next()?;
849 if !can_be_label(&name_tok.kind) && !is_type_keyword(&name_tok.kind) {
850 return Err(self.error_at_token(
851 &name_tok,
852 format!("Expected a command name, found {}", name_tok.kind),
853 ));
854 }
855 let cmd_name = name_tok.text.clone();
856
857 let mut args = Vec::new();
858 loop {
859 if self.at(&TokenKind::Arrow)?
862 || self.at(&TokenKind::RBracket)?
863 || self.at(&TokenKind::Eof)?
864 || is_spec_body_keyword(&self.peek()?.kind)
865 || self.at(&TokenKind::Spec)?
866 {
867 break;
868 }
869
870 let peek_kind = self.peek()?.kind.clone();
871 match peek_kind {
872 TokenKind::NumberLit => {
873 let tok = self.next()?;
874 args.push(CommandArg::Number(tok.text));
875 }
876 TokenKind::Minus | TokenKind::Plus => {
877 let second = self.lexer.peek_second()?.kind.clone();
878 if second == TokenKind::NumberLit {
879 let sign = self.next()?;
880 let num = self.next()?;
881 let text = format!("{}{}", sign.text, num.text);
882 args.push(CommandArg::Number(text));
883 } else {
884 break;
885 }
886 }
887 TokenKind::StringLit => {
888 let tok = self.next()?;
889 let content = unquote_string(&tok.text);
890 args.push(CommandArg::Text(content));
891 }
892 ref k if is_boolean_keyword(k) => {
893 let tok = self.next()?;
894 args.push(CommandArg::Boolean(tok.text));
895 }
896 ref k if can_be_label(k) || is_type_keyword(k) => {
897 let tok = self.next()?;
898 args.push(CommandArg::Label(tok.text));
899 }
900 _ => break,
901 }
902 }
903
904 Ok((cmd_name, args))
905 }
906
907 fn parse_meta(&mut self) -> Result<MetaField, Error> {
912 let meta_tok = self.expect(&TokenKind::Meta)?;
913 let start_span = meta_tok.span.clone();
914
915 let key_tok = self.next()?;
916 let key = key_tok.text.clone();
917
918 self.expect(&TokenKind::Colon)?;
919
920 let value = self.parse_meta_value()?;
921
922 let end_span = self.peek()?.span.clone();
923 let span = self.span_covering(&start_span, &end_span);
924
925 Ok(MetaField {
926 key,
927 value,
928 source_location: self.make_source(span),
929 })
930 }
931
932 fn parse_meta_value(&mut self) -> Result<MetaValue, Error> {
933 let peeked = self.peek()?;
935 match &peeked.kind {
936 TokenKind::StringLit => {
937 let value = self.parse_literal_value()?;
938 return Ok(MetaValue::Literal(value));
939 }
940 TokenKind::NumberLit => {
941 let value = self.parse_literal_value()?;
942 return Ok(MetaValue::Literal(value));
943 }
944 k if is_boolean_keyword(k) => {
945 let value = self.parse_literal_value()?;
946 return Ok(MetaValue::Literal(value));
947 }
948 _ => {}
949 }
950
951 let mut ident = String::new();
954 loop {
955 let peeked = self.peek()?;
956 match &peeked.kind {
957 k if k.is_identifier_like() => {
958 let tok = self.next()?;
959 ident.push_str(&tok.text);
960 }
961 TokenKind::Dot => {
962 self.next()?;
963 ident.push('.');
964 }
965 TokenKind::Slash => {
966 self.next()?;
967 ident.push('/');
968 }
969 TokenKind::Minus => {
970 self.next()?;
971 ident.push('-');
972 }
973 TokenKind::NumberLit => {
974 let tok = self.next()?;
975 ident.push_str(&tok.text);
976 }
977 _ => break,
978 }
979 }
980
981 if ident.is_empty() {
982 let tok = self.peek()?.clone();
983 return Err(self.error_at_token(&tok, "Expected a meta value"));
984 }
985
986 Ok(MetaValue::Unquoted(ident))
987 }
988
989 fn parse_literal_value(&mut self) -> Result<Value, Error> {
994 let peeked = self.peek()?;
995 match &peeked.kind {
996 TokenKind::StringLit => {
997 let tok = self.next()?;
998 let content = unquote_string(&tok.text);
999 Ok(Value::Text(content))
1000 }
1001 k if is_boolean_keyword(k) => {
1002 let tok = self.next()?;
1003 let bv = parse_boolean_value(&tok.text);
1004 Ok(Value::Boolean(bv))
1005 }
1006 TokenKind::NumberLit => self.parse_number_literal(),
1007 TokenKind::Minus | TokenKind::Plus => self.parse_signed_number_literal(),
1008 _ => {
1009 let tok = self.next()?;
1010 Err(self.error_at_token(
1011 &tok,
1012 format!(
1013 "Expected a value (number, text, boolean, date, etc.), found '{}'",
1014 tok.text
1015 ),
1016 ))
1017 }
1018 }
1019 }
1020
1021 fn parse_signed_number_literal(&mut self) -> Result<Value, Error> {
1022 let sign_tok = self.next()?;
1023 let sign_span = sign_tok.span.clone();
1024 let is_negative = sign_tok.kind == TokenKind::Minus;
1025
1026 if !self.at(&TokenKind::NumberLit)? {
1027 let tok = self.peek()?.clone();
1028 return Err(self.error_at_token(
1029 &tok,
1030 format!(
1031 "Expected a number after '{}', found '{}'",
1032 sign_tok.text, tok.text
1033 ),
1034 ));
1035 }
1036
1037 let value = self.parse_number_literal()?;
1038 if !is_negative {
1039 return Ok(value);
1040 }
1041 match value {
1042 Value::Number(d) => Ok(Value::Number(-d)),
1043 Value::Scale(d, unit) => Ok(Value::Scale(-d, unit)),
1044 Value::Duration(d, unit) => Ok(Value::Duration(-d, unit)),
1045 Value::Ratio(d, label) => Ok(Value::Ratio(-d, label)),
1046 other => Err(Error::parsing(
1047 format!("Cannot negate this value: {}", other),
1048 self.make_source(sign_span),
1049 None::<String>,
1050 )),
1051 }
1052 }
1053
1054 fn parse_number_literal(&mut self) -> Result<Value, Error> {
1055 let num_tok = self.next()?;
1056 let num_text = &num_tok.text;
1057 let num_span = num_tok.span.clone();
1058
1059 if num_text.len() == 4
1061 && num_text.chars().all(|c| c.is_ascii_digit())
1062 && self.at(&TokenKind::Minus)?
1063 {
1064 return self.parse_date_literal(num_text.clone(), num_span);
1065 }
1066
1067 let peeked = self.peek()?;
1069
1070 if num_text.len() == 2
1072 && num_text.chars().all(|c| c.is_ascii_digit())
1073 && peeked.kind == TokenKind::Colon
1074 {
1075 return self.try_parse_time_literal(num_text.clone(), num_span);
1082 }
1083
1084 if peeked.kind == TokenKind::PercentPercent {
1086 let pp_tok = self.next()?;
1087 if let Ok(next_peek) = self.peek() {
1089 if next_peek.kind == TokenKind::NumberLit {
1090 return Err(self.error_at_token(
1091 &pp_tok,
1092 "Permille literal cannot be followed by a digit",
1093 ));
1094 }
1095 }
1096 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1097 let ratio_value = decimal / Decimal::from(1000);
1098 return Ok(Value::Ratio(ratio_value, Some("permille".to_string())));
1099 }
1100
1101 if peeked.kind == TokenKind::Percent {
1103 let pct_tok = self.next()?;
1104 if let Ok(next_peek) = self.peek() {
1106 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1107 return Err(self.error_at_token(
1108 &pct_tok,
1109 "Percent literal cannot be followed by a digit",
1110 ));
1111 }
1112 }
1113 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1114 let ratio_value = decimal / Decimal::from(100);
1115 return Ok(Value::Ratio(ratio_value, Some("percent".to_string())));
1116 }
1117
1118 if peeked.kind == TokenKind::PercentKw {
1120 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1122 let ratio_value = decimal / Decimal::from(100);
1123 return Ok(Value::Ratio(ratio_value, Some("percent".to_string())));
1124 }
1125
1126 if peeked.kind == TokenKind::Permille {
1128 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1130 let ratio_value = decimal / Decimal::from(1000);
1131 return Ok(Value::Ratio(ratio_value, Some("permille".to_string())));
1132 }
1133
1134 if is_duration_unit(&peeked.kind) && peeked.kind != TokenKind::PercentKw {
1136 let unit_tok = self.next()?;
1137 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1138 let duration_unit = parse_duration_unit_from_text(&unit_tok.text);
1139 return Ok(Value::Duration(decimal, duration_unit));
1140 }
1141
1142 if can_be_label(&peeked.kind) {
1144 let unit_tok = self.next()?;
1145 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1146 return Ok(Value::Scale(decimal, unit_tok.text.clone()));
1147 }
1148
1149 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1151 Ok(Value::Number(decimal))
1152 }
1153
1154 fn parse_date_literal(&mut self, year_text: String, start_span: Span) -> Result<Value, Error> {
1155 let mut dt_str = year_text;
1156
1157 self.expect(&TokenKind::Minus)?;
1159 dt_str.push('-');
1160 let month_tok = self.expect(&TokenKind::NumberLit)?;
1161 dt_str.push_str(&month_tok.text);
1162
1163 self.expect(&TokenKind::Minus)?;
1165 dt_str.push('-');
1166 let day_tok = self.expect(&TokenKind::NumberLit)?;
1167 dt_str.push_str(&day_tok.text);
1168
1169 if self.at(&TokenKind::Identifier)? {
1171 let peeked = self.peek()?;
1172 if peeked.text.len() >= 2
1173 && (peeked.text.starts_with('T') || peeked.text.starts_with('t'))
1174 {
1175 let t_tok = self.next()?;
1177 dt_str.push_str(&t_tok.text);
1178
1179 if self.at(&TokenKind::Colon)? {
1181 self.next()?;
1182 dt_str.push(':');
1183 let min_tok = self.next()?;
1184 dt_str.push_str(&min_tok.text);
1185
1186 if self.at(&TokenKind::Colon)? {
1188 self.next()?;
1189 dt_str.push(':');
1190 let sec_tok = self.next()?;
1191 dt_str.push_str(&sec_tok.text);
1192
1193 if self.at(&TokenKind::Dot)? {
1195 self.next()?;
1196 dt_str.push('.');
1197 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1198 dt_str.push_str(&frac_tok.text);
1199 }
1200 }
1201 }
1202
1203 self.try_consume_timezone(&mut dt_str)?;
1205 }
1206 }
1207
1208 if let Some(dtv) = crate::parsing::literals::parse_datetime_str(&dt_str) {
1209 return Ok(Value::Date(dtv));
1210 }
1211
1212 Err(Error::parsing(
1213 format!("Invalid date/time format: '{}'", dt_str),
1214 self.make_source(start_span),
1215 None::<String>,
1216 ))
1217 }
1218
1219 fn try_consume_timezone(&mut self, dt_str: &mut String) -> Result<(), Error> {
1220 if self.at(&TokenKind::Identifier)? {
1222 let peeked = self.peek()?;
1223 if peeked.text == "Z" || peeked.text == "z" {
1224 let z_tok = self.next()?;
1225 dt_str.push_str(&z_tok.text);
1226 return Ok(());
1227 }
1228 }
1229
1230 if self.at(&TokenKind::Plus)? || self.at(&TokenKind::Minus)? {
1232 let sign_tok = self.next()?;
1233 dt_str.push_str(&sign_tok.text);
1234 let hour_tok = self.expect(&TokenKind::NumberLit)?;
1235 dt_str.push_str(&hour_tok.text);
1236 if self.at(&TokenKind::Colon)? {
1237 self.next()?;
1238 dt_str.push(':');
1239 let min_tok = self.expect(&TokenKind::NumberLit)?;
1240 dt_str.push_str(&min_tok.text);
1241 }
1242 }
1243
1244 Ok(())
1245 }
1246
1247 fn try_parse_time_literal(
1248 &mut self,
1249 hour_text: String,
1250 start_span: Span,
1251 ) -> Result<Value, Error> {
1252 let mut time_str = hour_text;
1253
1254 self.expect(&TokenKind::Colon)?;
1256 time_str.push(':');
1257 let min_tok = self.expect(&TokenKind::NumberLit)?;
1258 time_str.push_str(&min_tok.text);
1259
1260 if self.at(&TokenKind::Colon)? {
1262 self.next()?;
1263 time_str.push(':');
1264 let sec_tok = self.expect(&TokenKind::NumberLit)?;
1265 time_str.push_str(&sec_tok.text);
1266 }
1267
1268 self.try_consume_timezone(&mut time_str)?;
1270
1271 if let Ok(t) = time_str.parse::<chrono::NaiveTime>() {
1272 use chrono::Timelike;
1273 return Ok(Value::Time(TimeValue {
1274 hour: t.hour() as u8,
1275 minute: t.minute() as u8,
1276 second: t.second() as u8,
1277 timezone: None,
1278 }));
1279 }
1280
1281 Err(Error::parsing(
1282 format!("Invalid time format: '{}'", time_str),
1283 self.make_source(start_span),
1284 None::<String>,
1285 ))
1286 }
1287
1288 fn new_expression(
1293 &mut self,
1294 kind: ExpressionKind,
1295 source: Source,
1296 ) -> Result<Expression, Error> {
1297 self.expression_count += 1;
1298 if self.expression_count > self.max_expression_count {
1299 return Err(Error::resource_limit_exceeded(
1300 "max_expression_count",
1301 self.max_expression_count.to_string(),
1302 self.expression_count.to_string(),
1303 "Split logic into multiple rules to reduce expression count",
1304 Some(source),
1305 ));
1306 }
1307 Ok(Expression::new(kind, source))
1308 }
1309
1310 fn check_depth(&mut self) -> Result<(), Error> {
1311 if let Err(actual) = self.depth_tracker.push_depth() {
1312 let span = self.peek()?.span.clone();
1313 self.depth_tracker.pop_depth();
1314 return Err(Error::resource_limit_exceeded(
1315 "max_expression_depth",
1316 self.depth_tracker.max_depth().to_string(),
1317 actual.to_string(),
1318 "Simplify nested expressions or break into separate rules",
1319 Some(self.make_source(span)),
1320 ));
1321 }
1322 Ok(())
1323 }
1324
1325 fn parse_expression(&mut self) -> Result<Expression, Error> {
1326 self.check_depth()?;
1327 let result = self.parse_and_expression();
1328 self.depth_tracker.pop_depth();
1329 result
1330 }
1331
1332 fn parse_and_expression(&mut self) -> Result<Expression, Error> {
1333 let start_span = self.peek()?.span.clone();
1334 let mut left = self.parse_and_operand()?;
1335
1336 while self.at(&TokenKind::And)? {
1337 self.next()?; let right = self.parse_and_operand()?;
1339 let span = self.span_covering(
1340 &start_span,
1341 &right
1342 .source_location
1343 .as_ref()
1344 .map(|s| s.span.clone())
1345 .unwrap_or_else(|| start_span.clone()),
1346 );
1347 left = self.new_expression(
1348 ExpressionKind::LogicalAnd(Arc::new(left), Arc::new(right)),
1349 self.make_source(span),
1350 )?;
1351 }
1352
1353 Ok(left)
1354 }
1355
1356 fn parse_and_operand(&mut self) -> Result<Expression, Error> {
1357 if self.at(&TokenKind::Not)? {
1359 return self.parse_not_expression();
1360 }
1361
1362 self.parse_base_with_suffix()
1364 }
1365
1366 fn parse_not_expression(&mut self) -> Result<Expression, Error> {
1367 let not_tok = self.expect(&TokenKind::Not)?;
1368 let start_span = not_tok.span.clone();
1369
1370 self.check_depth()?;
1371 let operand = self.parse_and_operand()?;
1372 self.depth_tracker.pop_depth();
1373
1374 let end_span = operand
1375 .source_location
1376 .as_ref()
1377 .map(|s| s.span.clone())
1378 .unwrap_or_else(|| start_span.clone());
1379 let span = self.span_covering(&start_span, &end_span);
1380
1381 self.new_expression(
1382 ExpressionKind::LogicalNegation(Arc::new(operand), NegationType::Not),
1383 self.make_source(span),
1384 )
1385 }
1386
1387 fn parse_base_with_suffix(&mut self) -> Result<Expression, Error> {
1388 let start_span = self.peek()?.span.clone();
1389 let base = self.parse_base_expression()?;
1390
1391 let peeked = self.peek()?;
1393
1394 if is_comparison_operator(&peeked.kind) {
1396 return self.parse_comparison_suffix(base, start_span);
1397 }
1398
1399 if peeked.kind == TokenKind::Not {
1403 return self.parse_not_in_calendar_suffix(base, start_span);
1404 }
1405
1406 if peeked.kind == TokenKind::In {
1408 return self.parse_in_suffix(base, start_span);
1409 }
1410
1411 Ok(base)
1412 }
1413
1414 fn parse_comparison_suffix(
1415 &mut self,
1416 left: Expression,
1417 start_span: Span,
1418 ) -> Result<Expression, Error> {
1419 let operator = self.parse_comparison_operator()?;
1420
1421 let right = if self.at(&TokenKind::Not)? {
1423 self.parse_not_expression()?
1424 } else {
1425 let rhs = self.parse_base_expression()?;
1426 if self.at(&TokenKind::In)? {
1428 self.parse_in_suffix(rhs, start_span.clone())?
1429 } else {
1430 rhs
1431 }
1432 };
1433
1434 let end_span = right
1435 .source_location
1436 .as_ref()
1437 .map(|s| s.span.clone())
1438 .unwrap_or_else(|| start_span.clone());
1439 let span = self.span_covering(&start_span, &end_span);
1440
1441 self.new_expression(
1442 ExpressionKind::Comparison(Arc::new(left), operator, Arc::new(right)),
1443 self.make_source(span),
1444 )
1445 }
1446
1447 fn parse_comparison_operator(&mut self) -> Result<ComparisonComputation, Error> {
1448 let tok = self.next()?;
1449 match tok.kind {
1450 TokenKind::Gt => Ok(ComparisonComputation::GreaterThan),
1451 TokenKind::Lt => Ok(ComparisonComputation::LessThan),
1452 TokenKind::Gte => Ok(ComparisonComputation::GreaterThanOrEqual),
1453 TokenKind::Lte => Ok(ComparisonComputation::LessThanOrEqual),
1454 TokenKind::EqEq => Ok(ComparisonComputation::Equal),
1455 TokenKind::BangEq => Ok(ComparisonComputation::NotEqual),
1456 TokenKind::Is => {
1457 if self.at(&TokenKind::Not)? {
1459 self.next()?; Ok(ComparisonComputation::IsNot)
1461 } else {
1462 Ok(ComparisonComputation::Is)
1463 }
1464 }
1465 _ => Err(self.error_at_token(
1466 &tok,
1467 format!("Expected a comparison operator, found {}", tok.kind),
1468 )),
1469 }
1470 }
1471
1472 fn parse_not_in_calendar_suffix(
1473 &mut self,
1474 base: Expression,
1475 start_span: Span,
1476 ) -> Result<Expression, Error> {
1477 self.expect(&TokenKind::Not)?;
1478 self.expect(&TokenKind::In)?;
1479 self.expect(&TokenKind::Calendar)?;
1480 let unit = self.parse_calendar_unit()?;
1481 let end = self.peek()?.span.clone();
1482 let span = self.span_covering(&start_span, &end);
1483 self.new_expression(
1484 ExpressionKind::DateCalendar(DateCalendarKind::NotIn, unit, Arc::new(base)),
1485 self.make_source(span),
1486 )
1487 }
1488
1489 fn parse_in_suffix(&mut self, base: Expression, start_span: Span) -> Result<Expression, Error> {
1490 self.expect(&TokenKind::In)?;
1491
1492 let peeked = self.peek()?;
1493
1494 if peeked.kind == TokenKind::Past || peeked.kind == TokenKind::Future {
1496 let direction = self.next()?;
1497 let rel_kind = if direction.text.to_lowercase() == "past" {
1498 DateRelativeKind::InPast
1499 } else {
1500 DateRelativeKind::InFuture
1501 };
1502
1503 if self.at(&TokenKind::Calendar)? {
1505 self.next()?; let cal_kind = if direction.text.to_lowercase() == "past" {
1507 DateCalendarKind::Past
1508 } else {
1509 DateCalendarKind::Future
1510 };
1511 let unit = self.parse_calendar_unit()?;
1512 let end = self.peek()?.span.clone();
1513 let span = self.span_covering(&start_span, &end);
1514 return self.new_expression(
1515 ExpressionKind::DateCalendar(cal_kind, unit, Arc::new(base)),
1516 self.make_source(span),
1517 );
1518 }
1519
1520 let tolerance = if !self.at(&TokenKind::And)?
1522 && !self.at(&TokenKind::Unless)?
1523 && !self.at(&TokenKind::Then)?
1524 && !self.at(&TokenKind::Eof)?
1525 && !is_comparison_operator(&self.peek()?.kind)
1526 {
1527 let peek_kind = self.peek()?.kind.clone();
1528 if peek_kind == TokenKind::NumberLit
1529 || peek_kind == TokenKind::LParen
1530 || can_be_reference_segment(&peek_kind)
1531 || is_math_function(&peek_kind)
1532 {
1533 Some(Arc::new(self.parse_base_expression()?))
1534 } else {
1535 None
1536 }
1537 } else {
1538 None
1539 };
1540
1541 let end = self.peek()?.span.clone();
1542 let span = self.span_covering(&start_span, &end);
1543 return self.new_expression(
1544 ExpressionKind::DateRelative(rel_kind, Arc::new(base), tolerance),
1545 self.make_source(span),
1546 );
1547 }
1548
1549 if peeked.kind == TokenKind::Calendar {
1551 self.next()?; let unit = self.parse_calendar_unit()?;
1553 let end = self.peek()?.span.clone();
1554 let span = self.span_covering(&start_span, &end);
1555 return self.new_expression(
1556 ExpressionKind::DateCalendar(DateCalendarKind::Current, unit, Arc::new(base)),
1557 self.make_source(span),
1558 );
1559 }
1560
1561 let target_tok = self.next()?;
1563 let target_text = target_tok.text.clone();
1564 let target = parse_conversion_target(&target_text);
1565
1566 let converted = self.new_expression(
1567 ExpressionKind::UnitConversion(Arc::new(base), target),
1568 self.make_source(self.span_covering(&start_span, &target_tok.span)),
1569 )?;
1570
1571 if is_comparison_operator(&self.peek()?.kind) {
1573 return self.parse_comparison_suffix(converted, start_span);
1574 }
1575
1576 Ok(converted)
1577 }
1578
1579 fn parse_calendar_unit(&mut self) -> Result<CalendarUnit, Error> {
1580 let tok = self.next()?;
1581 match tok.text.to_lowercase().as_str() {
1582 "year" => Ok(CalendarUnit::Year),
1583 "month" => Ok(CalendarUnit::Month),
1584 "week" => Ok(CalendarUnit::Week),
1585 _ => Err(self.error_at_token(
1586 &tok,
1587 format!("Expected 'year', 'month', or 'week', found '{}'", tok.text),
1588 )),
1589 }
1590 }
1591
1592 fn parse_base_expression(&mut self) -> Result<Expression, Error> {
1597 let start_span = self.peek()?.span.clone();
1598 let mut left = self.parse_term()?;
1599
1600 while self.at_any(&[TokenKind::Plus, TokenKind::Minus])? {
1601 let op_tok = self.next()?;
1604 let operation = match op_tok.kind {
1605 TokenKind::Plus => ArithmeticComputation::Add,
1606 TokenKind::Minus => ArithmeticComputation::Subtract,
1607 _ => unreachable!("BUG: only + and - should reach here"),
1608 };
1609
1610 let right = self.parse_term()?;
1611 let end_span = right
1612 .source_location
1613 .as_ref()
1614 .map(|s| s.span.clone())
1615 .unwrap_or_else(|| start_span.clone());
1616 let span = self.span_covering(&start_span, &end_span);
1617
1618 left = self.new_expression(
1619 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
1620 self.make_source(span),
1621 )?;
1622 }
1623
1624 Ok(left)
1625 }
1626
1627 fn parse_term(&mut self) -> Result<Expression, Error> {
1628 let start_span = self.peek()?.span.clone();
1629 let mut left = self.parse_power()?;
1630
1631 while self.at_any(&[TokenKind::Star, TokenKind::Slash, TokenKind::Percent])? {
1632 let op_tok = self.next()?;
1635 let operation = match op_tok.kind {
1636 TokenKind::Star => ArithmeticComputation::Multiply,
1637 TokenKind::Slash => ArithmeticComputation::Divide,
1638 TokenKind::Percent => ArithmeticComputation::Modulo,
1639 _ => unreachable!("BUG: only *, /, % should reach here"),
1640 };
1641
1642 let right = self.parse_power()?;
1643 let end_span = right
1644 .source_location
1645 .as_ref()
1646 .map(|s| s.span.clone())
1647 .unwrap_or_else(|| start_span.clone());
1648 let span = self.span_covering(&start_span, &end_span);
1649
1650 left = self.new_expression(
1651 ExpressionKind::Arithmetic(Arc::new(left), operation, Arc::new(right)),
1652 self.make_source(span),
1653 )?;
1654 }
1655
1656 Ok(left)
1657 }
1658
1659 fn parse_power(&mut self) -> Result<Expression, Error> {
1660 let start_span = self.peek()?.span.clone();
1661 let left = self.parse_factor()?;
1662
1663 if self.at(&TokenKind::Caret)? {
1664 self.next()?;
1665 self.check_depth()?;
1666 let right = self.parse_power()?;
1667 self.depth_tracker.pop_depth();
1668 let end_span = right
1669 .source_location
1670 .as_ref()
1671 .map(|s| s.span.clone())
1672 .unwrap_or_else(|| start_span.clone());
1673 let span = self.span_covering(&start_span, &end_span);
1674
1675 return self.new_expression(
1676 ExpressionKind::Arithmetic(
1677 Arc::new(left),
1678 ArithmeticComputation::Power,
1679 Arc::new(right),
1680 ),
1681 self.make_source(span),
1682 );
1683 }
1684
1685 Ok(left)
1686 }
1687
1688 fn parse_factor(&mut self) -> Result<Expression, Error> {
1689 let peeked = self.peek()?;
1690 let start_span = peeked.span.clone();
1691
1692 if peeked.kind == TokenKind::Minus {
1693 self.next()?;
1694 let operand = self.parse_primary_or_math()?;
1695 let end_span = operand
1696 .source_location
1697 .as_ref()
1698 .map(|s| s.span.clone())
1699 .unwrap_or_else(|| start_span.clone());
1700 let span = self.span_covering(&start_span, &end_span);
1701
1702 let zero = self.new_expression(
1703 ExpressionKind::Literal(Value::Number(Decimal::ZERO)),
1704 self.make_source(start_span),
1705 )?;
1706 return self.new_expression(
1707 ExpressionKind::Arithmetic(
1708 Arc::new(zero),
1709 ArithmeticComputation::Subtract,
1710 Arc::new(operand),
1711 ),
1712 self.make_source(span),
1713 );
1714 }
1715
1716 if peeked.kind == TokenKind::Plus {
1717 self.next()?;
1718 return self.parse_primary_or_math();
1719 }
1720
1721 self.parse_primary_or_math()
1722 }
1723
1724 fn parse_primary_or_math(&mut self) -> Result<Expression, Error> {
1725 let peeked = self.peek()?;
1726
1727 if is_math_function(&peeked.kind) {
1729 return self.parse_math_function();
1730 }
1731
1732 self.parse_primary()
1733 }
1734
1735 fn parse_math_function(&mut self) -> Result<Expression, Error> {
1736 let func_tok = self.next()?;
1737 let start_span = func_tok.span.clone();
1738
1739 let operator = match func_tok.kind {
1740 TokenKind::Sqrt => MathematicalComputation::Sqrt,
1741 TokenKind::Sin => MathematicalComputation::Sin,
1742 TokenKind::Cos => MathematicalComputation::Cos,
1743 TokenKind::Tan => MathematicalComputation::Tan,
1744 TokenKind::Asin => MathematicalComputation::Asin,
1745 TokenKind::Acos => MathematicalComputation::Acos,
1746 TokenKind::Atan => MathematicalComputation::Atan,
1747 TokenKind::Log => MathematicalComputation::Log,
1748 TokenKind::Exp => MathematicalComputation::Exp,
1749 TokenKind::Abs => MathematicalComputation::Abs,
1750 TokenKind::Floor => MathematicalComputation::Floor,
1751 TokenKind::Ceil => MathematicalComputation::Ceil,
1752 TokenKind::Round => MathematicalComputation::Round,
1753 _ => unreachable!("BUG: only math functions should reach here"),
1754 };
1755
1756 self.check_depth()?;
1757 let operand = self.parse_base_expression()?;
1758 self.depth_tracker.pop_depth();
1759
1760 let end_span = operand
1761 .source_location
1762 .as_ref()
1763 .map(|s| s.span.clone())
1764 .unwrap_or_else(|| start_span.clone());
1765 let span = self.span_covering(&start_span, &end_span);
1766
1767 self.new_expression(
1768 ExpressionKind::MathematicalComputation(operator, Arc::new(operand)),
1769 self.make_source(span),
1770 )
1771 }
1772
1773 fn parse_primary(&mut self) -> Result<Expression, Error> {
1774 let peeked = self.peek()?;
1775 let start_span = peeked.span.clone();
1776
1777 match &peeked.kind {
1778 TokenKind::LParen => {
1780 self.next()?; let inner = self.parse_expression()?;
1782 self.expect(&TokenKind::RParen)?;
1783 Ok(inner)
1784 }
1785
1786 TokenKind::Now => {
1788 let tok = self.next()?;
1789 self.new_expression(ExpressionKind::Now, self.make_source(tok.span))
1790 }
1791
1792 TokenKind::StringLit => {
1794 let tok = self.next()?;
1795 let content = unquote_string(&tok.text);
1796 self.new_expression(
1797 ExpressionKind::Literal(Value::Text(content)),
1798 self.make_source(tok.span),
1799 )
1800 }
1801
1802 k if is_boolean_keyword(k) => {
1804 let tok = self.next()?;
1805 let bv = parse_boolean_value(&tok.text);
1806 self.new_expression(
1807 ExpressionKind::Literal(Value::Boolean(bv)),
1808 self.make_source(tok.span),
1809 )
1810 }
1811
1812 TokenKind::NumberLit => self.parse_number_expression(),
1814
1815 k if can_be_reference_segment(k) => {
1817 let reference = self.parse_expression_reference()?;
1818 let end_span = self.peek()?.span.clone();
1819 let span = self.span_covering(&start_span, &end_span);
1820 self.new_expression(ExpressionKind::Reference(reference), self.make_source(span))
1821 }
1822
1823 _ => {
1824 let tok = self.next()?;
1825 Err(self.error_at_token(
1826 &tok,
1827 format!("Expected an expression, found '{}'", tok.text),
1828 ))
1829 }
1830 }
1831 }
1832
1833 fn parse_number_expression(&mut self) -> Result<Expression, Error> {
1834 let num_tok = self.next()?;
1835 let num_text = num_tok.text.clone();
1836 let start_span = num_tok.span.clone();
1837
1838 if num_text.len() == 4
1840 && num_text.chars().all(|c| c.is_ascii_digit())
1841 && self.at(&TokenKind::Minus)?
1842 {
1843 let minus_span = self.peek()?.span.clone();
1850 if minus_span.start == start_span.end {
1852 let value = self.parse_date_literal(num_text, start_span.clone())?;
1853 return self
1854 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
1855 }
1856 }
1857
1858 if num_text.len() == 2
1860 && num_text.chars().all(|c| c.is_ascii_digit())
1861 && self.at(&TokenKind::Colon)?
1862 {
1863 let colon_span = self.peek()?.span.clone();
1864 if colon_span.start == start_span.end {
1865 let value = self.try_parse_time_literal(num_text, start_span.clone())?;
1866 return self
1867 .new_expression(ExpressionKind::Literal(value), self.make_source(start_span));
1868 }
1869 }
1870
1871 if self.at(&TokenKind::PercentPercent)? {
1873 let pp_tok = self.next()?;
1874 if let Ok(next_peek) = self.peek() {
1875 if next_peek.kind == TokenKind::NumberLit {
1876 return Err(self.error_at_token(
1877 &pp_tok,
1878 "Permille literal cannot be followed by a digit",
1879 ));
1880 }
1881 }
1882 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1883 let ratio_value = decimal / Decimal::from(1000);
1884 return self.new_expression(
1885 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("permille".to_string()))),
1886 self.make_source(start_span),
1887 );
1888 }
1889
1890 if self.at(&TokenKind::Percent)? {
1892 let pct_span = self.peek()?.span.clone();
1893 let pct_tok = self.next()?;
1896 if let Ok(next_peek) = self.peek() {
1897 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1898 return Err(self.error_at_token(
1899 &pct_tok,
1900 "Percent literal cannot be followed by a digit",
1901 ));
1902 }
1903 }
1904 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1905 let ratio_value = decimal / Decimal::from(100);
1906 return self.new_expression(
1907 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("percent".to_string()))),
1908 self.make_source(self.span_covering(&start_span, &pct_span)),
1909 );
1910 }
1911
1912 if self.at(&TokenKind::PercentKw)? {
1914 self.next()?;
1915 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1916 let ratio_value = decimal / Decimal::from(100);
1917 return self.new_expression(
1918 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("percent".to_string()))),
1919 self.make_source(start_span),
1920 );
1921 }
1922
1923 if self.at(&TokenKind::Permille)? {
1925 self.next()?;
1926 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1927 let ratio_value = decimal / Decimal::from(1000);
1928 return self.new_expression(
1929 ExpressionKind::Literal(Value::Ratio(ratio_value, Some("permille".to_string()))),
1930 self.make_source(start_span),
1931 );
1932 }
1933
1934 if is_duration_unit(&self.peek()?.kind) && self.peek()?.kind != TokenKind::PercentKw {
1936 let unit_tok = self.next()?;
1937 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1938 let duration_unit = parse_duration_unit_from_text(&unit_tok.text);
1939 return self.new_expression(
1940 ExpressionKind::Literal(Value::Duration(decimal, duration_unit)),
1941 self.make_source(self.span_covering(&start_span, &unit_tok.span)),
1942 );
1943 }
1944
1945 if can_be_label(&self.peek()?.kind) {
1947 let unit_tok = self.next()?;
1948 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1949 return self.new_expression(
1950 ExpressionKind::UnresolvedUnitLiteral(decimal, unit_tok.text.clone()),
1951 self.make_source(self.span_covering(&start_span, &unit_tok.span)),
1952 );
1953 }
1954
1955 let decimal = parse_decimal_string(&num_text, &start_span, self)?;
1957 self.new_expression(
1958 ExpressionKind::Literal(Value::Number(decimal)),
1959 self.make_source(start_span),
1960 )
1961 }
1962
1963 fn parse_expression_reference(&mut self) -> Result<Reference, Error> {
1964 let mut segments = Vec::new();
1965
1966 let first = self.next()?;
1967 segments.push(first.text.clone());
1968
1969 while self.at(&TokenKind::Dot)? {
1970 self.next()?; let seg = self.next()?;
1972 if !can_be_reference_segment(&seg.kind) {
1973 return Err(self.error_at_token(
1974 &seg,
1975 format!("Expected an identifier after '.', found {}", seg.kind),
1976 ));
1977 }
1978 segments.push(seg.text.clone());
1979 }
1980
1981 Ok(Reference::from_path(segments))
1982 }
1983}
1984
1985fn unquote_string(s: &str) -> String {
1990 if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
1991 s[1..s.len() - 1].to_string()
1992 } else {
1993 s.to_string()
1994 }
1995}
1996
1997fn parse_boolean_value(text: &str) -> BooleanValue {
1998 match text.to_lowercase().as_str() {
1999 "true" => BooleanValue::True,
2000 "false" => BooleanValue::False,
2001 "yes" => BooleanValue::Yes,
2002 "no" => BooleanValue::No,
2003 "accept" => BooleanValue::Accept,
2004 "reject" => BooleanValue::Reject,
2005 other => unreachable!("BUG: '{}' is not a boolean keyword", other),
2006 }
2007}
2008
2009fn parse_decimal_string(text: &str, span: &Span, parser: &Parser) -> Result<Decimal, Error> {
2010 let clean = text.replace(['_', ','], "");
2011 Decimal::from_str(&clean).map_err(|_| {
2012 Error::parsing(
2013 format!(
2014 "Invalid number: '{}'. Expected a valid decimal number (e.g., 42, 3.14, 1_000_000)",
2015 text
2016 ),
2017 parser.make_source(span.clone()),
2018 None::<String>,
2019 )
2020 })
2021}
2022
2023fn parse_conversion_target(unit_str: &str) -> ConversionTarget {
2024 let unit_lower = unit_str.to_lowercase();
2025 match unit_lower.as_str() {
2026 "year" | "years" => ConversionTarget::Duration(DurationUnit::Year),
2027 "month" | "months" => ConversionTarget::Duration(DurationUnit::Month),
2028 "week" | "weeks" => ConversionTarget::Duration(DurationUnit::Week),
2029 "day" | "days" => ConversionTarget::Duration(DurationUnit::Day),
2030 "hour" | "hours" => ConversionTarget::Duration(DurationUnit::Hour),
2031 "minute" | "minutes" => ConversionTarget::Duration(DurationUnit::Minute),
2032 "second" | "seconds" => ConversionTarget::Duration(DurationUnit::Second),
2033 "millisecond" | "milliseconds" => ConversionTarget::Duration(DurationUnit::Millisecond),
2034 "microsecond" | "microseconds" => ConversionTarget::Duration(DurationUnit::Microsecond),
2035 _ => ConversionTarget::Unit(unit_lower),
2036 }
2037}
2038
2039fn parse_duration_unit_from_text(text: &str) -> DurationUnit {
2040 match text.to_lowercase().as_str() {
2041 "year" | "years" => DurationUnit::Year,
2042 "month" | "months" => DurationUnit::Month,
2043 "week" | "weeks" => DurationUnit::Week,
2044 "day" | "days" => DurationUnit::Day,
2045 "hour" | "hours" => DurationUnit::Hour,
2046 "minute" | "minutes" => DurationUnit::Minute,
2047 "second" | "seconds" => DurationUnit::Second,
2048 "millisecond" | "milliseconds" => DurationUnit::Millisecond,
2049 "microsecond" | "microseconds" => DurationUnit::Microsecond,
2050 other => unreachable!("BUG: '{}' is not a duration unit", other),
2051 }
2052}
2053
2054fn is_comparison_operator(kind: &TokenKind) -> bool {
2055 matches!(
2056 kind,
2057 TokenKind::Gt
2058 | TokenKind::Lt
2059 | TokenKind::Gte
2060 | TokenKind::Lte
2061 | TokenKind::EqEq
2062 | TokenKind::BangEq
2063 | TokenKind::Is
2064 )
2065}
2066
2067impl TokenKind {
2069 fn is_identifier_like(&self) -> bool {
2070 matches!(self, TokenKind::Identifier)
2071 || can_be_label(self)
2072 || is_type_keyword(self)
2073 || is_boolean_keyword(self)
2074 || is_duration_unit(self)
2075 || is_math_function(self)
2076 }
2077}