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