1use anyhow::{Result, anyhow, bail};
16use ast::{Arena, Constraints};
17use mangle_ast as ast;
18use std::io;
19
20mod error;
21mod quote;
22mod scan;
23mod token;
24
25pub use error::{ErrorContext, ParseError};
26use token::Token;
27
28pub struct Parser<'arena, R>
29where
30 R: io::Read,
31{
32 sc: scan::Scanner<R>,
33 token: crate::token::Token,
34 arena: &'arena Arena,
35 anon_counter: usize,
36}
37
38fn package_sym(arena: &Arena) -> ast::PredicateIndex {
39 arena.predicate_sym("Package", Some(0))
40}
41
42fn name_sym(arena: &Arena) -> ast::PredicateIndex {
43 arena.predicate_sym("name", Some(1))
44}
45
46fn use_sym(arena: &Arena) -> ast::PredicateIndex {
47 arena.predicate_sym("Use", Some(0))
48}
49
50fn lt_sym(arena: &Arena) -> ast::PredicateIndex {
51 arena.predicate_sym(":lt", Some(2))
52}
53
54fn le_sym(arena: &Arena) -> ast::PredicateIndex {
55 arena.predicate_sym(":le", Some(2))
56}
57
58fn gt_sym(arena: &Arena) -> ast::PredicateIndex {
59 arena.predicate_sym(":gt", Some(2))
60}
61
62fn ge_sym(arena: &Arena) -> ast::PredicateIndex {
63 arena.predicate_sym(":ge", Some(2))
64}
65
66fn fn_list_sym(arena: &Arena) -> ast::FunctionIndex {
67 arena.function_sym("fn:list", None)
68}
69
70fn fn_map_sym(arena: &Arena) -> ast::FunctionIndex {
71 arena.function_sym("fn:map", None)
72}
73
74fn fn_struct_sym(arena: &Arena) -> ast::FunctionIndex {
75 arena.function_sym("fn:struct", None)
76}
77
78fn fn_list_type_sym(arena: &Arena) -> ast::FunctionIndex {
79 arena.function_sym("fn:List", None)
80}
81
82fn fn_option_type_sym(arena: &Arena) -> ast::FunctionIndex {
83 arena.function_sym("fn:Option", None)
84}
85
86fn fn_opt_sym(arena: &Arena) -> ast::FunctionIndex {
87 arena.function_sym("fn:opt", None)
88}
89
90fn empty_package_decl(arena: &Arena) -> ast::Decl<'_> {
91 ast::Decl {
92 atom: arena.alloc(ast::Atom {
93 sym: package_sym(arena),
94 args: &[],
95 }),
96 is_temporal: false,
97 descr: arena.alloc_slice_copy(&[arena.alloc(ast::Atom {
98 sym: name_sym(arena),
99 args: arena
100 .alloc_slice_copy(&[arena.alloc(ast::BaseTerm::Const(ast::Const::String("")))]),
101 })]),
102 bounds: None,
103 constraints: None,
104 }
105}
106
107macro_rules! alloc {
108 ($self:expr, $e:expr) => {
109 &*$self.arena.alloc($e)
110 };
111}
112
113macro_rules! alloc_str {
114 ($self:expr, $e:expr) => {
115 &*$self.arena.alloc_str($e)
116 };
117}
118
119macro_rules! alloc_slice {
120 ($self:expr, $e:expr) => {
121 &*$self.arena.alloc_slice_copy($e)
122 };
123}
124
125impl<'arena, R> Parser<'arena, R>
126where
127 R: io::Read,
128{
129 pub fn new<P: ToString>(arena: &'arena Arena, reader: R, path: P) -> Self
130 where
131 R: io::Read,
132 {
133 Self {
134 sc: scan::Scanner::new(reader, path),
135 token: token::Token::Illegal,
136 arena,
137 anon_counter: 0,
138 }
139 }
140
141 pub fn next_token(&mut self) -> Result<()> {
142 self.token = self.sc.next_token()?;
143 Ok(())
144 }
145
146 fn expect(&mut self, expected: Token) -> Result<()> {
148 if expected != self.token {
149 let error = ParseError::Unexpected(
150 self.sc.get_error_context(),
151 expected.clone(),
152 self.token.clone(),
153 );
154 return Err(anyhow!(error));
155 }
156 self.next_token()
157 }
158
159 pub fn parse_unit(&mut self) -> Result<&'arena ast::Unit<'arena>> {
160 let package = if matches!(self.token.clone(), Token::Package) {
161 self.parse_package_decl()?
162 } else {
163 self.arena.alloc(empty_package_decl(self.arena))
164 };
165 let mut decls = vec![package];
166 while let Token::Use = self.token {
167 decls.push(self.parse_use_decl()?);
168 }
169 let mut clauses = vec![];
170 loop {
171 match self.token {
172 Token::Eof => break,
173 Token::Decl => decls.push(self.parse_decl()?),
174 _ => clauses.push(self.parse_clause()?),
175 }
176 }
177 let decls: &'arena [&'arena ast::Decl<'arena>] = self.arena.alloc_slice_copy(&decls);
178 let clauses: &'arena [&'arena ast::Clause<'arena>] = self.arena.alloc_slice_copy(&clauses);
179 let unit: &'arena ast::Unit<'arena> = &*self.arena.alloc(ast::Unit { clauses, decls });
180 Ok(unit)
181 }
182
183 pub fn parse_package_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
185 self.expect(Token::Package)?;
186 let package_name: &'arena str = if let Token::Ident { name } = &self.token {
187 self.arena.alloc_str(name.as_str())
188 } else {
189 bail!("expected identifer got {}", self.token);
190 };
191
192 let name_atom: &'arena ast::Atom<'arena> = self.arena.alloc(ast::Atom {
193 sym: name_sym(self.arena),
194 args: self.arena.alloc_slice_copy(&[self
195 .arena
196 .alloc(ast::BaseTerm::Const(ast::Const::String(package_name)))]),
197 });
198 let mut descr_atoms: Vec<&'arena ast::Atom<'arena>> = vec![name_atom];
199 self.next_token()?;
200 if Token::LBracket == self.token {
201 self.parse_bracket_atoms(&mut descr_atoms)?;
202 }
203 let descr = alloc_slice!(self, &descr_atoms);
204
205 self.expect(Token::Bang)?;
206
207 let package_atom = alloc!(
208 self,
209 ast::Atom {
210 sym: package_sym(self.arena),
211 args: &[]
212 }
213 );
214
215 let decl: &'arena ast::Decl = alloc!(
217 self,
218 ast::Decl {
219 atom: package_atom,
220 bounds: None,
221 descr,
222 constraints: None,
223 is_temporal: false,
224 }
225 );
226 Ok(decl)
227 }
228
229 fn parse_use_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
230 self.expect(Token::Use)?;
231 let use_atom = alloc!(
232 self,
233 ast::Atom {
234 sym: use_sym(self.arena),
235 args: &[]
236 }
237 );
238
239 let name = match &self.token {
240 Token::Ident { name } => name.as_str(),
241 _ => bail!("parse_use_decl: expected identifer got {}", self.token),
242 };
243
244 let name: &'arena str = alloc_str!(self, name);
245 let name = alloc!(self, ast::BaseTerm::Const(ast::Const::String(name)));
246 let args = alloc_slice!(self, &[name]);
247
248 let mut descr_atoms: Vec<&ast::Atom> = vec![self.arena.alloc(ast::Atom {
249 sym: name_sym(self.arena),
250 args,
251 })];
252 self.next_token()?;
253 if Token::LBracket == self.token {
254 self.parse_bracket_atoms(&mut descr_atoms)?;
255 }
256 self.expect(Token::Bang)?;
257
258 let descr_atoms = alloc_slice!(self, &descr_atoms);
259 Ok(alloc!(
260 self,
261 ast::Decl {
262 atom: use_atom,
263 descr: descr_atoms,
264 bounds: None,
265 constraints: None,
266 is_temporal: false,
267 }
268 ))
269 }
270
271 fn parse_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
272 self.expect(Token::Decl)?;
273 let atom = self.parse_atom()?;
274 let is_temporal = match &self.token {
276 Token::Ident { name } if name == "temporal" => {
277 self.next_token()?;
278 true
279 }
280 _ => false,
281 };
282 let mut descr_atoms = vec![];
283 if Token::Descr == self.token {
284 self.next_token()?;
285 self.parse_bracket_atoms(&mut descr_atoms)?;
286 }
287 let mut bound_decls = vec![];
288 loop {
289 if Token::Bound != self.token {
290 break;
291 }
292 bound_decls.push(self.parse_bounds_decl()?);
293 }
294 let bounds = if bound_decls.is_empty() {
295 None
296 } else {
297 Some(alloc_slice!(self, &bound_decls))
298 };
299 let constraints = if Token::Inclusion == self.token {
300 Some(self.parse_inclusion_constraint()?)
301 } else {
302 None
303 };
304 self.expect(Token::Dot)?;
305 Ok(alloc!(
306 self,
307 ast::Decl {
308 atom,
309 descr: alloc_slice!(self, &descr_atoms),
310 bounds,
311 constraints,
312 is_temporal,
313 }
314 ))
315 }
316
317 fn parse_bounds_decl(&mut self) -> Result<&'arena ast::BoundDecl<'arena>> {
319 self.expect(Token::Bound)?;
320 self.expect(Token::LBracket)?;
321 let mut base_terms = vec![];
322 self.parse_base_terms(&mut base_terms)?;
323 self.expect(Token::RBracket)?;
324 let base_terms = alloc_slice!(self, &base_terms);
325 let bound_decl = alloc!(self, ast::BoundDecl { base_terms });
326 Ok(bound_decl)
327 }
328
329 fn parse_inclusion_constraint(&mut self) -> Result<&'arena ast::Constraints<'arena>> {
330 self.expect(Token::Inclusion)?;
331 let mut consequences = vec![];
332 self.parse_bracket_atoms(&mut consequences)?;
333 let consequences = alloc_slice!(self, &consequences);
334 Ok(alloc!(
335 self,
336 Constraints {
337 consequences,
338 alternatives: &[]
339 }
340 ))
341 }
342
343 pub fn parse_clause(&mut self) -> Result<&'arena ast::Clause<'arena>> {
344 let head = self.parse_atom()?;
345 let head_time = self.try_parse_interval()?;
346 let mut premises = vec![];
347 let mut transform = vec![];
348 match self.token {
349 Token::ColonDash | Token::LongLeftDoubleArrow => {
350 self.next_token()?;
351 self.parse_terms(&mut premises)?;
352 if let Token::PipeGt = self.token {
353 self.next_token()?;
354 self.parse_transforms(&mut transform)?;
355 }
356 }
357 _ => {}
358 }
359 self.expect(Token::Dot)?;
360 let premises = alloc_slice!(self, &premises);
361 let transform = alloc_slice!(self, &transform);
362 Ok(alloc!(
363 self,
364 ast::Clause {
365 head,
366 head_time,
367 premises,
368 transform,
369 }
370 ))
371 }
372
373 fn parse_terms(&mut self, terms: &mut Vec<&'arena ast::Term<'arena>>) -> Result<()> {
375 terms.push(self.parse_term()?);
376 loop {
377 if Token::Comma != self.token {
378 return Ok(());
379 }
380 self.next_token()?;
381 terms.push(self.parse_term()?);
382 }
383 }
384
385 pub fn parse_term(&mut self) -> Result<&'arena ast::Term<'arena>> {
386 match &self.token {
387 Token::Bang => {
388 self.next_token()?;
389 let atom = self.parse_atom()?;
390 Ok(alloc!(self, ast::Term::NegAtom(atom)))
391 }
392 t if base_term_start(t) => {
393 let leading_var: Option<String> = match &self.token {
394 Token::Ident { name } if is_variable(name) => Some(name.clone()),
395 _ => None,
396 };
397 let left_base_term = self.parse_base_term()?;
398 let op = self.token.clone();
399 match op {
400 Token::Eq | Token::BangEq | Token::Lt | Token::Le | Token::Gt | Token::Ge => self.next_token()?,
401 Token::LParen if leading_var.is_some() => {
402 let name = leading_var.unwrap();
403 bail!(
404 "{}: `{}` starts with an uppercase letter and is therefore a variable; \
405 predicate names must start with a lowercase letter. Did you mean `{}`?",
406 self.sc.get_error_context(),
407 name,
408 lowercase_first(&name)
409 );
410 }
411 _ => bail!("parse_terms: expected comparison operator, got {}", self.token),
412 };
413 let right_base_term = self.parse_base_term()?;
414 let term = match op {
415 Token::Eq => ast::Term::Eq(left_base_term, right_base_term),
416 Token::BangEq => ast::Term::Ineq(left_base_term, right_base_term),
417 Token::Lt => ast::Term::Atom(alloc!(
418 self,
419 ast::Atom {
420 sym: lt_sym(self.arena),
421 args: alloc_slice!(self, &[left_base_term, right_base_term]),
422 }
423 )),
424 Token::Le => ast::Term::Atom(self.arena.alloc(ast::Atom {
425 sym: le_sym(self.arena),
426 args: alloc_slice!(self, &[left_base_term, right_base_term]),
427 })),
428 Token::Gt => ast::Term::Atom(alloc!(
429 self,
430 ast::Atom {
431 sym: gt_sym(self.arena),
432 args: alloc_slice!(self, &[left_base_term, right_base_term]),
433 }
434 )),
435 Token::Ge => ast::Term::Atom(self.arena.alloc(ast::Atom {
436 sym: ge_sym(self.arena),
437 args: alloc_slice!(self, &[left_base_term, right_base_term]),
438 })),
439 _ => unreachable!(),
440 };
441 Ok(alloc!(self, term))
442 }
443 Token::Ident { .. } => {
444 let atom = self.parse_atom()?;
445 if let Some(interval) = self.try_parse_interval()? {
446 Ok(alloc!(self, ast::Term::TemporalAtom(atom, interval)))
447 } else {
448 Ok(alloc!(self, ast::Term::Atom(atom)))
449 }
450 }
451 _ => bail!("parse_term: unexpected token {:?}", self.token),
452 }
453 }
454
455 fn parse_bracket_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
457 self.expect(Token::LBracket)?;
458 self.parse_atoms(atoms)?;
459 self.expect(Token::RBracket)?;
460 Ok(())
461 }
462
463 fn parse_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
465 if let Token::Ident { .. } = self.token {
466 atoms.push(self.parse_atom()?);
467 loop {
468 if Token::Comma != self.token {
469 break;
470 }
471 self.next_token()?;
472 if !matches!(self.token, Token::Ident { .. }) {
473 break; }
475 atoms.push(self.parse_atom()?);
476 }
477 }
478 Ok(())
479 }
480
481 pub fn parse_atom(&mut self) -> Result<&'arena ast::Atom<'arena>> {
484 let mut name_buf = match &self.token {
485 Token::Ident { name } => {
486 if is_variable(name) {
487 bail!(
488 "{}: `{}` is not a valid predicate name: predicate names must start \
489 with a lowercase letter (identifiers starting with an uppercase letter \
490 are variables). Did you mean `{}`?",
491 self.sc.get_error_context(),
492 name,
493 lowercase_first(name)
494 );
495 }
496 name.clone()
497 }
498 _ => bail!("parse_atom: expected identifer got {}", self.token),
499 };
500
501 self.next_token()?;
502
503 while self.token == Token::Dot {
505 self.next_token()?;
509 match &self.token {
510 Token::Ident { name: next_name } => {
511 name_buf.push('.');
512 name_buf.push_str(next_name);
513 self.next_token()?;
514 }
515 _ => {
516 bail!(
522 "parse_atom: expected identifier after `.` in qualified name, got {}",
523 self.token
524 );
525 }
526 }
527 }
528
529 let name = self.arena.alloc_str(&name_buf);
530
531 self.expect(Token::LParen)?;
532 let mut args = vec![];
533 if Token::RParen != self.token {
534 self.parse_base_terms(&mut args)?;
535 }
536 self.expect(Token::RParen)?;
537 let args = alloc_slice!(self, &args);
538 Ok(alloc!(
539 self,
540 ast::Atom {
541 sym: self.arena.predicate_sym(name, None),
542 args
543 }
544 ))
545 }
546
547 fn parse_transforms(
548 &mut self,
549 transforms: &mut Vec<&'arena ast::TransformStmt<'arena>>,
550 ) -> Result<()> {
551 if Token::Do == self.token {
554 self.next_token()?;
555 let expr = self.parse_base_term()?;
556 transforms.push(alloc!(
557 self,
558 ast::TransformStmt {
559 var: None,
560 app: expr
561 }
562 ));
563 self.expect_transform_sep()?;
564 }
565 loop {
566 if Token::Let != self.token {
567 break;
568 }
569 self.next_token()?;
570 if let Token::Ident { name } = &self.token {
571 let name = alloc_str!(self, name.as_str());
572 self.next_token()?;
573 self.expect(Token::Eq)?;
574 let expr = self.parse_base_term()?;
575 transforms.push(alloc!(
576 self,
577 ast::TransformStmt {
578 var: Some(name),
579 app: expr
580 }
581 ))
582 }
583 if let Token::Dot = self.token {
584 break;
585 }
586 self.expect_transform_sep()?;
587 }
588 Ok(())
589 }
590
591 fn expect_transform_sep(&mut self) -> Result<()> {
592 match self.token {
593 Token::Comma | Token::Semi => self.next_token(),
594 _ => bail!(
595 "{}: expected `,` or `;` got {}",
596 self.sc.get_error_context(),
597 self.token
598 ),
599 }
600 }
601
602 fn try_parse_interval(&mut self) -> Result<Option<ast::Interval>> {
608 if self.token != Token::At {
609 return Ok(None);
610 }
611 self.next_token()?; self.expect(Token::LBracket)?;
613 let start = self.parse_temporal_bound(true)?;
614 let end = if self.token == Token::Comma {
615 self.next_token()?;
616 self.parse_temporal_bound(false)?
617 } else {
618 start
620 };
621 self.expect(Token::RBracket)?;
622 Ok(Some(ast::Interval { start, end }))
623 }
624
625 fn parse_temporal_bound(&mut self, is_start: bool) -> Result<ast::TemporalBound> {
627 match &self.token {
628 Token::Timestamp { nanos } => {
629 let nanos = *nanos;
630 self.next_token()?;
631 Ok(ast::TemporalBound::Timestamp(nanos))
632 }
633 Token::Ident { name } if name == "_" => {
634 self.next_token()?;
635 if is_start {
636 Ok(ast::TemporalBound::NegInf)
637 } else {
638 Ok(ast::TemporalBound::PosInf)
639 }
640 }
641 Token::Ident { name } if is_variable(name) => {
642 let var_idx = self.arena.variable_sym(name);
643 self.next_token()?;
644 Ok(ast::TemporalBound::Variable(var_idx))
645 }
646 _ => bail!("parse_temporal_bound: expected timestamp, variable, or '_', got {:?}", self.token),
647 }
648 }
649
650 pub fn parse_base_term(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
660 match &self.token {
661 Token::LBracket => return self.parse_list_or_map(),
662 Token::LBrace => return self.parse_struct(),
663 _ => {}
664 }
665
666 let mut is_type = false;
667 let mut base_term = match &self.token {
668 Token::Ident { name } if name == "_" => {
669 let unique = format!("_Anon{}", self.anon_counter);
670 self.anon_counter += 1;
671 ast::BaseTerm::Variable(self.arena.variable_sym(&unique))
672 }
673 Token::Ident { name } if is_variable(name) => {
674 ast::BaseTerm::Variable(self.arena.variable_sym(name))
675 }
676 Token::Ident { name } if is_fn(name) => {
677 let name = self.arena.alloc_str(name);
678 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
680 }
681 Token::DotIdent { name } => {
682 let name = self.arena.alloc_str(name);
683 is_type = true;
684 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
686 }
687 Token::String { decoded } => {
688 let value = self.arena.alloc_str(decoded.as_str());
689 ast::BaseTerm::Const(ast::Const::String(value))
690 }
691 Token::Bytes { decoded } => {
692 let value = self.arena.alloc_slice_copy(decoded);
693 ast::BaseTerm::Const(ast::Const::Bytes(value))
694 }
695 Token::Int { decoded } => ast::BaseTerm::Const(ast::Const::Number(*decoded)),
696 Token::Float { decoded } => ast::BaseTerm::Const(ast::Const::Float(*decoded)),
697 Token::Timestamp { nanos } => ast::BaseTerm::Const(ast::Const::Time(*nanos)),
698 Token::Duration { nanos } => ast::BaseTerm::Const(ast::Const::Duration(*nanos)),
699 Token::Name { name } => {
700 let name = self.arena.intern(name);
701 ast::BaseTerm::Const(ast::Const::Name(name))
702 }
703 _ => bail!("parse_base_term: unexpected token {:?}", self.token),
704 };
705 self.next_token()?;
706 if let ast::BaseTerm::ApplyFn(fn_sym, _) = base_term {
707 let mut fn_args = vec![];
708 if is_type {
709 self.parse_langle_base_terms(&mut fn_args)?;
710 } else {
711 self.parse_paren_base_terms(&mut fn_args)?;
712 }
713 let fn_args = self.arena.alloc_slice_copy(&fn_args);
714 base_term = ast::BaseTerm::ApplyFn(fn_sym, fn_args);
715 }
716 let base_term = alloc!(self, base_term);
717 Ok(base_term)
718 }
719
720 fn parse_list_or_map(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
721 self.expect(Token::LBracket)?;
722 if Token::RBracket == self.token {
723 self.next_token()?;
724 return Ok(alloc!(
725 self,
726 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), &[])
727 ));
728 }
729 let first = self.parse_base_term()?;
730 let expr = if Token::Colon != self.token {
731 let mut items = vec![first];
732 if Token::Comma == self.token {
733 self.next_token()?;
734 if Token::RBracket != self.token {
735 self.parse_base_terms(&mut items)?;
736 }
737 }
738 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), alloc_slice!(self, &items))
741 } else {
742 self.expect(Token::Colon)?; let first_val = self.parse_base_term()?;
744 let mut items = vec![first, first_val];
745 loop {
746 if Token::Comma != self.token {
747 break;
748 }
749 self.next_token()?;
750 if Token::RBracket == self.token {
751 break; }
753 items.push(self.parse_base_term()?);
754 self.expect(Token::Colon)?;
755 items.push(self.parse_base_term()?);
756 }
757 ast::BaseTerm::ApplyFn(fn_map_sym(self.arena), alloc_slice!(self, &items))
758 };
759 self.expect(Token::RBracket)?;
760 Ok(alloc!(self, expr))
761 }
762
763 fn parse_struct(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
764 self.expect(Token::LBrace)?;
765 if Token::RBrace == self.token {
766 self.next_token()?;
767 return Ok(alloc!(
768 self,
769 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), &[])
770 ));
771 }
772 let mut items = vec![];
773 let name = self.parse_base_term()?;
774 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
775 items.push(name)
776 } else {
777 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}",);
778 }
779 self.expect(Token::Colon)?;
780 items.push(self.parse_base_term()?);
781 loop {
782 if Token::Comma != self.token {
783 break;
784 }
785 self.next_token()?;
786 if Token::RBrace == self.token {
787 break; }
789 let name = self.parse_base_term()?;
790 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
791 items.push(name)
792 } else {
793 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}");
794 }
795 self.expect(Token::Colon)?;
796 items.push(self.parse_base_term()?);
797 }
798 self.expect(Token::RBrace)?;
799 Ok(alloc!(
800 self,
801 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), alloc_slice!(self, &items))
802 ))
803 }
804
805 fn parse_langle_base_terms(
816 &mut self,
817 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
818 ) -> Result<()> {
819 self.expect(Token::Lt)?;
820 if Token::Gt == self.token {
821 self.next_token()?;
822 return Ok(());
823 }
824 self.parse_langle_member(base_terms)?;
825 while Token::Comma == self.token {
826 self.next_token()?;
827 if !base_term_start(&self.token) && !self.is_opt_keyword() {
828 break; }
830 self.parse_langle_member(base_terms)?;
831 }
832 self.expect(Token::Gt)?;
833 Ok(())
834 }
835
836 fn is_opt_keyword(&self) -> bool {
837 matches!(&self.token, Token::Ident { name } if name == "opt")
838 }
839
840 fn parse_langle_member(
841 &mut self,
842 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
843 ) -> Result<()> {
844 if self.is_opt_keyword() {
845 self.next_token()?;
846 let name = self.parse_base_term()?;
847 self.expect(Token::Colon)?;
848 let ty = self.parse_base_term()?;
849 let opt = alloc!(
850 self,
851 ast::BaseTerm::ApplyFn(fn_opt_sym(self.arena), alloc_slice!(self, &[name, ty]))
852 );
853 base_terms.push(opt);
854 return Ok(());
855 }
856 base_terms.push(self.parse_base_term()?);
857 if Token::Colon == self.token {
858 self.next_token()?;
859 base_terms.push(self.parse_base_term()?);
860 }
861 Ok(())
862 }
863
864 fn parse_paren_base_terms(
866 &mut self,
867 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
868 ) -> Result<()> {
869 self.expect(Token::LParen)?;
870 if Token::RParen != self.token {
871 self.parse_base_terms(base_terms)?;
872 }
873 self.expect(Token::RParen)?;
874 Ok(())
875 }
876
877 fn parse_base_terms(
879 &mut self,
880 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
881 ) -> Result<()> {
882 base_terms.push(self.parse_base_term()?);
883 while let Token::Comma = self.token {
884 self.next_token()?;
885 if !base_term_start(&self.token) {
886 break; }
888 base_terms.push(self.parse_base_term()?);
889 }
890
891 Ok(())
892 }
893}
894
895fn is_variable(name: &str) -> bool {
896 name.chars().next().unwrap().is_ascii_uppercase()
897}
898
899fn lowercase_first(name: &str) -> String {
900 let mut chars = name.chars();
901 match chars.next() {
902 Some(c) => c.to_ascii_lowercase().to_string() + chars.as_str(),
903 None => String::new(),
904 }
905}
906
907fn is_fn(name: &str) -> bool {
908 name.starts_with("fn:")
909}
910
911fn base_term_start(t: &Token) -> bool {
912 match t {
913 Token::Name { .. }
914 | Token::Int { .. }
915 | Token::Float { .. }
916 | Token::String { .. }
917 | Token::Bytes { .. }
918 | Token::Timestamp { .. }
919 | Token::Duration { .. }
920 | Token::LBracket
921 | Token::LBrace
922 | Token::DotIdent { .. } => true,
923 Token::Ident { name } => is_variable(name) || is_fn(name) || name == "_",
924 _ => false,
925 }
926}
927
928#[cfg(test)]
929mod test {
930
931 use super::*;
932 use googletest::prelude::{eq, gtest, verify_that};
933
934 fn make_parser<'arena>(
935 arena: &'arena Arena,
936 input: &'arena str,
937 ) -> Parser<'arena, &'arena [u8]> {
938 let mut p = Parser::new(arena, input.as_bytes(), "test");
939 p.next_token().unwrap();
940 p
941 }
942
943 #[test]
944 fn test_empty_unit() -> Result<()> {
945 let arena = Arena::new_with_global_interner();
946 let mut p = make_parser(&arena, "");
947 match p.parse_unit()? {
948 &ast::Unit { decls: &[pkg], .. } => {
949 assert_eq!(pkg, &empty_package_decl(&arena));
950 }
951 z => panic!("unexpected: {:?}", z),
952 }
953 Ok(())
954 }
955
956 #[test]
957 fn test_package_use() -> Result<()> {
958 let arena = Arena::new_with_global_interner();
959 let input = "Package foo[bar()]! Use baz[bar()]!";
960
961 let mut p = make_parser(&arena, input);
962 let unit = p.parse_unit()?;
963 match unit.decls {
964 &[
965 &ast::Decl {
966 atom:
967 &ast::Atom {
968 sym: got_package_sym,
969 ..
970 },
971 descr:
972 &[
973 &ast::Atom {
974 sym: got_name_sym1,
975 args: &[ast::BaseTerm::Const(ast::Const::String("foo"))],
976 },
977 &ast::Atom {
978 sym: got_bar_sym1,
979 args: &[],
980 },
981 ],
982 ..
983 },
984 &ast::Decl {
985 atom:
986 &ast::Atom {
987 sym: got_use_sym, ..
988 },
989 descr:
990 &[
991 &ast::Atom {
992 sym: got_name_sym2,
993 args: &[ast::BaseTerm::Const(ast::Const::String("baz"))],
994 },
995 &ast::Atom {
996 sym: got_bar_sym2,
997 args: &[],
998 },
999 ],
1000 ..
1001 },
1002 ] => {
1003 assert_eq!(got_use_sym, use_sym(&arena));
1004 assert_eq!(got_package_sym, package_sym(&arena));
1005 assert_eq!(got_name_sym1, name_sym(&arena));
1006 assert_eq!(got_name_sym2, name_sym(&arena));
1007 assert_eq!(got_bar_sym1, arena.predicate_sym("bar", None));
1008 assert_eq!(got_bar_sym2, arena.predicate_sym("bar", None));
1009 }
1010 z => panic!("unexpected {z:?}"),
1011 }
1012 Ok(())
1013 }
1014
1015 #[test]
1016 fn test_decl() -> Result<()> {
1017 let arena = Arena::new_with_global_interner();
1018 let input = "Decl foo(X, Y).";
1019 let mut p = make_parser(&arena, input);
1020 match p.parse_decl()? {
1021 &ast::Decl {
1022 atom:
1023 &ast::Atom {
1024 sym: got_foo_sym,
1025 args:
1026 &[
1027 &ast::BaseTerm::Variable(x_sym),
1028 &ast::BaseTerm::Variable(y_sym),
1029 ],
1030 },
1031 ..
1032 } => {
1033 assert_eq!(got_foo_sym, arena.predicate_sym("foo", None));
1034 assert_eq!(x_sym, arena.variable_sym("X"));
1035 assert_eq!(y_sym, arena.variable_sym("Y"))
1036 }
1037 decl => panic!("got {:?}", decl),
1038 };
1039 Ok(())
1040 }
1041
1042 #[test]
1043 fn test_base_term() -> googletest::Result<()> {
1044 let arena = Arena::new_with_global_interner();
1045 let input = "X 3 1.5 'foo' /foo fn:list() fn:list(/a) fn:list(/a, 3)"; let mut p = make_parser(&arena, input);
1047 let mut got_base_terms = vec![];
1048 loop {
1049 if Token::Eof == p.token {
1050 break;
1051 }
1052 let base_term = p.parse_base_term().unwrap();
1054 got_base_terms.push(base_term);
1055 }
1056 let expected = vec![
1057 arena.variable("X"),
1058 arena.const_(ast::Const::Number(3)),
1059 arena.const_(ast::Const::Float(1.5)),
1060 arena.const_(ast::Const::String("foo")),
1061 arena.const_(arena.name("/foo")),
1062 arena.apply_fn(fn_list_sym(&arena), &[]),
1063 arena.apply_fn(fn_list_sym(&arena), &[arena.const_(arena.name("/a"))]),
1064 arena.apply_fn(
1065 fn_list_sym(&arena),
1066 &[
1067 arena.const_(arena.name("/a")),
1068 arena.const_(ast::Const::Number(3)),
1069 ],
1070 ),
1071 ];
1072 verify_that!(got_base_terms, eq(&expected))
1073 }
1074
1075 #[test]
1076 fn test_term() -> googletest::Result<()> {
1077 let arena = Arena::new_with_global_interner();
1078 let input = "foo(/bar) !bar() X = Z X != 3 3 < 1 3 <= 1";
1079 let mut p = make_parser(&arena, input);
1080 let mut got_terms = vec![];
1081 loop {
1082 if Token::Eof == p.token {
1083 break;
1084 }
1085 got_terms.push(p.parse_term().unwrap());
1087 }
1088 let expected = [
1089 &ast::Term::Atom(arena.atom(
1090 arena.predicate_sym("foo", None),
1091 &[arena.const_(arena.name("/bar"))],
1092 )),
1093 &ast::Term::NegAtom(arena.atom(arena.predicate_sym("bar", None), &[])),
1094 &ast::Term::Eq(arena.variable("X"), arena.variable("Z")),
1095 &ast::Term::Ineq(
1096 arena.variable("X"),
1097 arena.alloc(ast::BaseTerm::Const(ast::Const::Number(3))),
1098 ),
1099 &ast::Term::Atom(arena.atom(
1100 arena.predicate_sym(":lt", Some(2)),
1101 &[
1102 arena.const_(ast::Const::Number(3)),
1103 arena.const_(ast::Const::Number(1)),
1104 ],
1105 )),
1106 &ast::Term::Atom(arena.atom(
1107 arena.predicate_sym(":le", Some(2)),
1108 &[
1109 arena.const_(ast::Const::Number(3)),
1110 arena.const_(ast::Const::Number(1)),
1111 ],
1112 )),
1113 ];
1114 verify_that!(got_terms, eq(&expected))
1115 }
1116
1117 #[gtest]
1118 fn test_structured_data_and_types() -> googletest::Result<()> {
1119 let arena = Arena::new_with_global_interner();
1120 let input =
1121 "[] [1,2,3] [1: 'one', 2: 'two'] {} {/foo: /bar} {/name: \"alice\", /age: 30} .List<.Option</name>, /string>";
1122 let mut p = make_parser(&arena, input);
1123 let mut got_base_terms = vec![];
1124 loop {
1125 if Token::Eof == p.token {
1126 break;
1127 }
1128 let base_term = p.parse_base_term().unwrap();
1130 got_base_terms.push(base_term);
1131 }
1132 let expected = vec![
1133 arena.apply_fn(fn_list_sym(&arena), &[]),
1134 arena.apply_fn(
1135 fn_list_sym(&arena),
1136 &[
1137 arena.const_(ast::Const::Number(1)),
1138 arena.const_(ast::Const::Number(2)),
1139 arena.const_(ast::Const::Number(3)),
1140 ],
1141 ),
1142 arena.apply_fn(
1143 fn_map_sym(&arena),
1144 &[
1145 arena.const_(ast::Const::Number(1)),
1146 arena.const_(ast::Const::String("one")),
1147 arena.const_(ast::Const::Number(2)),
1148 arena.const_(ast::Const::String("two")),
1149 ],
1150 ),
1151 arena.apply_fn(fn_struct_sym(&arena), &[]),
1152 arena.apply_fn(
1153 fn_struct_sym(&arena),
1154 &[
1155 arena.const_(arena.name("/foo")),
1156 arena.const_(arena.name("/bar")),
1157 ],
1158 ),
1159 arena.apply_fn(
1160 fn_struct_sym(&arena),
1161 &[
1162 arena.const_(arena.name("/name")),
1163 arena.const_(ast::Const::String("alice")),
1164 arena.const_(arena.name("/age")),
1165 arena.const_(ast::Const::Number(30)),
1166 ],
1167 ),
1168 arena.apply_fn(
1169 fn_list_type_sym(&arena),
1170 &[
1171 arena.apply_fn(
1172 fn_option_type_sym(&arena),
1173 &[arena.const_(arena.name("/name"))],
1174 ),
1175 arena.const_(arena.name("/string")),
1176 ],
1177 ),
1178 ];
1179 verify_that!(got_base_terms, eq(&expected))
1180 }
1181
1182 #[gtest]
1183 fn test_trailing_commas() -> googletest::Result<()> {
1184 let arena = Arena::new_with_global_interner();
1185 let input = "[1, 2, 3,] [1: 'one', 2: 'two',] {/a: 1, /b: 2,}";
1186 let mut p = make_parser(&arena, input);
1187 let mut got_base_terms = vec![];
1188 loop {
1189 if Token::Eof == p.token {
1190 break;
1191 }
1192 let base_term = p.parse_base_term().unwrap();
1193 got_base_terms.push(base_term);
1194 }
1195 let expected = vec![
1196 arena.apply_fn(
1197 fn_list_sym(&arena),
1198 &[
1199 arena.const_(ast::Const::Number(1)),
1200 arena.const_(ast::Const::Number(2)),
1201 arena.const_(ast::Const::Number(3)),
1202 ],
1203 ),
1204 arena.apply_fn(
1205 fn_map_sym(&arena),
1206 &[
1207 arena.const_(ast::Const::Number(1)),
1208 arena.const_(ast::Const::String("one")),
1209 arena.const_(ast::Const::Number(2)),
1210 arena.const_(ast::Const::String("two")),
1211 ],
1212 ),
1213 arena.apply_fn(
1214 fn_struct_sym(&arena),
1215 &[
1216 arena.const_(arena.name("/a")),
1217 arena.const_(ast::Const::Number(1)),
1218 arena.const_(arena.name("/b")),
1219 arena.const_(ast::Const::Number(2)),
1220 ],
1221 ),
1222 ];
1223 verify_that!(got_base_terms, eq(&expected))
1224 }
1225
1226 #[test]
1227 fn test_single_element_list() -> googletest::Result<()> {
1228 let arena = Arena::new_with_global_interner();
1229 let mut p = make_parser(&arena, "[42]");
1230 let got = p.parse_base_term().unwrap();
1231 let expected = arena.apply_fn(
1232 fn_list_sym(&arena),
1233 &[arena.const_(ast::Const::Number(42))],
1234 );
1235 verify_that!(got, eq(expected))
1236 }
1237
1238 #[test]
1239 fn test_descr_trailing_comma() -> googletest::Result<()> {
1240 let arena = Arena::new_with_global_interner();
1242 let mut p = make_parser(&arena, "Decl foo(X) descr [ bar(), ].");
1243 p.parse_decl().expect("descr list with trailing comma parses");
1244 Ok(())
1245 }
1246
1247 #[test]
1248 fn test_opt_in_struct_type() -> googletest::Result<()> {
1249 let arena = Arena::new_with_global_interner();
1253 let mut p = make_parser(&arena, ".Struct</x : /number, opt /y : /string>");
1254 let got = p.parse_base_term().unwrap();
1255 let opt_inner = arena.apply_fn(
1256 fn_opt_sym(&arena),
1257 &[arena.const_(arena.name("/y")), arena.const_(arena.name("/string"))],
1258 );
1259 let expected = arena.apply_fn(
1260 arena.function_sym("fn:Struct", None),
1261 &[
1262 arena.const_(arena.name("/x")),
1263 arena.const_(arena.name("/number")),
1264 opt_inner,
1265 ],
1266 );
1267 verify_that!(got, eq(expected))
1268 }
1269
1270 #[test]
1271 fn test_transform_sep_comma_and_semi() -> googletest::Result<()> {
1272 let arena = Arena::new_with_global_interner();
1275 let src_comma = "q(K, S) :- p(K, V) |> do fn:group_by(K), let S = fn:sum(V).";
1276 let src_semi = "q(K, S) :- p(K, V) |> do fn:group_by(K); let S = fn:sum(V).";
1277 let a = make_parser(&arena, src_comma).parse_clause().unwrap();
1278 let b = make_parser(&arena, src_semi).parse_clause().unwrap();
1279 verify_that!(a.transform.len(), eq(2))?;
1280 verify_that!(b.transform.len(), eq(2))
1281 }
1282
1283 #[test]
1284 fn test_clause() -> Result<()> {
1285 let arena = Arena::new_with_global_interner();
1286 let mut p = make_parser(&arena, "foo(X).");
1287 let clause = p.parse_clause()?;
1288 match clause {
1289 &ast::Clause {
1290 head:
1291 &ast::Atom {
1292 args: &[ast::BaseTerm::Variable(x_sym)],
1293 ..
1294 },
1295 premises: &[],
1296 transform: &[],
1297 ..
1298 } => {
1299 assert_eq!(*x_sym, arena.variable_sym("X"));
1300 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1301 }
1302 _ => panic!("unexpected: {:?}", clause),
1303 }
1304 let mut p = make_parser(&arena, "foo(X) :- !bar(X).");
1305 let clause = p.parse_clause()?;
1306 match clause {
1307 &ast::Clause {
1308 head:
1309 &ast::Atom {
1310 sym: foo_sym,
1311 args: _,
1312 },
1313 premises:
1314 &[
1315 &ast::Term::NegAtom(&ast::Atom {
1316 sym: bar_sym,
1317 args: _,
1318 }),
1319 ],
1320 transform: &[],
1321 ..
1322 } => {
1323 assert_eq!(foo_sym, arena.predicate_sym("foo", None));
1324 assert_eq!(bar_sym, arena.predicate_sym("bar", None));
1325 }
1326 _ => panic!("unexpected: {:?}", clause),
1327 };
1328 let mut p = make_parser(
1329 &arena,
1330 "foo(Z) ⟸ bar(Y) |> do fn:group_by(); let X = fn:count(Y).",
1331 );
1332
1333 let clause = p.parse_clause()?;
1334 match clause {
1335 &ast::Clause {
1336 head: &ast::Atom { .. },
1337 premises: &[&ast::Term::Atom(ast::Atom { .. })],
1338 transform:
1339 &[
1340 &ast::TransformStmt {
1341 var: None,
1342 app: ast::BaseTerm::ApplyFn(first_sym, _),
1343 },
1344 &ast::TransformStmt {
1345 var: Some("X"),
1346 app: ast::BaseTerm::ApplyFn(second_sym, _),
1347 },
1348 ],
1349 ..
1350 } => {
1351 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1352 assert_eq!(clause.transform.len(), 2);
1353 assert_eq!(*first_sym, arena.function_sym("fn:group_by", None));
1354 assert_eq!(*second_sym, arena.function_sym("fn:count", None));
1355 }
1356 _ => panic!("unexpected: {:?}", clause),
1357 }
1358
1359 Ok(())
1360 }
1361
1362 #[test]
1363 fn test_anonymous_variable_single() -> Result<()> {
1364 let arena = Arena::new_with_global_interner();
1365 let mut p = make_parser(&arena, "foo(_, X).");
1366 let clause = p.parse_clause()?;
1367 match clause.head.args {
1369 &[&ast::BaseTerm::Variable(anon), &ast::BaseTerm::Variable(x)] => {
1370 assert_eq!(anon, arena.variable_sym("_Anon0"));
1371 assert_eq!(x, arena.variable_sym("X"));
1372 }
1373 _ => panic!("unexpected args: {:?}", clause.head.args),
1374 }
1375 Ok(())
1376 }
1377
1378 #[test]
1379 fn test_anonymous_variable_multiple_distinct() -> Result<()> {
1380 let arena = Arena::new_with_global_interner();
1381 let mut p = make_parser(&arena, "foo(_, _, _).");
1382 let clause = p.parse_clause()?;
1383 match clause.head.args {
1385 &[
1386 &ast::BaseTerm::Variable(a0),
1387 &ast::BaseTerm::Variable(a1),
1388 &ast::BaseTerm::Variable(a2),
1389 ] => {
1390 assert_eq!(a0, arena.variable_sym("_Anon0"));
1391 assert_eq!(a1, arena.variable_sym("_Anon1"));
1392 assert_eq!(a2, arena.variable_sym("_Anon2"));
1393 assert_ne!(a0, a1);
1395 assert_ne!(a1, a2);
1396 }
1397 _ => panic!("unexpected args: {:?}", clause.head.args),
1398 }
1399 Ok(())
1400 }
1401
1402 #[test]
1403 fn test_anonymous_variable_in_rule_body() -> Result<()> {
1404 let arena = Arena::new_with_global_interner();
1405 let mut p = make_parser(&arena, "result(X) :- foo(X, _).");
1406 let clause = p.parse_clause()?;
1407 assert_eq!(clause.head.sym, arena.predicate_sym("result", None));
1408 match clause.premises {
1409 &[&ast::Term::Atom(&ast::Atom { args, .. })] => match args {
1410 &[&ast::BaseTerm::Variable(x), &ast::BaseTerm::Variable(anon)] => {
1411 assert_eq!(x, arena.variable_sym("X"));
1412 assert_eq!(anon, arena.variable_sym("_Anon0"));
1413 }
1414 _ => panic!("unexpected args: {:?}", args),
1415 },
1416 _ => panic!("unexpected premises: {:?}", clause.premises),
1417 }
1418 Ok(())
1419 }
1420
1421 #[test]
1422 fn test_anonymous_variable_with_negation() -> Result<()> {
1423 let arena = Arena::new_with_global_interner();
1424 let mut p = make_parser(&arena, "orphan(X) :- node(X, _), !has_parent(X).");
1425 let clause = p.parse_clause()?;
1426 assert_eq!(clause.head.sym, arena.predicate_sym("orphan", None));
1427 assert_eq!(clause.premises.len(), 2);
1428 match clause.premises[0] {
1430 &ast::Term::Atom(&ast::Atom { args, .. }) => match args {
1431 &[&ast::BaseTerm::Variable(_), &ast::BaseTerm::Variable(anon)] => {
1432 assert_eq!(anon, arena.variable_sym("_Anon0"));
1433 }
1434 _ => panic!("unexpected args: {:?}", args),
1435 },
1436 _ => panic!("expected Atom, got {:?}", clause.premises[0]),
1437 }
1438 match clause.premises[1] {
1440 &ast::Term::NegAtom(&ast::Atom { sym, .. }) => {
1441 assert_eq!(sym, arena.predicate_sym("has_parent", None));
1442 }
1443 _ => panic!("expected NegAtom, got {:?}", clause.premises[1]),
1444 }
1445 Ok(())
1446 }
1447
1448 #[test]
1449 fn test_uppercase_predicate_name_rejected_at_head() {
1450 let arena = Arena::new_with_global_interner();
1451 let mut p = make_parser(&arena, "Foo(1, 2).");
1452 let err = p.parse_clause().unwrap_err().to_string();
1453 assert!(
1454 err.contains("`Foo` is not a valid predicate name")
1455 && err.contains("Did you mean `foo`?"),
1456 "unexpected error: {err}"
1457 );
1458 }
1459
1460 #[test]
1461 fn test_uppercase_predicate_name_rejected_in_body() {
1462 let arena = Arena::new_with_global_interner();
1463 let mut p = make_parser(&arena, "bar(X) :- Foo(X, Y).");
1464 let err = p.parse_clause().unwrap_err().to_string();
1465 assert!(
1466 err.contains("`Foo` starts with an uppercase letter")
1467 && err.contains("Did you mean `foo`?"),
1468 "unexpected error: {err}"
1469 );
1470 }
1471
1472 #[test]
1478 fn test_temporal_fact_with_interval() -> Result<()> {
1479 let arena = Arena::new_with_global_interner();
1480 let mut p = make_parser(&arena, "foo(/bar)@[2024-01-15, 2024-06-30].");
1481 let clause = p.parse_clause()?;
1482 assert!(clause.head_time.is_some(), "expected temporal annotation");
1483 let interval = clause.head_time.unwrap();
1484 match interval.start {
1485 ast::TemporalBound::Timestamp(_) => {}
1486 _ => panic!("expected Timestamp start, got {:?}", interval.start),
1487 }
1488 match interval.end {
1489 ast::TemporalBound::Timestamp(_) => {}
1490 _ => panic!("expected Timestamp end, got {:?}", interval.end),
1491 }
1492 Ok(())
1493 }
1494
1495 #[test]
1497 fn test_temporal_fact_point_interval() -> Result<()> {
1498 let arena = Arena::new_with_global_interner();
1499 let mut p = make_parser(&arena, "event(/login)@[2024-03-15].");
1500 let clause = p.parse_clause()?;
1501 assert!(clause.head_time.is_some(), "expected temporal annotation");
1502 let interval = clause.head_time.unwrap();
1503 assert_eq!(interval.start, interval.end);
1505 Ok(())
1506 }
1507
1508 #[test]
1510 fn test_non_temporal_fact() -> Result<()> {
1511 let arena = Arena::new_with_global_interner();
1512 let mut p = make_parser(&arena, "regular(/fact).");
1513 let clause = p.parse_clause()?;
1514 assert!(clause.head_time.is_none(), "non-temporal fact should have no annotation");
1515 Ok(())
1516 }
1517
1518 #[test]
1520 fn test_temporal_declaration() -> Result<()> {
1521 let arena = Arena::new_with_global_interner();
1522 let mut p = make_parser(&arena, "Decl employee(X) temporal bound [/name].");
1523 let unit = p.parse_unit()?;
1524 assert_eq!(unit.decls.len(), 2);
1526 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1527 Ok(())
1528 }
1529
1530 #[test]
1532 fn test_non_temporal_declaration() -> Result<()> {
1533 let arena = Arena::new_with_global_interner();
1534 let mut p = make_parser(&arena, "Decl config(X) bound [/string].");
1535 let unit = p.parse_unit()?;
1536 assert_eq!(unit.decls.len(), 2);
1537 assert!(!unit.decls[1].is_temporal, "expected non-temporal declaration");
1538 Ok(())
1539 }
1540
1541 #[test]
1543 fn test_temporal_declaration_with_descr() -> Result<()> {
1544 let arena = Arena::new_with_global_interner();
1545 let input = r#"Decl status(X, Y) temporal
1546 descr [doc("Employee status over time")]
1547 bound [/name, /string]."#;
1548 let mut p = make_parser(&arena, input);
1549 let unit = p.parse_unit()?;
1550 assert_eq!(unit.decls.len(), 2);
1551 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1552 Ok(())
1553 }
1554
1555 #[test]
1557 fn test_backward_compat_no_temporal() -> Result<()> {
1558 let programs = [
1560 "edge(/a, /b). path(X, Y) :- edge(X, Y).",
1561 "all(/a). excluded(/a). included(X) :- all(X), !excluded(X).",
1562 "age(/alice, 30). adult(Name) :- age(Name, Age), Age >= 18 .",
1563 ];
1564 for prog in &programs {
1565 let arena = Arena::new_with_global_interner();
1566 let mut p = make_parser(&arena, prog);
1567 let unit = p.parse_unit()?;
1568 for clause in unit.clauses {
1569 assert!(clause.head_time.is_none(), "clause should not have temporal annotation in: {prog}");
1570 }
1571 }
1572 Ok(())
1573 }
1574
1575 #[test]
1577 fn test_temporal_rule_with_variable_interval() -> Result<()> {
1578 let arena = Arena::new_with_global_interner();
1579 let mut p = make_parser(&arena, "reachable(X, Y)@[T] :- link(X, Y)@[T].");
1580 let clause = p.parse_clause()?;
1581 assert!(clause.head_time.is_some());
1583 let interval = clause.head_time.unwrap();
1584 match interval.start {
1585 ast::TemporalBound::Variable(_) => {}
1586 _ => panic!("expected Variable start, got {:?}", interval.start),
1587 }
1588 assert_eq!(interval.start, interval.end);
1590 assert_eq!(clause.premises.len(), 1);
1592 match clause.premises[0] {
1593 ast::Term::TemporalAtom(_, _) => {}
1594 _ => panic!("expected TemporalAtom, got {:?}", clause.premises[0]),
1595 }
1596 Ok(())
1597 }
1598
1599 #[test]
1601 fn test_temporal_rule_with_interval_range() -> Result<()> {
1602 let arena = Arena::new_with_global_interner();
1603 let mut p = make_parser(&arena, "reachable(X, Y)@[S, E] :- link(X, Y)@[S, E].");
1604 let clause = p.parse_clause()?;
1605 let interval = clause.head_time.unwrap();
1606 match interval.start {
1607 ast::TemporalBound::Variable(v) => {
1608 assert_eq!(arena.lookup_name(v.0).unwrap(), "S");
1609 }
1610 _ => panic!("expected Variable start"),
1611 }
1612 match interval.end {
1613 ast::TemporalBound::Variable(v) => {
1614 assert_eq!(arena.lookup_name(v.0).unwrap(), "E");
1615 }
1616 _ => panic!("expected Variable end"),
1617 }
1618 Ok(())
1619 }
1620
1621 #[test]
1623 fn test_temporal_wildcard_bounds() -> Result<()> {
1624 let arena = Arena::new_with_global_interner();
1625 let mut p = make_parser(&arena, "always(/true)@[_, _].");
1626 let clause = p.parse_clause()?;
1627 let interval = clause.head_time.unwrap();
1628 assert_eq!(interval.start, ast::TemporalBound::NegInf);
1629 assert_eq!(interval.end, ast::TemporalBound::PosInf);
1630 Ok(())
1631 }
1632
1633 #[gtest]
1636 fn test_colon_syntax_in_angle_brackets() -> googletest::Result<()> {
1637 let arena = Arena::new_with_global_interner();
1638 let input = ".Struct</x : /number, /y : /string>";
1639 let mut p = make_parser(&arena, input);
1640 let got = p.parse_base_term().unwrap();
1641
1642 let struct_type_sym = arena.function_sym("fn:Struct", None);
1644 let expected = arena.apply_fn(
1645 struct_type_sym,
1646 &[
1647 arena.const_(arena.name("/x")),
1648 arena.const_(arena.name("/number")),
1649 arena.const_(arena.name("/y")),
1650 arena.const_(arena.name("/string")),
1651 ],
1652 );
1653 verify_that!(got, eq(expected))
1654 }
1655
1656 #[gtest]
1658 fn test_tagged_union_colon_syntax() -> googletest::Result<()> {
1659 let arena = Arena::new_with_global_interner();
1660 let input = ".TaggedUnion</kind, /move : .Struct</x : /number>, /quit : .Struct<>>";
1661 let mut p = make_parser(&arena, input);
1662 let got = p.parse_base_term().unwrap();
1663
1664 let tu_sym = arena.function_sym("fn:TaggedUnion", None);
1665 let struct_sym = arena.function_sym("fn:Struct", None);
1666 let expected = arena.apply_fn(
1667 tu_sym,
1668 &[
1669 arena.const_(arena.name("/kind")),
1670 arena.const_(arena.name("/move")),
1671 arena.apply_fn(
1672 struct_sym,
1673 &[
1674 arena.const_(arena.name("/x")),
1675 arena.const_(arena.name("/number")),
1676 ],
1677 ),
1678 arena.const_(arena.name("/quit")),
1679 arena.apply_fn(struct_sym, &[]),
1680 ],
1681 );
1682 verify_that!(got, eq(expected))
1683 }
1684
1685 #[gtest]
1687 fn test_mixed_colon_syntax() -> googletest::Result<()> {
1688 let arena = Arena::new_with_global_interner();
1689 let input = ".List</number>";
1691 let mut p = make_parser(&arena, input);
1692 let got = p.parse_base_term().unwrap();
1693
1694 let list_sym = arena.function_sym("fn:List", None);
1695 let expected = arena.apply_fn(list_sym, &[arena.const_(arena.name("/number"))]);
1696 verify_that!(got, eq(expected))
1697 }
1698
1699 #[gtest]
1703 fn test_paren_syntax_unchanged() -> googletest::Result<()> {
1704 let arena = Arena::new_with_global_interner();
1705 let input = "fn:Struct(/x, /number, /y, /string)";
1706 let mut p = make_parser(&arena, input);
1707 let got = p.parse_base_term().unwrap();
1708
1709 let struct_type_sym = arena.function_sym("fn:Struct", None);
1710 let expected = arena.apply_fn(
1711 struct_type_sym,
1712 &[
1713 arena.const_(arena.name("/x")),
1714 arena.const_(arena.name("/number")),
1715 arena.const_(arena.name("/y")),
1716 arena.const_(arena.name("/string")),
1717 ],
1718 );
1719 verify_that!(got, eq(expected))
1720 }
1721
1722 #[gtest]
1724 fn test_colon_syntax_trailing_comma() -> googletest::Result<()> {
1725 let arena = Arena::new_with_global_interner();
1726 let input = ".Struct</x : /number,>";
1727 let mut p = make_parser(&arena, input);
1728 let got = p.parse_base_term().unwrap();
1729
1730 let struct_type_sym = arena.function_sym("fn:Struct", None);
1731 let expected = arena.apply_fn(
1732 struct_type_sym,
1733 &[
1734 arena.const_(arena.name("/x")),
1735 arena.const_(arena.name("/number")),
1736 ],
1737 );
1738 verify_that!(got, eq(expected))
1739 }
1740}