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 => {
401 self.next_token()?
402 }
403 Token::LParen if leading_var.is_some() => {
404 let name = leading_var.unwrap();
405 bail!(
406 "{}: `{}` starts with an uppercase letter and is therefore a variable; \
407 predicate names must start with a lowercase letter. Did you mean `{}`?",
408 self.sc.get_error_context(),
409 name,
410 lowercase_first(&name)
411 );
412 }
413 _ => bail!(
414 "parse_terms: expected comparison operator, got {}",
415 self.token
416 ),
417 };
418 let right_base_term = self.parse_base_term()?;
419 let term = match op {
420 Token::Eq => ast::Term::Eq(left_base_term, right_base_term),
421 Token::BangEq => ast::Term::Ineq(left_base_term, right_base_term),
422 Token::Lt => ast::Term::Atom(alloc!(
423 self,
424 ast::Atom {
425 sym: lt_sym(self.arena),
426 args: alloc_slice!(self, &[left_base_term, right_base_term]),
427 }
428 )),
429 Token::Le => ast::Term::Atom(self.arena.alloc(ast::Atom {
430 sym: le_sym(self.arena),
431 args: alloc_slice!(self, &[left_base_term, right_base_term]),
432 })),
433 Token::Gt => ast::Term::Atom(alloc!(
434 self,
435 ast::Atom {
436 sym: gt_sym(self.arena),
437 args: alloc_slice!(self, &[left_base_term, right_base_term]),
438 }
439 )),
440 Token::Ge => ast::Term::Atom(self.arena.alloc(ast::Atom {
441 sym: ge_sym(self.arena),
442 args: alloc_slice!(self, &[left_base_term, right_base_term]),
443 })),
444 _ => unreachable!(),
445 };
446 Ok(alloc!(self, term))
447 }
448 Token::Ident { .. } => {
449 let atom = self.parse_atom()?;
450 if let Some(interval) = self.try_parse_interval()? {
451 Ok(alloc!(self, ast::Term::TemporalAtom(atom, interval)))
452 } else {
453 Ok(alloc!(self, ast::Term::Atom(atom)))
454 }
455 }
456 _ => bail!("parse_term: unexpected token {:?}", self.token),
457 }
458 }
459
460 fn parse_bracket_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
462 self.expect(Token::LBracket)?;
463 self.parse_atoms(atoms)?;
464 self.expect(Token::RBracket)?;
465 Ok(())
466 }
467
468 fn parse_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
470 if let Token::Ident { .. } = self.token {
471 atoms.push(self.parse_atom()?);
472 loop {
473 if Token::Comma != self.token {
474 break;
475 }
476 self.next_token()?;
477 if !matches!(self.token, Token::Ident { .. }) {
478 break; }
480 atoms.push(self.parse_atom()?);
481 }
482 }
483 Ok(())
484 }
485
486 pub fn parse_atom(&mut self) -> Result<&'arena ast::Atom<'arena>> {
489 let mut name_buf = match &self.token {
490 Token::Ident { name } => {
491 if is_variable(name) {
492 bail!(
493 "{}: `{}` is not a valid predicate name: predicate names must start \
494 with a lowercase letter (identifiers starting with an uppercase letter \
495 are variables). Did you mean `{}`?",
496 self.sc.get_error_context(),
497 name,
498 lowercase_first(name)
499 );
500 }
501 name.clone()
502 }
503 _ => bail!("parse_atom: expected identifer got {}", self.token),
504 };
505
506 self.next_token()?;
507
508 while self.token == Token::Dot {
510 self.next_token()?;
514 match &self.token {
515 Token::Ident { name: next_name } => {
516 name_buf.push('.');
517 name_buf.push_str(next_name);
518 self.next_token()?;
519 }
520 _ => {
521 bail!(
527 "parse_atom: expected identifier after `.` in qualified name, got {}",
528 self.token
529 );
530 }
531 }
532 }
533
534 let name = self.arena.alloc_str(&name_buf);
535
536 self.expect(Token::LParen)?;
537 let mut args = vec![];
538 if Token::RParen != self.token {
539 self.parse_base_terms(&mut args)?;
540 }
541 self.expect(Token::RParen)?;
542 let args = alloc_slice!(self, &args);
543 Ok(alloc!(
544 self,
545 ast::Atom {
546 sym: self.arena.predicate_sym(name, None),
547 args
548 }
549 ))
550 }
551
552 fn parse_transforms(
553 &mut self,
554 transforms: &mut Vec<&'arena ast::TransformStmt<'arena>>,
555 ) -> Result<()> {
556 if Token::Do == self.token {
559 self.next_token()?;
560 let expr = self.parse_base_term()?;
561 transforms.push(alloc!(
562 self,
563 ast::TransformStmt {
564 var: None,
565 app: expr
566 }
567 ));
568 self.expect_transform_sep()?;
569 }
570 loop {
571 if Token::Let != self.token {
572 break;
573 }
574 self.next_token()?;
575 if let Token::Ident { name } = &self.token {
576 let name = alloc_str!(self, name.as_str());
577 self.next_token()?;
578 self.expect(Token::Eq)?;
579 let expr = self.parse_base_term()?;
580 transforms.push(alloc!(
581 self,
582 ast::TransformStmt {
583 var: Some(name),
584 app: expr
585 }
586 ))
587 }
588 if let Token::Dot = self.token {
589 break;
590 }
591 self.expect_transform_sep()?;
592 }
593 Ok(())
594 }
595
596 fn expect_transform_sep(&mut self) -> Result<()> {
597 match self.token {
598 Token::Comma | Token::Semi => self.next_token(),
599 _ => bail!(
600 "{}: expected `,` or `;` got {}",
601 self.sc.get_error_context(),
602 self.token
603 ),
604 }
605 }
606
607 fn try_parse_interval(&mut self) -> Result<Option<ast::Interval>> {
613 if self.token != Token::At {
614 return Ok(None);
615 }
616 self.next_token()?; self.expect(Token::LBracket)?;
618 let start = self.parse_temporal_bound(true)?;
619 let end = if self.token == Token::Comma {
620 self.next_token()?;
621 self.parse_temporal_bound(false)?
622 } else {
623 start
625 };
626 self.expect(Token::RBracket)?;
627 Ok(Some(ast::Interval { start, end }))
628 }
629
630 fn parse_temporal_bound(&mut self, is_start: bool) -> Result<ast::TemporalBound> {
632 match &self.token {
633 Token::Timestamp { nanos } => {
634 let nanos = *nanos;
635 self.next_token()?;
636 Ok(ast::TemporalBound::Timestamp(nanos))
637 }
638 Token::Ident { name } if name == "_" => {
639 self.next_token()?;
640 if is_start {
641 Ok(ast::TemporalBound::NegInf)
642 } else {
643 Ok(ast::TemporalBound::PosInf)
644 }
645 }
646 Token::Ident { name } if is_variable(name) => {
647 let var_idx = self.arena.variable_sym(name);
648 self.next_token()?;
649 Ok(ast::TemporalBound::Variable(var_idx))
650 }
651 _ => bail!(
652 "parse_temporal_bound: expected timestamp, variable, or '_', got {:?}",
653 self.token
654 ),
655 }
656 }
657
658 pub fn parse_base_term(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
668 match &self.token {
669 Token::LBracket => return self.parse_list_or_map(),
670 Token::LBrace => return self.parse_struct(),
671 _ => {}
672 }
673
674 let mut is_type = false;
675 let mut base_term = match &self.token {
676 Token::Ident { name } if name == "_" => {
677 let unique = format!("_Anon{}", self.anon_counter);
678 self.anon_counter += 1;
679 ast::BaseTerm::Variable(self.arena.variable_sym(&unique))
680 }
681 Token::Ident { name } if is_variable(name) => {
682 ast::BaseTerm::Variable(self.arena.variable_sym(name))
683 }
684 Token::Ident { name } if is_fn(name) => {
685 let name = self.arena.alloc_str(name);
686 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
688 }
689 Token::DotIdent { name } => {
690 let name = self.arena.alloc_str(name);
691 is_type = true;
692 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
694 }
695 Token::String { decoded } => {
696 let value = self.arena.alloc_str(decoded.as_str());
697 ast::BaseTerm::Const(ast::Const::String(value))
698 }
699 Token::Bytes { decoded } => {
700 let value = self.arena.alloc_slice_copy(decoded);
701 ast::BaseTerm::Const(ast::Const::Bytes(value))
702 }
703 Token::Int { decoded } => ast::BaseTerm::Const(ast::Const::Number(*decoded)),
704 Token::Float { decoded } => ast::BaseTerm::Const(ast::Const::Float(*decoded)),
705 Token::Timestamp { nanos } => ast::BaseTerm::Const(ast::Const::Time(*nanos)),
706 Token::Duration { nanos } => ast::BaseTerm::Const(ast::Const::Duration(*nanos)),
707 Token::Name { name } => {
708 let name = self.arena.intern(name);
709 ast::BaseTerm::Const(ast::Const::Name(name))
710 }
711 _ => bail!("parse_base_term: unexpected token {:?}", self.token),
712 };
713 self.next_token()?;
714 if let ast::BaseTerm::ApplyFn(fn_sym, _) = base_term {
715 let mut fn_args = vec![];
716 if is_type {
717 self.parse_langle_base_terms(&mut fn_args)?;
718 } else {
719 self.parse_paren_base_terms(&mut fn_args)?;
720 }
721 let fn_args = self.arena.alloc_slice_copy(&fn_args);
722 base_term = ast::BaseTerm::ApplyFn(fn_sym, fn_args);
723 }
724 let base_term = alloc!(self, base_term);
725 Ok(base_term)
726 }
727
728 fn parse_list_or_map(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
729 self.expect(Token::LBracket)?;
730 if Token::RBracket == self.token {
731 self.next_token()?;
732 return Ok(alloc!(
733 self,
734 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), &[])
735 ));
736 }
737 let first = self.parse_base_term()?;
738 let expr = if Token::Colon != self.token {
739 let mut items = vec![first];
740 if Token::Comma == self.token {
741 self.next_token()?;
742 if Token::RBracket != self.token {
743 self.parse_base_terms(&mut items)?;
744 }
745 }
746 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), alloc_slice!(self, &items))
749 } else {
750 self.expect(Token::Colon)?; let first_val = self.parse_base_term()?;
752 let mut items = vec![first, first_val];
753 loop {
754 if Token::Comma != self.token {
755 break;
756 }
757 self.next_token()?;
758 if Token::RBracket == self.token {
759 break; }
761 items.push(self.parse_base_term()?);
762 self.expect(Token::Colon)?;
763 items.push(self.parse_base_term()?);
764 }
765 ast::BaseTerm::ApplyFn(fn_map_sym(self.arena), alloc_slice!(self, &items))
766 };
767 self.expect(Token::RBracket)?;
768 Ok(alloc!(self, expr))
769 }
770
771 fn parse_struct(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
772 self.expect(Token::LBrace)?;
773 if Token::RBrace == self.token {
774 self.next_token()?;
775 return Ok(alloc!(
776 self,
777 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), &[])
778 ));
779 }
780 let mut items = vec![];
781 let name = self.parse_base_term()?;
782 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
783 items.push(name)
784 } else {
785 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}",);
786 }
787 self.expect(Token::Colon)?;
788 items.push(self.parse_base_term()?);
789 loop {
790 if Token::Comma != self.token {
791 break;
792 }
793 self.next_token()?;
794 if Token::RBrace == self.token {
795 break; }
797 let name = self.parse_base_term()?;
798 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
799 items.push(name)
800 } else {
801 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}");
802 }
803 self.expect(Token::Colon)?;
804 items.push(self.parse_base_term()?);
805 }
806 self.expect(Token::RBrace)?;
807 Ok(alloc!(
808 self,
809 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), alloc_slice!(self, &items))
810 ))
811 }
812
813 fn parse_langle_base_terms(
824 &mut self,
825 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
826 ) -> Result<()> {
827 self.expect(Token::Lt)?;
828 if Token::Gt == self.token {
829 self.next_token()?;
830 return Ok(());
831 }
832 self.parse_langle_member(base_terms)?;
833 while Token::Comma == self.token {
834 self.next_token()?;
835 if !base_term_start(&self.token) && !self.is_opt_keyword() {
836 break; }
838 self.parse_langle_member(base_terms)?;
839 }
840 self.expect(Token::Gt)?;
841 Ok(())
842 }
843
844 fn is_opt_keyword(&self) -> bool {
845 matches!(&self.token, Token::Ident { name } if name == "opt")
846 }
847
848 fn parse_langle_member(
849 &mut self,
850 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
851 ) -> Result<()> {
852 if self.is_opt_keyword() {
853 self.next_token()?;
854 let name = self.parse_base_term()?;
855 self.expect(Token::Colon)?;
856 let ty = self.parse_base_term()?;
857 let opt = alloc!(
858 self,
859 ast::BaseTerm::ApplyFn(fn_opt_sym(self.arena), alloc_slice!(self, &[name, ty]))
860 );
861 base_terms.push(opt);
862 return Ok(());
863 }
864 base_terms.push(self.parse_base_term()?);
865 if Token::Colon == self.token {
866 self.next_token()?;
867 base_terms.push(self.parse_base_term()?);
868 }
869 Ok(())
870 }
871
872 fn parse_paren_base_terms(
874 &mut self,
875 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
876 ) -> Result<()> {
877 self.expect(Token::LParen)?;
878 if Token::RParen != self.token {
879 self.parse_base_terms(base_terms)?;
880 }
881 self.expect(Token::RParen)?;
882 Ok(())
883 }
884
885 fn parse_base_terms(
887 &mut self,
888 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
889 ) -> Result<()> {
890 base_terms.push(self.parse_base_term()?);
891 while let Token::Comma = self.token {
892 self.next_token()?;
893 if !base_term_start(&self.token) {
894 break; }
896 base_terms.push(self.parse_base_term()?);
897 }
898
899 Ok(())
900 }
901}
902
903fn is_variable(name: &str) -> bool {
904 name.chars().next().unwrap().is_ascii_uppercase()
905}
906
907fn lowercase_first(name: &str) -> String {
908 let mut chars = name.chars();
909 match chars.next() {
910 Some(c) => c.to_ascii_lowercase().to_string() + chars.as_str(),
911 None => String::new(),
912 }
913}
914
915fn is_fn(name: &str) -> bool {
916 name.starts_with("fn:")
917}
918
919fn base_term_start(t: &Token) -> bool {
920 match t {
921 Token::Name { .. }
922 | Token::Int { .. }
923 | Token::Float { .. }
924 | Token::String { .. }
925 | Token::Bytes { .. }
926 | Token::Timestamp { .. }
927 | Token::Duration { .. }
928 | Token::LBracket
929 | Token::LBrace
930 | Token::DotIdent { .. } => true,
931 Token::Ident { name } => is_variable(name) || is_fn(name) || name == "_",
932 _ => false,
933 }
934}
935
936#[cfg(test)]
937mod test {
938
939 use super::*;
940 use googletest::prelude::{eq, gtest, verify_that};
941
942 fn make_parser<'arena>(
943 arena: &'arena Arena,
944 input: &'arena str,
945 ) -> Parser<'arena, &'arena [u8]> {
946 let mut p = Parser::new(arena, input.as_bytes(), "test");
947 p.next_token().unwrap();
948 p
949 }
950
951 #[test]
952 fn test_empty_unit() -> Result<()> {
953 let arena = Arena::new_with_global_interner();
954 let mut p = make_parser(&arena, "");
955 match p.parse_unit()? {
956 &ast::Unit { decls: &[pkg], .. } => {
957 assert_eq!(pkg, &empty_package_decl(&arena));
958 }
959 z => panic!("unexpected: {:?}", z),
960 }
961 Ok(())
962 }
963
964 #[test]
965 fn test_package_use() -> Result<()> {
966 let arena = Arena::new_with_global_interner();
967 let input = "Package foo[bar()]! Use baz[bar()]!";
968
969 let mut p = make_parser(&arena, input);
970 let unit = p.parse_unit()?;
971 match unit.decls {
972 &[
973 &ast::Decl {
974 atom:
975 &ast::Atom {
976 sym: got_package_sym,
977 ..
978 },
979 descr:
980 &[
981 &ast::Atom {
982 sym: got_name_sym1,
983 args: &[ast::BaseTerm::Const(ast::Const::String("foo"))],
984 },
985 &ast::Atom {
986 sym: got_bar_sym1,
987 args: &[],
988 },
989 ],
990 ..
991 },
992 &ast::Decl {
993 atom:
994 &ast::Atom {
995 sym: got_use_sym, ..
996 },
997 descr:
998 &[
999 &ast::Atom {
1000 sym: got_name_sym2,
1001 args: &[ast::BaseTerm::Const(ast::Const::String("baz"))],
1002 },
1003 &ast::Atom {
1004 sym: got_bar_sym2,
1005 args: &[],
1006 },
1007 ],
1008 ..
1009 },
1010 ] => {
1011 assert_eq!(got_use_sym, use_sym(&arena));
1012 assert_eq!(got_package_sym, package_sym(&arena));
1013 assert_eq!(got_name_sym1, name_sym(&arena));
1014 assert_eq!(got_name_sym2, name_sym(&arena));
1015 assert_eq!(got_bar_sym1, arena.predicate_sym("bar", None));
1016 assert_eq!(got_bar_sym2, arena.predicate_sym("bar", None));
1017 }
1018 z => panic!("unexpected {z:?}"),
1019 }
1020 Ok(())
1021 }
1022
1023 #[test]
1024 fn test_decl() -> Result<()> {
1025 let arena = Arena::new_with_global_interner();
1026 let input = "Decl foo(X, Y).";
1027 let mut p = make_parser(&arena, input);
1028 match p.parse_decl()? {
1029 &ast::Decl {
1030 atom:
1031 &ast::Atom {
1032 sym: got_foo_sym,
1033 args:
1034 &[
1035 &ast::BaseTerm::Variable(x_sym),
1036 &ast::BaseTerm::Variable(y_sym),
1037 ],
1038 },
1039 ..
1040 } => {
1041 assert_eq!(got_foo_sym, arena.predicate_sym("foo", None));
1042 assert_eq!(x_sym, arena.variable_sym("X"));
1043 assert_eq!(y_sym, arena.variable_sym("Y"))
1044 }
1045 decl => panic!("got {:?}", decl),
1046 };
1047 Ok(())
1048 }
1049
1050 #[test]
1051 fn test_base_term() -> googletest::Result<()> {
1052 let arena = Arena::new_with_global_interner();
1053 let input = "X 3 1.5 'foo' /foo fn:list() fn:list(/a) fn:list(/a, 3)"; let mut p = make_parser(&arena, input);
1055 let mut got_base_terms = vec![];
1056 loop {
1057 if Token::Eof == p.token {
1058 break;
1059 }
1060 let base_term = p.parse_base_term().unwrap();
1062 got_base_terms.push(base_term);
1063 }
1064 let expected = vec![
1065 arena.variable("X"),
1066 arena.const_(ast::Const::Number(3)),
1067 arena.const_(ast::Const::Float(1.5)),
1068 arena.const_(ast::Const::String("foo")),
1069 arena.const_(arena.name("/foo")),
1070 arena.apply_fn(fn_list_sym(&arena), &[]),
1071 arena.apply_fn(fn_list_sym(&arena), &[arena.const_(arena.name("/a"))]),
1072 arena.apply_fn(
1073 fn_list_sym(&arena),
1074 &[
1075 arena.const_(arena.name("/a")),
1076 arena.const_(ast::Const::Number(3)),
1077 ],
1078 ),
1079 ];
1080 verify_that!(got_base_terms, eq(&expected))
1081 }
1082
1083 #[test]
1084 fn test_term() -> googletest::Result<()> {
1085 let arena = Arena::new_with_global_interner();
1086 let input = "foo(/bar) !bar() X = Z X != 3 3 < 1 3 <= 1";
1087 let mut p = make_parser(&arena, input);
1088 let mut got_terms = vec![];
1089 loop {
1090 if Token::Eof == p.token {
1091 break;
1092 }
1093 got_terms.push(p.parse_term().unwrap());
1095 }
1096 let expected = [
1097 &ast::Term::Atom(arena.atom(
1098 arena.predicate_sym("foo", None),
1099 &[arena.const_(arena.name("/bar"))],
1100 )),
1101 &ast::Term::NegAtom(arena.atom(arena.predicate_sym("bar", None), &[])),
1102 &ast::Term::Eq(arena.variable("X"), arena.variable("Z")),
1103 &ast::Term::Ineq(
1104 arena.variable("X"),
1105 arena.alloc(ast::BaseTerm::Const(ast::Const::Number(3))),
1106 ),
1107 &ast::Term::Atom(arena.atom(
1108 arena.predicate_sym(":lt", Some(2)),
1109 &[
1110 arena.const_(ast::Const::Number(3)),
1111 arena.const_(ast::Const::Number(1)),
1112 ],
1113 )),
1114 &ast::Term::Atom(arena.atom(
1115 arena.predicate_sym(":le", Some(2)),
1116 &[
1117 arena.const_(ast::Const::Number(3)),
1118 arena.const_(ast::Const::Number(1)),
1119 ],
1120 )),
1121 ];
1122 verify_that!(got_terms, eq(&expected))
1123 }
1124
1125 #[gtest]
1126 fn test_structured_data_and_types() -> googletest::Result<()> {
1127 let arena = Arena::new_with_global_interner();
1128 let input = "[] [1,2,3] [1: 'one', 2: 'two'] {} {/foo: /bar} {/name: \"alice\", /age: 30} .List<.Option</name>, /string>";
1129 let mut p = make_parser(&arena, input);
1130 let mut got_base_terms = vec![];
1131 loop {
1132 if Token::Eof == p.token {
1133 break;
1134 }
1135 let base_term = p.parse_base_term().unwrap();
1137 got_base_terms.push(base_term);
1138 }
1139 let expected = vec![
1140 arena.apply_fn(fn_list_sym(&arena), &[]),
1141 arena.apply_fn(
1142 fn_list_sym(&arena),
1143 &[
1144 arena.const_(ast::Const::Number(1)),
1145 arena.const_(ast::Const::Number(2)),
1146 arena.const_(ast::Const::Number(3)),
1147 ],
1148 ),
1149 arena.apply_fn(
1150 fn_map_sym(&arena),
1151 &[
1152 arena.const_(ast::Const::Number(1)),
1153 arena.const_(ast::Const::String("one")),
1154 arena.const_(ast::Const::Number(2)),
1155 arena.const_(ast::Const::String("two")),
1156 ],
1157 ),
1158 arena.apply_fn(fn_struct_sym(&arena), &[]),
1159 arena.apply_fn(
1160 fn_struct_sym(&arena),
1161 &[
1162 arena.const_(arena.name("/foo")),
1163 arena.const_(arena.name("/bar")),
1164 ],
1165 ),
1166 arena.apply_fn(
1167 fn_struct_sym(&arena),
1168 &[
1169 arena.const_(arena.name("/name")),
1170 arena.const_(ast::Const::String("alice")),
1171 arena.const_(arena.name("/age")),
1172 arena.const_(ast::Const::Number(30)),
1173 ],
1174 ),
1175 arena.apply_fn(
1176 fn_list_type_sym(&arena),
1177 &[
1178 arena.apply_fn(
1179 fn_option_type_sym(&arena),
1180 &[arena.const_(arena.name("/name"))],
1181 ),
1182 arena.const_(arena.name("/string")),
1183 ],
1184 ),
1185 ];
1186 verify_that!(got_base_terms, eq(&expected))
1187 }
1188
1189 #[gtest]
1190 fn test_trailing_commas() -> googletest::Result<()> {
1191 let arena = Arena::new_with_global_interner();
1192 let input = "[1, 2, 3,] [1: 'one', 2: 'two',] {/a: 1, /b: 2,}";
1193 let mut p = make_parser(&arena, input);
1194 let mut got_base_terms = vec![];
1195 loop {
1196 if Token::Eof == p.token {
1197 break;
1198 }
1199 let base_term = p.parse_base_term().unwrap();
1200 got_base_terms.push(base_term);
1201 }
1202 let expected = vec![
1203 arena.apply_fn(
1204 fn_list_sym(&arena),
1205 &[
1206 arena.const_(ast::Const::Number(1)),
1207 arena.const_(ast::Const::Number(2)),
1208 arena.const_(ast::Const::Number(3)),
1209 ],
1210 ),
1211 arena.apply_fn(
1212 fn_map_sym(&arena),
1213 &[
1214 arena.const_(ast::Const::Number(1)),
1215 arena.const_(ast::Const::String("one")),
1216 arena.const_(ast::Const::Number(2)),
1217 arena.const_(ast::Const::String("two")),
1218 ],
1219 ),
1220 arena.apply_fn(
1221 fn_struct_sym(&arena),
1222 &[
1223 arena.const_(arena.name("/a")),
1224 arena.const_(ast::Const::Number(1)),
1225 arena.const_(arena.name("/b")),
1226 arena.const_(ast::Const::Number(2)),
1227 ],
1228 ),
1229 ];
1230 verify_that!(got_base_terms, eq(&expected))
1231 }
1232
1233 #[test]
1234 fn test_single_element_list() -> googletest::Result<()> {
1235 let arena = Arena::new_with_global_interner();
1236 let mut p = make_parser(&arena, "[42]");
1237 let got = p.parse_base_term().unwrap();
1238 let expected = arena.apply_fn(fn_list_sym(&arena), &[arena.const_(ast::Const::Number(42))]);
1239 verify_that!(got, eq(expected))
1240 }
1241
1242 #[test]
1243 fn test_descr_trailing_comma() -> googletest::Result<()> {
1244 let arena = Arena::new_with_global_interner();
1246 let mut p = make_parser(&arena, "Decl foo(X) descr [ bar(), ].");
1247 p.parse_decl()
1248 .expect("descr list with trailing comma parses");
1249 Ok(())
1250 }
1251
1252 #[test]
1253 fn test_opt_in_struct_type() -> googletest::Result<()> {
1254 let arena = Arena::new_with_global_interner();
1258 let mut p = make_parser(&arena, ".Struct</x : /number, opt /y : /string>");
1259 let got = p.parse_base_term().unwrap();
1260 let opt_inner = arena.apply_fn(
1261 fn_opt_sym(&arena),
1262 &[
1263 arena.const_(arena.name("/y")),
1264 arena.const_(arena.name("/string")),
1265 ],
1266 );
1267 let expected = arena.apply_fn(
1268 arena.function_sym("fn:Struct", None),
1269 &[
1270 arena.const_(arena.name("/x")),
1271 arena.const_(arena.name("/number")),
1272 opt_inner,
1273 ],
1274 );
1275 verify_that!(got, eq(expected))
1276 }
1277
1278 #[test]
1279 fn test_transform_sep_comma_and_semi() -> googletest::Result<()> {
1280 let arena = Arena::new_with_global_interner();
1283 let src_comma = "q(K, S) :- p(K, V) |> do fn:group_by(K), let S = fn:sum(V).";
1284 let src_semi = "q(K, S) :- p(K, V) |> do fn:group_by(K); let S = fn:sum(V).";
1285 let a = make_parser(&arena, src_comma).parse_clause().unwrap();
1286 let b = make_parser(&arena, src_semi).parse_clause().unwrap();
1287 verify_that!(a.transform.len(), eq(2))?;
1288 verify_that!(b.transform.len(), eq(2))
1289 }
1290
1291 #[test]
1292 fn test_clause() -> Result<()> {
1293 let arena = Arena::new_with_global_interner();
1294 let mut p = make_parser(&arena, "foo(X).");
1295 let clause = p.parse_clause()?;
1296 match clause {
1297 &ast::Clause {
1298 head:
1299 &ast::Atom {
1300 args: &[ast::BaseTerm::Variable(x_sym)],
1301 ..
1302 },
1303 premises: &[],
1304 transform: &[],
1305 ..
1306 } => {
1307 assert_eq!(*x_sym, arena.variable_sym("X"));
1308 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1309 }
1310 _ => panic!("unexpected: {:?}", clause),
1311 }
1312 let mut p = make_parser(&arena, "foo(X) :- !bar(X).");
1313 let clause = p.parse_clause()?;
1314 match clause {
1315 &ast::Clause {
1316 head:
1317 &ast::Atom {
1318 sym: foo_sym,
1319 args: _,
1320 },
1321 premises:
1322 &[
1323 &ast::Term::NegAtom(&ast::Atom {
1324 sym: bar_sym,
1325 args: _,
1326 }),
1327 ],
1328 transform: &[],
1329 ..
1330 } => {
1331 assert_eq!(foo_sym, arena.predicate_sym("foo", None));
1332 assert_eq!(bar_sym, arena.predicate_sym("bar", None));
1333 }
1334 _ => panic!("unexpected: {:?}", clause),
1335 };
1336 let mut p = make_parser(
1337 &arena,
1338 "foo(Z) ⟸ bar(Y) |> do fn:group_by(); let X = fn:count(Y).",
1339 );
1340
1341 let clause = p.parse_clause()?;
1342 match clause {
1343 &ast::Clause {
1344 head: &ast::Atom { .. },
1345 premises: &[&ast::Term::Atom(ast::Atom { .. })],
1346 transform:
1347 &[
1348 &ast::TransformStmt {
1349 var: None,
1350 app: ast::BaseTerm::ApplyFn(first_sym, _),
1351 },
1352 &ast::TransformStmt {
1353 var: Some("X"),
1354 app: ast::BaseTerm::ApplyFn(second_sym, _),
1355 },
1356 ],
1357 ..
1358 } => {
1359 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1360 assert_eq!(clause.transform.len(), 2);
1361 assert_eq!(*first_sym, arena.function_sym("fn:group_by", None));
1362 assert_eq!(*second_sym, arena.function_sym("fn:count", None));
1363 }
1364 _ => panic!("unexpected: {:?}", clause),
1365 }
1366
1367 Ok(())
1368 }
1369
1370 #[test]
1371 fn test_anonymous_variable_single() -> Result<()> {
1372 let arena = Arena::new_with_global_interner();
1373 let mut p = make_parser(&arena, "foo(_, X).");
1374 let clause = p.parse_clause()?;
1375 match clause.head.args {
1377 &[&ast::BaseTerm::Variable(anon), &ast::BaseTerm::Variable(x)] => {
1378 assert_eq!(anon, arena.variable_sym("_Anon0"));
1379 assert_eq!(x, arena.variable_sym("X"));
1380 }
1381 _ => panic!("unexpected args: {:?}", clause.head.args),
1382 }
1383 Ok(())
1384 }
1385
1386 #[test]
1387 fn test_anonymous_variable_multiple_distinct() -> Result<()> {
1388 let arena = Arena::new_with_global_interner();
1389 let mut p = make_parser(&arena, "foo(_, _, _).");
1390 let clause = p.parse_clause()?;
1391 match clause.head.args {
1393 &[
1394 &ast::BaseTerm::Variable(a0),
1395 &ast::BaseTerm::Variable(a1),
1396 &ast::BaseTerm::Variable(a2),
1397 ] => {
1398 assert_eq!(a0, arena.variable_sym("_Anon0"));
1399 assert_eq!(a1, arena.variable_sym("_Anon1"));
1400 assert_eq!(a2, arena.variable_sym("_Anon2"));
1401 assert_ne!(a0, a1);
1403 assert_ne!(a1, a2);
1404 }
1405 _ => panic!("unexpected args: {:?}", clause.head.args),
1406 }
1407 Ok(())
1408 }
1409
1410 #[test]
1411 fn test_anonymous_variable_in_rule_body() -> Result<()> {
1412 let arena = Arena::new_with_global_interner();
1413 let mut p = make_parser(&arena, "result(X) :- foo(X, _).");
1414 let clause = p.parse_clause()?;
1415 assert_eq!(clause.head.sym, arena.predicate_sym("result", None));
1416 match clause.premises {
1417 &[&ast::Term::Atom(&ast::Atom { args, .. })] => match args {
1418 &[&ast::BaseTerm::Variable(x), &ast::BaseTerm::Variable(anon)] => {
1419 assert_eq!(x, arena.variable_sym("X"));
1420 assert_eq!(anon, arena.variable_sym("_Anon0"));
1421 }
1422 _ => panic!("unexpected args: {:?}", args),
1423 },
1424 _ => panic!("unexpected premises: {:?}", clause.premises),
1425 }
1426 Ok(())
1427 }
1428
1429 #[test]
1430 fn test_anonymous_variable_with_negation() -> Result<()> {
1431 let arena = Arena::new_with_global_interner();
1432 let mut p = make_parser(&arena, "orphan(X) :- node(X, _), !has_parent(X).");
1433 let clause = p.parse_clause()?;
1434 assert_eq!(clause.head.sym, arena.predicate_sym("orphan", None));
1435 assert_eq!(clause.premises.len(), 2);
1436 match clause.premises[0] {
1438 &ast::Term::Atom(&ast::Atom { args, .. }) => match args {
1439 &[&ast::BaseTerm::Variable(_), &ast::BaseTerm::Variable(anon)] => {
1440 assert_eq!(anon, arena.variable_sym("_Anon0"));
1441 }
1442 _ => panic!("unexpected args: {:?}", args),
1443 },
1444 _ => panic!("expected Atom, got {:?}", clause.premises[0]),
1445 }
1446 match clause.premises[1] {
1448 &ast::Term::NegAtom(&ast::Atom { sym, .. }) => {
1449 assert_eq!(sym, arena.predicate_sym("has_parent", None));
1450 }
1451 _ => panic!("expected NegAtom, got {:?}", clause.premises[1]),
1452 }
1453 Ok(())
1454 }
1455
1456 #[test]
1457 fn test_uppercase_predicate_name_rejected_at_head() {
1458 let arena = Arena::new_with_global_interner();
1459 let mut p = make_parser(&arena, "Foo(1, 2).");
1460 let err = p.parse_clause().unwrap_err().to_string();
1461 assert!(
1462 err.contains("`Foo` is not a valid predicate name")
1463 && err.contains("Did you mean `foo`?"),
1464 "unexpected error: {err}"
1465 );
1466 }
1467
1468 #[test]
1469 fn test_uppercase_predicate_name_rejected_in_body() {
1470 let arena = Arena::new_with_global_interner();
1471 let mut p = make_parser(&arena, "bar(X) :- Foo(X, Y).");
1472 let err = p.parse_clause().unwrap_err().to_string();
1473 assert!(
1474 err.contains("`Foo` starts with an uppercase letter")
1475 && err.contains("Did you mean `foo`?"),
1476 "unexpected error: {err}"
1477 );
1478 }
1479
1480 #[test]
1486 fn test_temporal_fact_with_interval() -> Result<()> {
1487 let arena = Arena::new_with_global_interner();
1488 let mut p = make_parser(&arena, "foo(/bar)@[2024-01-15, 2024-06-30].");
1489 let clause = p.parse_clause()?;
1490 assert!(clause.head_time.is_some(), "expected temporal annotation");
1491 let interval = clause.head_time.unwrap();
1492 match interval.start {
1493 ast::TemporalBound::Timestamp(_) => {}
1494 _ => panic!("expected Timestamp start, got {:?}", interval.start),
1495 }
1496 match interval.end {
1497 ast::TemporalBound::Timestamp(_) => {}
1498 _ => panic!("expected Timestamp end, got {:?}", interval.end),
1499 }
1500 Ok(())
1501 }
1502
1503 #[test]
1505 fn test_temporal_fact_point_interval() -> Result<()> {
1506 let arena = Arena::new_with_global_interner();
1507 let mut p = make_parser(&arena, "event(/login)@[2024-03-15].");
1508 let clause = p.parse_clause()?;
1509 assert!(clause.head_time.is_some(), "expected temporal annotation");
1510 let interval = clause.head_time.unwrap();
1511 assert_eq!(interval.start, interval.end);
1513 Ok(())
1514 }
1515
1516 #[test]
1518 fn test_non_temporal_fact() -> Result<()> {
1519 let arena = Arena::new_with_global_interner();
1520 let mut p = make_parser(&arena, "regular(/fact).");
1521 let clause = p.parse_clause()?;
1522 assert!(
1523 clause.head_time.is_none(),
1524 "non-temporal fact should have no annotation"
1525 );
1526 Ok(())
1527 }
1528
1529 #[test]
1531 fn test_temporal_declaration() -> Result<()> {
1532 let arena = Arena::new_with_global_interner();
1533 let mut p = make_parser(&arena, "Decl employee(X) temporal bound [/name].");
1534 let unit = p.parse_unit()?;
1535 assert_eq!(unit.decls.len(), 2);
1537 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1538 Ok(())
1539 }
1540
1541 #[test]
1543 fn test_non_temporal_declaration() -> Result<()> {
1544 let arena = Arena::new_with_global_interner();
1545 let mut p = make_parser(&arena, "Decl config(X) bound [/string].");
1546 let unit = p.parse_unit()?;
1547 assert_eq!(unit.decls.len(), 2);
1548 assert!(
1549 !unit.decls[1].is_temporal,
1550 "expected non-temporal declaration"
1551 );
1552 Ok(())
1553 }
1554
1555 #[test]
1557 fn test_temporal_declaration_with_descr() -> Result<()> {
1558 let arena = Arena::new_with_global_interner();
1559 let input = r#"Decl status(X, Y) temporal
1560 descr [doc("Employee status over time")]
1561 bound [/name, /string]."#;
1562 let mut p = make_parser(&arena, input);
1563 let unit = p.parse_unit()?;
1564 assert_eq!(unit.decls.len(), 2);
1565 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1566 Ok(())
1567 }
1568
1569 #[test]
1571 fn test_backward_compat_no_temporal() -> Result<()> {
1572 let programs = [
1574 "edge(/a, /b). path(X, Y) :- edge(X, Y).",
1575 "all(/a). excluded(/a). included(X) :- all(X), !excluded(X).",
1576 "age(/alice, 30). adult(Name) :- age(Name, Age), Age >= 18 .",
1577 ];
1578 for prog in &programs {
1579 let arena = Arena::new_with_global_interner();
1580 let mut p = make_parser(&arena, prog);
1581 let unit = p.parse_unit()?;
1582 for clause in unit.clauses {
1583 assert!(
1584 clause.head_time.is_none(),
1585 "clause should not have temporal annotation in: {prog}"
1586 );
1587 }
1588 }
1589 Ok(())
1590 }
1591
1592 #[test]
1594 fn test_temporal_rule_with_variable_interval() -> Result<()> {
1595 let arena = Arena::new_with_global_interner();
1596 let mut p = make_parser(&arena, "reachable(X, Y)@[T] :- link(X, Y)@[T].");
1597 let clause = p.parse_clause()?;
1598 assert!(clause.head_time.is_some());
1600 let interval = clause.head_time.unwrap();
1601 match interval.start {
1602 ast::TemporalBound::Variable(_) => {}
1603 _ => panic!("expected Variable start, got {:?}", interval.start),
1604 }
1605 assert_eq!(interval.start, interval.end);
1607 assert_eq!(clause.premises.len(), 1);
1609 match clause.premises[0] {
1610 ast::Term::TemporalAtom(_, _) => {}
1611 _ => panic!("expected TemporalAtom, got {:?}", clause.premises[0]),
1612 }
1613 Ok(())
1614 }
1615
1616 #[test]
1618 fn test_temporal_rule_with_interval_range() -> Result<()> {
1619 let arena = Arena::new_with_global_interner();
1620 let mut p = make_parser(&arena, "reachable(X, Y)@[S, E] :- link(X, Y)@[S, E].");
1621 let clause = p.parse_clause()?;
1622 let interval = clause.head_time.unwrap();
1623 match interval.start {
1624 ast::TemporalBound::Variable(v) => {
1625 assert_eq!(arena.lookup_name(v.0).unwrap(), "S");
1626 }
1627 _ => panic!("expected Variable start"),
1628 }
1629 match interval.end {
1630 ast::TemporalBound::Variable(v) => {
1631 assert_eq!(arena.lookup_name(v.0).unwrap(), "E");
1632 }
1633 _ => panic!("expected Variable end"),
1634 }
1635 Ok(())
1636 }
1637
1638 #[test]
1640 fn test_temporal_wildcard_bounds() -> Result<()> {
1641 let arena = Arena::new_with_global_interner();
1642 let mut p = make_parser(&arena, "always(/true)@[_, _].");
1643 let clause = p.parse_clause()?;
1644 let interval = clause.head_time.unwrap();
1645 assert_eq!(interval.start, ast::TemporalBound::NegInf);
1646 assert_eq!(interval.end, ast::TemporalBound::PosInf);
1647 Ok(())
1648 }
1649
1650 #[gtest]
1653 fn test_colon_syntax_in_angle_brackets() -> googletest::Result<()> {
1654 let arena = Arena::new_with_global_interner();
1655 let input = ".Struct</x : /number, /y : /string>";
1656 let mut p = make_parser(&arena, input);
1657 let got = p.parse_base_term().unwrap();
1658
1659 let struct_type_sym = arena.function_sym("fn:Struct", None);
1661 let expected = arena.apply_fn(
1662 struct_type_sym,
1663 &[
1664 arena.const_(arena.name("/x")),
1665 arena.const_(arena.name("/number")),
1666 arena.const_(arena.name("/y")),
1667 arena.const_(arena.name("/string")),
1668 ],
1669 );
1670 verify_that!(got, eq(expected))
1671 }
1672
1673 #[gtest]
1675 fn test_tagged_union_colon_syntax() -> googletest::Result<()> {
1676 let arena = Arena::new_with_global_interner();
1677 let input = ".TaggedUnion</kind, /move : .Struct</x : /number>, /quit : .Struct<>>";
1678 let mut p = make_parser(&arena, input);
1679 let got = p.parse_base_term().unwrap();
1680
1681 let tu_sym = arena.function_sym("fn:TaggedUnion", None);
1682 let struct_sym = arena.function_sym("fn:Struct", None);
1683 let expected = arena.apply_fn(
1684 tu_sym,
1685 &[
1686 arena.const_(arena.name("/kind")),
1687 arena.const_(arena.name("/move")),
1688 arena.apply_fn(
1689 struct_sym,
1690 &[
1691 arena.const_(arena.name("/x")),
1692 arena.const_(arena.name("/number")),
1693 ],
1694 ),
1695 arena.const_(arena.name("/quit")),
1696 arena.apply_fn(struct_sym, &[]),
1697 ],
1698 );
1699 verify_that!(got, eq(expected))
1700 }
1701
1702 #[gtest]
1704 fn test_mixed_colon_syntax() -> googletest::Result<()> {
1705 let arena = Arena::new_with_global_interner();
1706 let input = ".List</number>";
1708 let mut p = make_parser(&arena, input);
1709 let got = p.parse_base_term().unwrap();
1710
1711 let list_sym = arena.function_sym("fn:List", None);
1712 let expected = arena.apply_fn(list_sym, &[arena.const_(arena.name("/number"))]);
1713 verify_that!(got, eq(expected))
1714 }
1715
1716 #[gtest]
1720 fn test_paren_syntax_unchanged() -> googletest::Result<()> {
1721 let arena = Arena::new_with_global_interner();
1722 let input = "fn:Struct(/x, /number, /y, /string)";
1723 let mut p = make_parser(&arena, input);
1724 let got = p.parse_base_term().unwrap();
1725
1726 let struct_type_sym = arena.function_sym("fn:Struct", None);
1727 let expected = arena.apply_fn(
1728 struct_type_sym,
1729 &[
1730 arena.const_(arena.name("/x")),
1731 arena.const_(arena.name("/number")),
1732 arena.const_(arena.name("/y")),
1733 arena.const_(arena.name("/string")),
1734 ],
1735 );
1736 verify_that!(got, eq(expected))
1737 }
1738
1739 #[gtest]
1741 fn test_colon_syntax_trailing_comma() -> googletest::Result<()> {
1742 let arena = Arena::new_with_global_interner();
1743 let input = ".Struct</x : /number,>";
1744 let mut p = make_parser(&arena, input);
1745 let got = p.parse_base_term().unwrap();
1746
1747 let struct_type_sym = arena.function_sym("fn:Struct", None);
1748 let expected = arena.apply_fn(
1749 struct_type_sym,
1750 &[
1751 arena.const_(arena.name("/x")),
1752 arena.const_(arena.name("/number")),
1753 ],
1754 );
1755 verify_that!(got, eq(expected))
1756 }
1757}