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