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