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