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