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