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_tok = self.next()?;
1366 let measure_ref = measure_ref_tok.text.clone();
1367
1368 let explicit_exp: Option<i32> = if self.at(&TokenKind::Caret)? {
1370 self.next()?; let negative = if self.at(&TokenKind::Minus)? {
1373 self.next()?; true
1375 } else {
1376 false
1377 };
1378
1379 if !self.at(&TokenKind::NumberLit)? {
1380 let bad_tok = self.next()?;
1381 return Err(self.error_at_token(
1382 &bad_tok,
1383 format!(
1384 "Expected an integer exponent after '^' in unit expression, found {}",
1385 bad_tok.kind
1386 ),
1387 ));
1388 }
1389
1390 let exp_tok = self.next()?;
1391 let raw: i32 = exp_tok.text.parse::<i32>().map_err(|_| {
1392 self.error_at_token(
1393 &exp_tok,
1394 format!(
1395 "Exponent '{}' is not a valid integer in unit expression",
1396 exp_tok.text
1397 ),
1398 )
1399 })?;
1400
1401 if raw == 0 {
1402 return Err(self.error_at_token(
1403 &exp_tok,
1404 "Exponent cannot be zero in a unit expression".to_string(),
1405 ));
1406 }
1407
1408 Some(if negative { -raw } else { raw })
1409 } else {
1410 None
1411 };
1412
1413 let final_exp = match (explicit_exp, denominator_mode) {
1415 (Some(exponent), true) => -exponent,
1416 (Some(exponent), false) => exponent,
1417 (None, true) => -1,
1418 (None, false) => 1,
1419 };
1420
1421 factors.push(UnitFactor {
1422 measure_ref,
1423 exp: final_exp,
1424 });
1425 }
1426
1427 Ok(factors)
1428 }
1429
1430 fn parse_meta(&mut self) -> Result<MetaField, Error> {
1435 let meta_tok = self.expect(&TokenKind::Meta)?;
1436 let start_span = meta_tok.span.clone();
1437
1438 let key_tok = self.next()?;
1439 let key = key_tok.text.clone();
1440
1441 self.expect(&TokenKind::Colon)?;
1442
1443 let value = self.parse_meta_value()?;
1444
1445 let span = self.span_covering(&start_span, &self.last_span);
1446
1447 Ok(MetaField {
1448 key,
1449 value,
1450 source_location: self.make_source(span),
1451 })
1452 }
1453
1454 fn parse_meta_value(&mut self) -> Result<MetaValue, Error> {
1455 let peeked = self.peek()?;
1457 match &peeked.kind {
1458 TokenKind::StringLit => {
1459 let value = self.parse_literal_value()?;
1460 return Ok(MetaValue::Literal(value));
1461 }
1462 TokenKind::NumberLit => {
1463 let value = self.parse_literal_value()?;
1464 return Ok(MetaValue::Literal(value));
1465 }
1466 k if is_boolean_keyword(k) => {
1467 let value = self.parse_literal_value()?;
1468 return Ok(MetaValue::Literal(value));
1469 }
1470 _ => {}
1471 }
1472
1473 let mut ident = String::new();
1476 loop {
1477 let peeked = self.peek()?;
1478 match &peeked.kind {
1479 k if k.is_identifier_like() => {
1480 let tok = self.next()?;
1481 ident.push_str(&tok.text);
1482 }
1483 TokenKind::Dot => {
1484 self.next()?;
1485 ident.push('.');
1486 }
1487 TokenKind::Slash => {
1488 self.next()?;
1489 ident.push('/');
1490 }
1491 TokenKind::Minus => {
1492 self.next()?;
1493 ident.push('-');
1494 }
1495 TokenKind::NumberLit => {
1496 let tok = self.next()?;
1497 ident.push_str(&tok.text);
1498 }
1499 _ => break,
1500 }
1501 }
1502
1503 if ident.is_empty() {
1504 let tok = self.peek()?.clone();
1505 return Err(self.error_at_token(&tok, "Expected a meta value"));
1506 }
1507
1508 Ok(MetaValue::Unquoted(ident))
1509 }
1510
1511 fn parse_literal_value(&mut self) -> Result<Value, Error> {
1516 let left = self.parse_scalar_literal_value()?;
1517 if self.at(&TokenKind::Ellipsis)? {
1518 self.next()?;
1519 let right = self.parse_scalar_literal_value()?;
1520 Ok(Value::Range(Box::new(left), Box::new(right)))
1521 } else {
1522 Ok(left)
1523 }
1524 }
1525
1526 fn parse_signed_number_literal(&mut self) -> Result<Value, Error> {
1527 let sign_tok = self.next()?;
1528 let sign_span = sign_tok.span.clone();
1529 let is_negative = sign_tok.kind == TokenKind::Minus;
1530
1531 if !self.at(&TokenKind::NumberLit)? {
1532 let tok = self.peek()?.clone();
1533 return Err(self.error_at_token(
1534 &tok,
1535 format!(
1536 "Expected a number after '{}', found '{}'",
1537 sign_tok.text, tok.text
1538 ),
1539 ));
1540 }
1541
1542 let value = self.parse_number_literal()?;
1543 if !is_negative {
1544 return Ok(value);
1545 }
1546 match value {
1547 Value::Number(d) => Ok(Value::Number(-d)),
1548 Value::NumberWithUnit(d, unit) => Ok(Value::NumberWithUnit(-d, unit)),
1549 other => Err(Error::parsing(
1550 format!("Cannot negate this value: {}", other),
1551 self.make_source(sign_span),
1552 None::<String>,
1553 )),
1554 }
1555 }
1556
1557 fn parse_number_literal(&mut self) -> Result<Value, Error> {
1558 let num_tok = self.next()?;
1559 let num_text = &num_tok.text;
1560 let num_span = num_tok.span.clone();
1561
1562 if num_text.len() == 4
1564 && num_text.chars().all(|c| c.is_ascii_digit())
1565 && self.at(&TokenKind::Minus)?
1566 {
1567 return self.parse_date_literal(num_text.clone(), num_span);
1568 }
1569
1570 let peeked = self.peek()?;
1572
1573 if num_text.len() == 2
1575 && num_text.chars().all(|c| c.is_ascii_digit())
1576 && peeked.kind == TokenKind::Colon
1577 {
1578 return self.try_parse_time_literal(num_text.clone(), num_span);
1585 }
1586
1587 if peeked.kind == TokenKind::PercentPercent {
1589 let pp_tok = self.next()?;
1590 if let Ok(next_peek) = self.peek() {
1592 if next_peek.kind == TokenKind::NumberLit {
1593 return Err(self.error_at_token(
1594 &pp_tok,
1595 "Permille literal cannot be followed by a digit",
1596 ));
1597 }
1598 }
1599 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1600 return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1601 }
1602
1603 if peeked.kind == TokenKind::Percent {
1605 let pct_tok = self.next()?;
1606 if let Ok(next_peek) = self.peek() {
1608 if next_peek.kind == TokenKind::NumberLit || next_peek.kind == TokenKind::Percent {
1609 return Err(self.error_at_token(
1610 &pct_tok,
1611 "Percent literal cannot be followed by a digit",
1612 ));
1613 }
1614 }
1615 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1616 return Ok(Value::NumberWithUnit(decimal, "percent".to_string()));
1617 }
1618
1619 if peeked.kind == TokenKind::Permille {
1621 self.next()?; let decimal = parse_decimal_string(num_text, &num_span, self)?;
1623 return Ok(Value::NumberWithUnit(decimal, "permille".to_string()));
1624 }
1625
1626 if can_be_label(&peeked.kind) {
1627 let unit_tok = self.next()?;
1628 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1629 return Ok(Value::NumberWithUnit(decimal, unit_tok.text.clone()));
1630 }
1631
1632 let decimal = parse_decimal_string(num_text, &num_span, self)?;
1634 Ok(Value::Number(decimal))
1635 }
1636
1637 fn parse_date_literal(&mut self, year_text: String, start_span: Span) -> Result<Value, Error> {
1638 let mut dt_str = year_text;
1639
1640 self.expect(&TokenKind::Minus)?;
1642 dt_str.push('-');
1643 let month_tok = self.expect(&TokenKind::NumberLit)?;
1644 dt_str.push_str(&month_tok.text);
1645
1646 self.expect(&TokenKind::Minus)?;
1648 dt_str.push('-');
1649 let day_tok = self.expect(&TokenKind::NumberLit)?;
1650 dt_str.push_str(&day_tok.text);
1651
1652 if self.at(&TokenKind::Identifier)? {
1654 let peeked = self.peek()?;
1655 if peeked.text.len() >= 2
1656 && (peeked.text.starts_with('T') || peeked.text.starts_with('t'))
1657 {
1658 let t_tok = self.next()?;
1660 dt_str.push_str(&t_tok.text);
1661
1662 if self.at(&TokenKind::Colon)? {
1664 self.next()?;
1665 dt_str.push(':');
1666 let min_tok = self.next()?;
1667 dt_str.push_str(&min_tok.text);
1668
1669 if self.at(&TokenKind::Colon)? {
1671 self.next()?;
1672 dt_str.push(':');
1673 let sec_tok = self.next()?;
1674 dt_str.push_str(&sec_tok.text);
1675
1676 if self.at(&TokenKind::Dot)? {
1678 self.next()?;
1679 dt_str.push('.');
1680 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1681 dt_str.push_str(&frac_tok.text);
1682 }
1683 }
1684 }
1685
1686 self.try_consume_timezone(&mut dt_str)?;
1688 }
1689 }
1690
1691 if let Ok(dtv) = dt_str.parse::<crate::literals::DateTimeValue>() {
1692 return Ok(Value::Date(dtv));
1693 }
1694
1695 Err(Error::parsing(
1696 format!("Invalid date/time format: '{}'", dt_str),
1697 self.make_source(start_span),
1698 None::<String>,
1699 ))
1700 }
1701
1702 fn try_consume_timezone(&mut self, dt_str: &mut String) -> Result<(), Error> {
1703 if self.at(&TokenKind::Identifier)? {
1705 let peeked = self.peek()?;
1706 if (peeked.text == "Z" || peeked.text == "z") && peeked.span.start == self.last_span.end
1707 {
1708 let z_tok = self.next()?;
1709 dt_str.push_str(&z_tok.text);
1710 return Ok(());
1711 }
1712 }
1713
1714 if self.at(&TokenKind::Plus)? || self.at(&TokenKind::Minus)? {
1716 let mut lookahead = self.lexer.clone();
1717 let sign_tok = lookahead.next_token()?;
1718 let hour_tok = lookahead.next_token()?;
1719 let colon_tok = lookahead.next_token()?;
1720 let minute_tok = lookahead.next_token()?;
1721
1722 let attached = sign_tok.span.start == self.last_span.end;
1723 let is_timezone_shape = hour_tok.kind == TokenKind::NumberLit
1724 && colon_tok.kind == TokenKind::Colon
1725 && minute_tok.kind == TokenKind::NumberLit;
1726
1727 if attached && is_timezone_shape {
1728 let sign_tok = self.next()?;
1729 dt_str.push_str(&sign_tok.text);
1730 let hour_tok = self.expect(&TokenKind::NumberLit)?;
1731 dt_str.push_str(&hour_tok.text);
1732 self.expect(&TokenKind::Colon)?;
1733 dt_str.push(':');
1734 let min_tok = self.expect(&TokenKind::NumberLit)?;
1735 dt_str.push_str(&min_tok.text);
1736 }
1737 }
1738
1739 Ok(())
1740 }
1741
1742 fn try_parse_time_literal(
1743 &mut self,
1744 hour_text: String,
1745 start_span: Span,
1746 ) -> Result<Value, Error> {
1747 let mut time_str = hour_text;
1748
1749 self.expect(&TokenKind::Colon)?;
1751 time_str.push(':');
1752 let min_tok = self.expect(&TokenKind::NumberLit)?;
1753 time_str.push_str(&min_tok.text);
1754
1755 if self.at(&TokenKind::Colon)? {
1757 self.next()?;
1758 time_str.push(':');
1759 let sec_tok = self.expect(&TokenKind::NumberLit)?;
1760 time_str.push_str(&sec_tok.text);
1761
1762 if self.at(&TokenKind::Dot)? {
1764 self.next()?;
1765 time_str.push('.');
1766 let frac_tok = self.expect(&TokenKind::NumberLit)?;
1767 time_str.push_str(&frac_tok.text);
1768 }
1769 }
1770
1771 self.try_consume_timezone(&mut time_str)?;
1773
1774 if let Ok(t) = time_str.parse::<TimeValue>() {
1775 return Ok(Value::Time(TimeValue {
1776 hour: t.hour,
1777 minute: t.minute,
1778 second: t.second,
1779 microsecond: t.microsecond,
1780 timezone: t.timezone,
1781 }));
1782 }
1783
1784 Err(Error::parsing(
1785 format!("Invalid time format: '{}'", time_str),
1786 self.make_source(start_span),
1787 None::<String>,
1788 ))
1789 }
1790
1791 fn new_expression(
1796 &mut self,
1797 kind: ExpressionKind,
1798 source: Source,
1799 ) -> Result<Expression, Error> {
1800 self.expression_count += 1;
1801 if self.expression_count > self.max_expression_count {
1802 return Err(Error::resource_limit_exceeded(
1803 "max_expression_count",
1804 self.max_expression_count.to_string(),
1805 self.expression_count.to_string(),
1806 "Split logic into multiple rules to reduce expression count",
1807 Some(source),
1808 None,
1809 None,
1810 ));
1811 }
1812 Ok(Expression::new(kind, source))
1813 }
1814
1815 fn check_depth(&mut self) -> Result<(), Error> {
1816 if let Err(actual) = self.depth_tracker.push_depth() {
1817 let span = self.peek()?.span.clone();
1818 self.depth_tracker.pop_depth();
1819 return Err(Error::resource_limit_exceeded(
1820 "max_expression_depth",
1821 self.depth_tracker.max_depth().to_string(),
1822 actual.to_string(),
1823 "Simplify nested expressions or break into separate rules",
1824 Some(self.make_source(span)),
1825 None,
1826 None,
1827 ));
1828 }
1829 Ok(())
1830 }
1831
1832 fn parse_expression(&mut self) -> Result<Expression, Error> {
1833 self.check_depth()?;
1834 let result = self.parse_and_expression();
1835 self.depth_tracker.pop_depth();
1836 result
1837 }
1838
1839 fn parse_and_expression(&mut self) -> Result<Expression, Error> {
1840 let start_span = self.peek()?.span.clone();
1841 let mut left = self.parse_and_operand()?;
1842
1843 while self.at(&TokenKind::And)? {
1844 self.next()?; let right = self.parse_and_operand()?;
1846 let span = self.span_covering(
1847 &start_span,
1848 &right
1849 .source_location
1850 .as_ref()
1851 .map(|s| s.span.clone())
1852 .unwrap_or_else(|| start_span.clone()),
1853 );
1854 left = self.new_expression(
1855 ExpressionKind::LogicalAnd(Arc::new(left), Arc::new(right)),
1856 self.make_source(span),
1857 )?;
1858 }
1859
1860 Ok(left)
1861 }
1862
1863 fn at_bare_veto_token(&mut self) -> Result<bool, Error> {
1864 if !self.at(&TokenKind::Veto)? {
1865 return Ok(false);
1866 }
1867 let checkpoint = self.checkpoint();
1868 self.next()?;
1869 let bare = !self.at(&TokenKind::StringLit)?;
1870 self.restore(checkpoint);
1871 Ok(bare)
1872 }
1873
1874 fn at_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
1875 if !self.at_bare_veto_token()? {
1876 return Ok(false);
1877 }
1878 let checkpoint = self.checkpoint();
1879 self.next()?;
1880 let followed = self.at(&TokenKind::Is)?;
1881 self.restore(checkpoint);
1882 Ok(followed)
1883 }
1884
1885 fn at_not_bare_veto_followed_by_is(&mut self) -> Result<bool, Error> {
1886 if !self.at(&TokenKind::Not)? {
1887 return Ok(false);
1888 }
1889 let checkpoint = self.checkpoint();
1890 self.next()?;
1891 if !self.at(&TokenKind::Veto)? {
1892 self.restore(checkpoint);
1893 return Ok(false);
1894 }
1895 self.next()?;
1896 if self.at(&TokenKind::StringLit)? {
1897 self.restore(checkpoint);
1898 return Ok(false);
1899 }
1900 let followed = self.at(&TokenKind::Is)?;
1901 self.restore(checkpoint);
1902 Ok(followed)
1903 }
1904
1905 fn wrap_result_is_veto_expression(
1906 &mut self,
1907 operand: Expression,
1908 operator_is_not: bool,
1909 keyword_was_negated: bool,
1910 start_span: Span,
1911 ) -> Result<Expression, Error> {
1912 let negate = operator_is_not ^ keyword_was_negated;
1913 let end_span = operand
1914 .source_location
1915 .as_ref()
1916 .map(|source| source.span.clone())
1917 .unwrap_or_else(|| start_span.clone());
1918 let span = self.span_covering(&start_span, &end_span);
1919 let core = self.new_expression(
1920 ExpressionKind::ResultIsVeto(Arc::new(operand)),
1921 self.make_source(span.clone()),
1922 )?;
1923 if negate {
1924 self.new_expression(
1925 ExpressionKind::LogicalNegation(Arc::new(core), NegationType::Not),
1926 self.make_source(span),
1927 )
1928 } else {
1929 Ok(core)
1930 }
1931 }
1932
1933 fn parse_veto_status_lhs_is_comparison(&mut self) -> Result<Expression, Error> {
1934 let start_span = self.peek()?.span.clone();
1935 let keyword_was_negated = if self.at(&TokenKind::Not)? {
1936 self.next()?;
1937 true
1938 } else {
1939 false
1940 };
1941 self.expect(&TokenKind::Veto)?;
1942 if self.at(&TokenKind::StringLit)? {
1943 let tok = self.peek()?.clone();
1944 return Err(self.error_at_token(
1945 &tok,
1946 "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
1947 ));
1948 }
1949 let operator = self.parse_comparison_operator()?;
1950 let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
1951 if !matches!(
1952 operator,
1953 ComparisonComputation::Is | ComparisonComputation::IsNot
1954 ) {
1955 let tok = self.peek()?.clone();
1956 return Err(self.error_at_token(
1957 &tok,
1958 "Expected `is` or `is not` after `veto` in a veto-status comparison",
1959 ));
1960 }
1961 let operand = self.parse_range_expression()?;
1962 self.wrap_result_is_veto_expression(
1963 operand,
1964 operator_is_not,
1965 keyword_was_negated,
1966 start_span,
1967 )
1968 }
1969
1970 fn parse_and_operand(&mut self) -> Result<Expression, Error> {
1971 if self.at_not_bare_veto_followed_by_is()? || self.at_bare_veto_followed_by_is()? {
1972 return self.parse_veto_status_lhs_is_comparison();
1973 }
1974
1975 if self.at(&TokenKind::Not)? {
1977 return self.parse_not_expression();
1978 }
1979
1980 self.parse_repository_with_suffix()
1982 }
1983
1984 fn parse_not_expression(&mut self) -> Result<Expression, Error> {
1985 let not_tok = self.expect(&TokenKind::Not)?;
1986 let start_span = not_tok.span.clone();
1987
1988 self.check_depth()?;
1989 let operand = self.parse_and_operand()?;
1990 self.depth_tracker.pop_depth();
1991
1992 let end_span = operand
1993 .source_location
1994 .as_ref()
1995 .map(|s| s.span.clone())
1996 .unwrap_or_else(|| start_span.clone());
1997 let span = self.span_covering(&start_span, &end_span);
1998
1999 self.new_expression(
2000 ExpressionKind::LogicalNegation(Arc::new(operand), NegationType::Not),
2001 self.make_source(span),
2002 )
2003 }
2004
2005 fn parse_repository_with_suffix(&mut self) -> Result<Expression, Error> {
2006 let start_span = self.peek()?.span.clone();
2007 let repository = self.parse_range_expression()?;
2008 self.continue_repository_operand(repository, start_span)
2009 }
2010
2011 fn continue_repository_operand(
2013 &mut self,
2014 mut expr: Expression,
2015 start_span: Span,
2016 ) -> Result<Expression, Error> {
2017 loop {
2018 let peeked = self.peek()?;
2019
2020 if is_comparison_operator(&peeked.kind) {
2021 return self.parse_comparison_suffix(expr, start_span);
2022 }
2023
2024 if peeked.kind == TokenKind::Not {
2025 expr = self.parse_not_in_calendar_suffix(expr, start_span.clone())?;
2026 continue;
2027 }
2028
2029 if peeked.kind == TokenKind::In {
2030 expr = self.parse_in_suffix(expr, start_span.clone())?;
2031 continue;
2032 }
2033
2034 if peeked.kind == TokenKind::As {
2035 expr = self.parse_as_chain(expr, start_span.clone())?;
2036 continue;
2037 }
2038
2039 break;
2040 }
2041
2042 if self.at_expression_suffix_end()? {
2043 return Ok(expr);
2044 }
2045
2046 let tok = self.peek()?.clone();
2047 Err(self.error_at_token(
2048 &tok,
2049 format!("Unexpected token '{}' after expression", tok.text),
2050 ))
2051 }
2052
2053 fn parse_comparison_suffix(
2054 &mut self,
2055 left: Expression,
2056 start_span: Span,
2057 ) -> Result<Expression, Error> {
2058 let operator = self.parse_comparison_operator()?;
2059 let operator_is_not = matches!(operator, ComparisonComputation::IsNot);
2060
2061 if matches!(
2062 operator,
2063 ComparisonComputation::Is | ComparisonComputation::IsNot
2064 ) && self.at_bare_veto_token()?
2065 {
2066 self.expect(&TokenKind::Veto)?;
2067 if self.at(&TokenKind::StringLit)? {
2068 let tok = self.peek()?.clone();
2069 return Err(self.error_at_token(
2070 &tok,
2071 "veto with a message is only valid as a rule or unless result, not in `is veto` comparisons",
2072 ));
2073 }
2074 return self.wrap_result_is_veto_expression(left, operator_is_not, false, start_span);
2075 }
2076
2077 let right = if self.at(&TokenKind::Not)? {
2079 self.parse_not_expression()?
2080 } else {
2081 self.parse_range_expression()?
2082 };
2083
2084 let end_span = right
2085 .source_location
2086 .as_ref()
2087 .map(|s| s.span.clone())
2088 .unwrap_or_else(|| start_span.clone());
2089 let span = self.span_covering(&start_span, &end_span);
2090
2091 self.new_expression(
2092 ExpressionKind::Comparison(Arc::new(left), operator, Arc::new(right)),
2093 self.make_source(span),
2094 )
2095 }
2096
2097 fn parse_comparison_operator(&mut self) -> Result<ComparisonComputation, Error> {
2098 let tok = self.next()?;
2099 match tok.kind {
2100 TokenKind::Gt => Ok(ComparisonComputation::GreaterThan),
2101 TokenKind::Lt => Ok(ComparisonComputation::LessThan),
2102 TokenKind::Gte => Ok(ComparisonComputation::GreaterThanOrEqual),
2103 TokenKind::Lte => Ok(ComparisonComputation::LessThanOrEqual),
2104 TokenKind::Is => {
2105 if self.at(&TokenKind::Not)? {
2107 self.next()?; Ok(ComparisonComputation::IsNot)
2109 } else {
2110 Ok(ComparisonComputation::Is)
2111 }
2112 }
2113 _ => Err(self.error_at_token(
2114 &tok,
2115 format!("Expected a comparison operator, found {}", tok.kind),
2116 )),
2117 }
2118 }
2119
2120 fn parse_not_in_calendar_suffix(
2121 &mut self,
2122 repository: Expression,
2123 start_span: Span,
2124 ) -> Result<Expression, Error> {
2125 self.expect(&TokenKind::Not)?;
2126 self.expect(&TokenKind::In)?;
2127 self.expect_calendar_period_marker()?;
2128 let unit = self.parse_calendar_unit()?;
2129 let end = self.peek()?.span.clone();
2130 let span = self.span_covering(&start_span, &end);
2131 self.new_expression(
2132 ExpressionKind::DateCalendar(DateCalendarKind::NotIn, unit, Arc::new(repository)),
2133 self.make_source(span),
2134 )
2135 }
2136
2137 fn parse_in_suffix(
2138 &mut self,
2139 repository: Expression,
2140 start_span: Span,
2141 ) -> Result<Expression, Error> {
2142 self.expect(&TokenKind::In)?;
2143
2144 let peeked = self.peek()?;
2145
2146 if peeked.kind == TokenKind::Past || peeked.kind == TokenKind::Future {
2148 let direction = self.next()?;
2149 let rel_kind = if direction.kind == TokenKind::Past {
2150 DateRelativeKind::InPast
2151 } else {
2152 DateRelativeKind::InFuture
2153 };
2154
2155 if self.at_calendar_period_marker()? {
2157 self.next_calendar_period_marker()?;
2158 let cal_kind = if direction.kind == TokenKind::Past {
2159 DateCalendarKind::Past
2160 } else {
2161 DateCalendarKind::Future
2162 };
2163 let unit = self.parse_calendar_unit()?;
2164 let end = self.peek()?.span.clone();
2165 let span = self.span_covering(&start_span, &end);
2166 return self.new_expression(
2167 ExpressionKind::DateCalendar(cal_kind, unit, Arc::new(repository)),
2168 self.make_source(span),
2169 );
2170 }
2171
2172 if self.at(&TokenKind::And)?
2173 || self.at(&TokenKind::Unless)?
2174 || self.at(&TokenKind::Then)?
2175 || self.at(&TokenKind::RParen)?
2176 || self.at(&TokenKind::Eof)?
2177 || is_comparison_operator(&self.peek()?.kind)
2178 {
2179 let end = self.peek()?.span.clone();
2180 let span = self.span_covering(&start_span, &end);
2181 return self.new_expression(
2182 ExpressionKind::DateRelative(rel_kind, Arc::new(repository)),
2183 self.make_source(span),
2184 );
2185 }
2186
2187 let offset = self.parse_repository_expression()?;
2188 let offset_end_span = offset
2189 .source_location
2190 .as_ref()
2191 .map(|s| s.span.clone())
2192 .unwrap_or_else(|| start_span.clone());
2193 let range = self.new_expression(
2194 ExpressionKind::PastFutureRange(rel_kind, Arc::new(offset)),
2195 self.make_source(self.span_covering(&direction.span, &offset_end_span)),
2196 )?;
2197 let span = self.span_covering(&start_span, &offset_end_span);
2198 return self.new_expression(
2199 ExpressionKind::RangeContainment(Arc::new(repository), Arc::new(range)),
2200 self.make_source(span),
2201 );
2202 }
2203
2204 if token_is_calendar_period_marker(peeked) {
2206 self.next_calendar_period_marker()?;
2207 let unit = self.parse_calendar_unit()?;
2208 let end = self.peek()?.span.clone();
2209 let span = self.span_covering(&start_span, &end);
2210 return self.new_expression(
2211 ExpressionKind::DateCalendar(DateCalendarKind::Current, unit, Arc::new(repository)),
2212 self.make_source(span),
2213 );
2214 }
2215
2216 let range = self.parse_range_expression()?;
2217 let end_span = range
2218 .source_location
2219 .as_ref()
2220 .map(|s| s.span.clone())
2221 .unwrap_or_else(|| start_span.clone());
2222 let span = self.span_covering(&start_span, &end_span);
2223 self.new_expression(
2224 ExpressionKind::RangeContainment(Arc::new(repository), Arc::new(range)),
2225 self.make_source(span),
2226 )
2227 }
2228
2229 fn parse_as_chain(
2230 &mut self,
2231 mut expr: Expression,
2232 start_span: Span,
2233 ) -> Result<Expression, Error> {
2234 while self.at(&TokenKind::As)? {
2235 self.expect(&TokenKind::As)?;
2236 let target_tok = self.next()?;
2237 let target = if matches!(target_tok.kind, TokenKind::Permille) {
2238 ConversionTarget::Unit {
2239 unit_name: "permille".to_string(),
2240 }
2241 } else if let Some(primitive) = token_kind_to_primitive(&target_tok.kind) {
2242 ConversionTarget::Type(primitive)
2243 } else if can_be_label(&target_tok.kind) {
2244 ConversionTarget::Unit {
2245 unit_name: target_tok.text.clone(),
2246 }
2247 } else {
2248 return Err(self.error_at_token(
2249 &target_tok,
2250 format!(
2251 "Expected a type keyword or unit name after 'as', found {}",
2252 target_tok.kind
2253 ),
2254 ));
2255 };
2256 expr = self.new_expression(
2257 ExpressionKind::UnitConversion(Arc::new(expr), target),
2258 self.make_source(self.span_covering(&start_span, &target_tok.span)),
2259 )?;
2260 }
2261 Ok(expr)
2262 }
2263
2264 fn is_plain_number_literal(expr: &Expression) -> bool {
2265 matches!(expr.kind, ExpressionKind::Literal(Value::Number(_)))
2266 }
2267
2268 fn is_unit_conversion(expr: &Expression) -> bool {
2269 matches!(expr.kind, ExpressionKind::UnitConversion(..))
2270 }
2271
2272 fn at_expression_suffix_end(&mut self) -> Result<bool, Error> {
2277 Ok(self.at(&TokenKind::And)?
2278 || self.at(&TokenKind::Unless)?
2279 || self.at(&TokenKind::Then)?
2280 || self.at(&TokenKind::RParen)?
2281 || self.at(&TokenKind::Eof)?
2282 || self.at(&TokenKind::Spec)?
2283 || self.at(&TokenKind::Repo)?
2284 || self.at(&TokenKind::Uses)?
2285 || is_spec_body_keyword(&self.peek()?.kind))
2286 }
2287
2288 fn parse_calendar_unit(&mut self) -> Result<CalendarPeriodUnit, Error> {
2289 let tok = self.next()?;
2290 if let Some(unit) = CalendarPeriodUnit::from_keyword(&tok.text) {
2291 return Ok(unit);
2292 }
2293 Err(self.error_at_token(
2294 &tok,
2295 format!("Expected 'year', 'month', or 'week', found '{}'", tok.text),
2296 ))
2297 }
2298
2299 fn parse_range_expression(&mut self) -> Result<Expression, Error> {
2304 self.parse_repository_expression()
2305 }
2306
2307 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_range_ellipsis_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_label(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_label(&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}