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 empty_package_decl(arena: &Arena) -> ast::Decl<'_> {
87 ast::Decl {
88 atom: arena.alloc(ast::Atom {
89 sym: package_sym(arena),
90 args: &[],
91 }),
92 is_temporal: false,
93 descr: arena.alloc_slice_copy(&[arena.alloc(ast::Atom {
94 sym: name_sym(arena),
95 args: arena
96 .alloc_slice_copy(&[arena.alloc(ast::BaseTerm::Const(ast::Const::String("")))]),
97 })]),
98 bounds: None,
99 constraints: None,
100 }
101}
102
103macro_rules! alloc {
104 ($self:expr, $e:expr) => {
105 &*$self.arena.alloc($e)
106 };
107}
108
109macro_rules! alloc_str {
110 ($self:expr, $e:expr) => {
111 &*$self.arena.alloc_str($e)
112 };
113}
114
115macro_rules! alloc_slice {
116 ($self:expr, $e:expr) => {
117 &*$self.arena.alloc_slice_copy($e)
118 };
119}
120
121impl<'arena, R> Parser<'arena, R>
122where
123 R: io::Read,
124{
125 pub fn new<P: ToString>(arena: &'arena Arena, reader: R, path: P) -> Self
126 where
127 R: io::Read,
128 {
129 Self {
130 sc: scan::Scanner::new(reader, path),
131 token: token::Token::Illegal,
132 arena,
133 anon_counter: 0,
134 }
135 }
136
137 pub fn next_token(&mut self) -> Result<()> {
138 self.token = self.sc.next_token()?;
139 Ok(())
140 }
141
142 fn expect(&mut self, expected: Token) -> Result<()> {
144 if expected != self.token {
145 let error = ParseError::Unexpected(
146 self.sc.get_error_context(),
147 expected.clone(),
148 self.token.clone(),
149 );
150 return Err(anyhow!(error));
151 }
152 self.next_token()
153 }
154
155 pub fn parse_unit(&mut self) -> Result<&'arena ast::Unit<'arena>> {
156 let package = if matches!(self.token.clone(), Token::Package) {
157 self.parse_package_decl()?
158 } else {
159 self.arena.alloc(empty_package_decl(self.arena))
160 };
161 let mut decls = vec![package];
162 while let Token::Use = self.token {
163 decls.push(self.parse_use_decl()?);
164 }
165 let mut clauses = vec![];
166 loop {
167 match self.token {
168 Token::Eof => break,
169 Token::Decl => decls.push(self.parse_decl()?),
170 _ => clauses.push(self.parse_clause()?),
171 }
172 }
173 let decls: &'arena [&'arena ast::Decl<'arena>] = self.arena.alloc_slice_copy(&decls);
174 let clauses: &'arena [&'arena ast::Clause<'arena>] = self.arena.alloc_slice_copy(&clauses);
175 let unit: &'arena ast::Unit<'arena> = &*self.arena.alloc(ast::Unit { clauses, decls });
176 Ok(unit)
177 }
178
179 pub fn parse_package_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
181 self.expect(Token::Package)?;
182 let package_name: &'arena str = if let Token::Ident { name } = &self.token {
183 self.arena.alloc_str(name.as_str())
184 } else {
185 bail!("expected identifer got {}", self.token);
186 };
187
188 let name_atom: &'arena ast::Atom<'arena> = self.arena.alloc(ast::Atom {
189 sym: name_sym(self.arena),
190 args: self.arena.alloc_slice_copy(&[self
191 .arena
192 .alloc(ast::BaseTerm::Const(ast::Const::String(package_name)))]),
193 });
194 let mut descr_atoms: Vec<&'arena ast::Atom<'arena>> = vec![name_atom];
195 self.next_token()?;
196 if Token::LBracket == self.token {
197 self.parse_bracket_atoms(&mut descr_atoms)?;
198 }
199 let descr = alloc_slice!(self, &descr_atoms);
200
201 self.expect(Token::Bang)?;
202
203 let package_atom = alloc!(
204 self,
205 ast::Atom {
206 sym: package_sym(self.arena),
207 args: &[]
208 }
209 );
210
211 let decl: &'arena ast::Decl = alloc!(
213 self,
214 ast::Decl {
215 atom: package_atom,
216 bounds: None,
217 descr,
218 constraints: None,
219 is_temporal: false,
220 }
221 );
222 Ok(decl)
223 }
224
225 fn parse_use_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
226 self.expect(Token::Use)?;
227 let use_atom = alloc!(
228 self,
229 ast::Atom {
230 sym: use_sym(self.arena),
231 args: &[]
232 }
233 );
234
235 let name = match &self.token {
236 Token::Ident { name } => name.as_str(),
237 _ => bail!("parse_use_decl: expected identifer got {}", self.token),
238 };
239
240 let name: &'arena str = alloc_str!(self, name);
241 let name = alloc!(self, ast::BaseTerm::Const(ast::Const::String(name)));
242 let args = alloc_slice!(self, &[name]);
243
244 let mut descr_atoms: Vec<&ast::Atom> = vec![self.arena.alloc(ast::Atom {
245 sym: name_sym(self.arena),
246 args,
247 })];
248 self.next_token()?;
249 if Token::LBracket == self.token {
250 self.parse_bracket_atoms(&mut descr_atoms)?;
251 }
252 self.expect(Token::Bang)?;
253
254 let descr_atoms = alloc_slice!(self, &descr_atoms);
255 Ok(alloc!(
256 self,
257 ast::Decl {
258 atom: use_atom,
259 descr: descr_atoms,
260 bounds: None,
261 constraints: None,
262 is_temporal: false,
263 }
264 ))
265 }
266
267 fn parse_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
268 self.expect(Token::Decl)?;
269 let atom = self.parse_atom()?;
270 let is_temporal = match &self.token {
272 Token::Ident { name } if name == "temporal" => {
273 self.next_token()?;
274 true
275 }
276 _ => false,
277 };
278 let mut descr_atoms = vec![];
279 if Token::Descr == self.token {
280 self.next_token()?;
281 self.parse_bracket_atoms(&mut descr_atoms)?;
282 }
283 let mut bound_decls = vec![];
284 loop {
285 if Token::Bound != self.token {
286 break;
287 }
288 bound_decls.push(self.parse_bounds_decl()?);
289 }
290 let bounds = if bound_decls.is_empty() {
291 None
292 } else {
293 Some(alloc_slice!(self, &bound_decls))
294 };
295 let constraints = if Token::Inclusion == self.token {
296 Some(self.parse_inclusion_constraint()?)
297 } else {
298 None
299 };
300 self.expect(Token::Dot)?;
301 Ok(alloc!(
302 self,
303 ast::Decl {
304 atom,
305 descr: alloc_slice!(self, &descr_atoms),
306 bounds,
307 constraints,
308 is_temporal,
309 }
310 ))
311 }
312
313 fn parse_bounds_decl(&mut self) -> Result<&'arena ast::BoundDecl<'arena>> {
315 self.expect(Token::Bound)?;
316 self.expect(Token::LBracket)?;
317 let mut base_terms = vec![];
318 self.parse_base_terms(&mut base_terms)?;
319 self.expect(Token::RBracket)?;
320 let base_terms = alloc_slice!(self, &base_terms);
321 let bound_decl = alloc!(self, ast::BoundDecl { base_terms });
322 Ok(bound_decl)
323 }
324
325 fn parse_inclusion_constraint(&mut self) -> Result<&'arena ast::Constraints<'arena>> {
326 self.expect(Token::Inclusion)?;
327 let mut consequences = vec![];
328 self.parse_bracket_atoms(&mut consequences)?;
329 let consequences = alloc_slice!(self, &consequences);
330 Ok(alloc!(
331 self,
332 Constraints {
333 consequences,
334 alternatives: &[]
335 }
336 ))
337 }
338
339 pub fn parse_clause(&mut self) -> Result<&'arena ast::Clause<'arena>> {
340 let head = self.parse_atom()?;
341 let head_time = self.try_parse_interval()?;
342 let mut premises = vec![];
343 let mut transform = vec![];
344 match self.token {
345 Token::ColonDash | Token::LongLeftDoubleArrow => {
346 self.next_token()?;
347 self.parse_terms(&mut premises)?;
348 if let Token::PipeGt = self.token {
349 self.next_token()?;
350 self.parse_transforms(&mut transform)?;
351 }
352 }
353 _ => {}
354 }
355 self.expect(Token::Dot)?;
356 let premises = alloc_slice!(self, &premises);
357 let transform = alloc_slice!(self, &transform);
358 Ok(alloc!(
359 self,
360 ast::Clause {
361 head,
362 head_time,
363 premises,
364 transform,
365 }
366 ))
367 }
368
369 fn parse_terms(&mut self, terms: &mut Vec<&'arena ast::Term<'arena>>) -> Result<()> {
371 terms.push(self.parse_term()?);
372 loop {
373 if Token::Comma != self.token {
374 return Ok(());
375 }
376 self.next_token()?;
377 terms.push(self.parse_term()?);
378 }
379 }
380
381 pub fn parse_term(&mut self) -> Result<&'arena ast::Term<'arena>> {
382 match &self.token {
383 Token::Bang => {
384 self.next_token()?;
385 let atom = self.parse_atom()?;
386 Ok(alloc!(self, ast::Term::NegAtom(atom)))
387 }
388 t if base_term_start(t) => {
389 let left_base_term = self.parse_base_term()?;
390 let op = self.token.clone();
391 match op {
392 Token::Eq | Token::BangEq | Token::Lt | Token::Le | Token::Gt | Token::Ge => self.next_token()?,
393 _ => bail!("parse_terms: expected comparison operator, got {}", self.token),
394 };
395 let right_base_term = self.parse_base_term()?;
396 let term = match op {
397 Token::Eq => ast::Term::Eq(left_base_term, right_base_term),
398 Token::BangEq => ast::Term::Ineq(left_base_term, right_base_term),
399 Token::Lt => ast::Term::Atom(alloc!(
400 self,
401 ast::Atom {
402 sym: lt_sym(self.arena),
403 args: alloc_slice!(self, &[left_base_term, right_base_term]),
404 }
405 )),
406 Token::Le => ast::Term::Atom(self.arena.alloc(ast::Atom {
407 sym: le_sym(self.arena),
408 args: alloc_slice!(self, &[left_base_term, right_base_term]),
409 })),
410 Token::Gt => ast::Term::Atom(alloc!(
411 self,
412 ast::Atom {
413 sym: gt_sym(self.arena),
414 args: alloc_slice!(self, &[left_base_term, right_base_term]),
415 }
416 )),
417 Token::Ge => ast::Term::Atom(self.arena.alloc(ast::Atom {
418 sym: ge_sym(self.arena),
419 args: alloc_slice!(self, &[left_base_term, right_base_term]),
420 })),
421 _ => unreachable!(),
422 };
423 Ok(alloc!(self, term))
424 }
425 Token::Ident { .. } => {
426 let atom = self.parse_atom()?;
427 if let Some(interval) = self.try_parse_interval()? {
428 Ok(alloc!(self, ast::Term::TemporalAtom(atom, interval)))
429 } else {
430 Ok(alloc!(self, ast::Term::Atom(atom)))
431 }
432 }
433 _ => bail!("parse_term: unexpected token {:?}", self.token),
434 }
435 }
436
437 fn parse_bracket_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
439 self.expect(Token::LBracket)?;
440 self.parse_atoms(atoms)?;
441 self.expect(Token::RBracket)?;
442 Ok(())
443 }
444
445 fn parse_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
447 if let Token::Ident { .. } = self.token {
448 atoms.push(self.parse_atom()?);
449 loop {
450 if Token::Comma != self.token {
451 break;
452 }
453 self.next_token()?;
454 let atom = self.parse_atom()?;
455 atoms.push(atom);
456 }
457 }
458 Ok(())
459 }
460
461 pub fn parse_atom(&mut self) -> Result<&'arena ast::Atom<'arena>> {
464 let mut name_buf = match &self.token {
465 Token::Ident { name } => name.clone(),
466 _ => bail!("parse_atom: expected identifer got {}", self.token),
467 };
468
469 self.next_token()?;
470
471 while self.token == Token::Dot {
473 self.next_token()?;
477 match &self.token {
478 Token::Ident { name: next_name } => {
479 name_buf.push('.');
480 name_buf.push_str(next_name);
481 self.next_token()?;
482 }
483 _ => {
484 bail!(
490 "parse_atom: expected identifier after `.` in qualified name, got {}",
491 self.token
492 );
493 }
494 }
495 }
496
497 let name = self.arena.alloc_str(&name_buf);
498
499 self.expect(Token::LParen)?;
500 let mut args = vec![];
501 if Token::RParen != self.token {
502 self.parse_base_terms(&mut args)?;
503 }
504 self.expect(Token::RParen)?;
505 let args = alloc_slice!(self, &args);
506 Ok(alloc!(
507 self,
508 ast::Atom {
509 sym: self.arena.predicate_sym(name, None),
510 args
511 }
512 ))
513 }
514
515 fn parse_transforms(
516 &mut self,
517 transforms: &mut Vec<&'arena ast::TransformStmt<'arena>>,
518 ) -> Result<()> {
519 if Token::Do == self.token {
520 self.next_token()?;
521 let expr = self.parse_base_term()?;
522 transforms.push(alloc!(
523 self,
524 ast::TransformStmt {
525 var: None,
526 app: expr
527 }
528 ));
529 self.expect(Token::Semi)?;
530 }
531 loop {
532 if Token::Let != self.token {
533 break;
534 }
535 self.next_token()?;
536 if let Token::Ident { name } = &self.token {
537 let name = alloc_str!(self, name.as_str());
538 self.next_token()?;
539 self.expect(Token::Eq)?;
540 let expr = self.parse_base_term()?;
541 transforms.push(alloc!(
542 self,
543 ast::TransformStmt {
544 var: Some(name),
545 app: expr
546 }
547 ))
548 }
549 if let Token::Dot = self.token {
550 break;
551 }
552 self.expect(Token::Semi)?;
553 }
554 Ok(())
555 }
556
557 fn try_parse_interval(&mut self) -> Result<Option<ast::Interval>> {
563 if self.token != Token::At {
564 return Ok(None);
565 }
566 self.next_token()?; self.expect(Token::LBracket)?;
568 let start = self.parse_temporal_bound(true)?;
569 let end = if self.token == Token::Comma {
570 self.next_token()?;
571 self.parse_temporal_bound(false)?
572 } else {
573 start
575 };
576 self.expect(Token::RBracket)?;
577 Ok(Some(ast::Interval { start, end }))
578 }
579
580 fn parse_temporal_bound(&mut self, is_start: bool) -> Result<ast::TemporalBound> {
582 match &self.token {
583 Token::Timestamp { nanos } => {
584 let nanos = *nanos;
585 self.next_token()?;
586 Ok(ast::TemporalBound::Timestamp(nanos))
587 }
588 Token::Ident { name } if name == "_" => {
589 self.next_token()?;
590 if is_start {
591 Ok(ast::TemporalBound::NegInf)
592 } else {
593 Ok(ast::TemporalBound::PosInf)
594 }
595 }
596 Token::Ident { name } if is_variable(name) => {
597 let var_idx = self.arena.variable_sym(name);
598 self.next_token()?;
599 Ok(ast::TemporalBound::Variable(var_idx))
600 }
601 _ => bail!("parse_temporal_bound: expected timestamp, variable, or '_', got {:?}", self.token),
602 }
603 }
604
605 pub fn parse_base_term(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
615 match &self.token {
616 Token::LBracket => return self.parse_list_or_map(),
617 Token::LBrace => return self.parse_struct(),
618 _ => {}
619 }
620
621 let mut is_type = false;
622 let mut base_term = match &self.token {
623 Token::Ident { name } if name == "_" => {
624 let unique = format!("_Anon{}", self.anon_counter);
625 self.anon_counter += 1;
626 ast::BaseTerm::Variable(self.arena.variable_sym(&unique))
627 }
628 Token::Ident { name } if is_variable(name) => {
629 ast::BaseTerm::Variable(self.arena.variable_sym(name))
630 }
631 Token::Ident { name } if is_fn(name) => {
632 let name = self.arena.alloc_str(name);
633 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
635 }
636 Token::DotIdent { name } => {
637 let name = self.arena.alloc_str(name);
638 is_type = true;
639 ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
641 }
642 Token::String { decoded } => {
643 let value = self.arena.alloc_str(decoded.as_str());
644 ast::BaseTerm::Const(ast::Const::String(value))
645 }
646 Token::Bytes { decoded } => {
647 let value = self.arena.alloc_slice_copy(decoded);
648 ast::BaseTerm::Const(ast::Const::Bytes(value))
649 }
650 Token::Int { decoded } => ast::BaseTerm::Const(ast::Const::Number(*decoded)),
651 Token::Float { decoded } => ast::BaseTerm::Const(ast::Const::Float(*decoded)),
652 Token::Timestamp { nanos } => ast::BaseTerm::Const(ast::Const::Time(*nanos)),
653 Token::Duration { nanos } => ast::BaseTerm::Const(ast::Const::Duration(*nanos)),
654 Token::Name { name } => {
655 let name = self.arena.intern(name);
656 ast::BaseTerm::Const(ast::Const::Name(name))
657 }
658 _ => bail!("parse_base_term: unexpected token {:?}", self.token),
659 };
660 self.next_token()?;
661 if let ast::BaseTerm::ApplyFn(fn_sym, _) = base_term {
662 let mut fn_args = vec![];
663 if is_type {
664 self.parse_langle_base_terms(&mut fn_args)?;
665 } else {
666 self.parse_paren_base_terms(&mut fn_args)?;
667 }
668 let fn_args = self.arena.alloc_slice_copy(&fn_args);
669 base_term = ast::BaseTerm::ApplyFn(fn_sym, fn_args);
670 }
671 let base_term = alloc!(self, base_term);
672 Ok(base_term)
673 }
674
675 fn parse_list_or_map(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
676 self.expect(Token::LBracket)?;
677 if Token::RBracket == self.token {
678 self.next_token()?;
679 return Ok(alloc!(
680 self,
681 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), &[])
682 ));
683 }
684 let first = self.parse_base_term()?;
685 let expr = if Token::Colon != self.token {
686 self.expect(Token::Comma)?;
687 let mut items = vec![first];
688 self.parse_base_terms(&mut items)?;
689 ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), alloc_slice!(self, &items))
690 } else {
691 self.expect(Token::Colon)?; let first_val = self.parse_base_term()?;
693 let mut items = vec![first, first_val];
694 loop {
695 if Token::Comma != self.token {
696 break;
697 }
698 self.next_token()?;
699 if Token::RBracket == self.token {
700 break; }
702 items.push(self.parse_base_term()?);
703 self.expect(Token::Colon)?;
704 items.push(self.parse_base_term()?);
705 }
706 ast::BaseTerm::ApplyFn(fn_map_sym(self.arena), alloc_slice!(self, &items))
707 };
708 self.expect(Token::RBracket)?;
709 Ok(alloc!(self, expr))
710 }
711
712 fn parse_struct(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
713 self.expect(Token::LBrace)?;
714 if Token::RBrace == self.token {
715 self.next_token()?;
716 return Ok(alloc!(
717 self,
718 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), &[])
719 ));
720 }
721 let mut items = vec![];
722 let name = self.parse_base_term()?;
723 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
724 items.push(name)
725 } else {
726 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}",);
727 }
728 self.expect(Token::Colon)?;
729 items.push(self.parse_base_term()?);
730 loop {
731 if Token::Comma != self.token {
732 break;
733 }
734 self.next_token()?;
735 if Token::RBrace == self.token {
736 break; }
738 let name = self.parse_base_term()?;
739 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
740 items.push(name)
741 } else {
742 bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}");
743 }
744 self.expect(Token::Colon)?;
745 items.push(self.parse_base_term()?);
746 }
747 self.expect(Token::RBrace)?;
748 Ok(alloc!(
749 self,
750 ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), alloc_slice!(self, &items))
751 ))
752 }
753
754 fn parse_langle_base_terms(
760 &mut self,
761 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
762 ) -> Result<()> {
763 self.expect(Token::Lt)?;
764 if Token::Gt == self.token {
765 self.next_token()?;
766 return Ok(());
767 }
768 base_terms.push(self.parse_base_term()?);
770 if Token::Colon == self.token {
771 self.next_token()?;
772 base_terms.push(self.parse_base_term()?);
773 }
774 while Token::Comma == self.token {
776 self.next_token()?;
777 if !base_term_start(&self.token) {
778 break; }
780 base_terms.push(self.parse_base_term()?);
781 if Token::Colon == self.token {
782 self.next_token()?;
783 base_terms.push(self.parse_base_term()?);
784 }
785 }
786 self.expect(Token::Gt)?;
787 Ok(())
788 }
789
790 fn parse_paren_base_terms(
792 &mut self,
793 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
794 ) -> Result<()> {
795 self.expect(Token::LParen)?;
796 if Token::RParen != self.token {
797 self.parse_base_terms(base_terms)?;
798 }
799 self.expect(Token::RParen)?;
800 Ok(())
801 }
802
803 fn parse_base_terms(
805 &mut self,
806 base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
807 ) -> Result<()> {
808 base_terms.push(self.parse_base_term()?);
809 while let Token::Comma = self.token {
810 self.next_token()?;
811 if !base_term_start(&self.token) {
812 break; }
814 base_terms.push(self.parse_base_term()?);
815 }
816
817 Ok(())
818 }
819}
820
821fn is_variable(name: &str) -> bool {
822 name.chars().next().unwrap().is_ascii_uppercase()
823}
824
825fn is_fn(name: &str) -> bool {
826 name.starts_with("fn:")
827}
828
829fn base_term_start(t: &Token) -> bool {
830 match t {
831 Token::Name { .. }
832 | Token::Int { .. }
833 | Token::Float { .. }
834 | Token::String { .. }
835 | Token::Bytes { .. }
836 | Token::Timestamp { .. }
837 | Token::Duration { .. }
838 | Token::LBracket
839 | Token::LBrace
840 | Token::DotIdent { .. } => true,
841 Token::Ident { name } => is_variable(name) || is_fn(name) || name == "_",
842 _ => false,
843 }
844}
845
846#[cfg(test)]
847mod test {
848
849 use super::*;
850 use googletest::prelude::{eq, gtest, verify_that};
851
852 fn make_parser<'arena>(
853 arena: &'arena Arena,
854 input: &'arena str,
855 ) -> Parser<'arena, &'arena [u8]> {
856 let mut p = Parser::new(arena, input.as_bytes(), "test");
857 p.next_token().unwrap();
858 p
859 }
860
861 #[test]
862 fn test_empty_unit() -> Result<()> {
863 let arena = Arena::new_with_global_interner();
864 let mut p = make_parser(&arena, "");
865 match p.parse_unit()? {
866 &ast::Unit { decls: &[pkg], .. } => {
867 assert_eq!(pkg, &empty_package_decl(&arena));
868 }
869 z => panic!("unexpected: {:?}", z),
870 }
871 Ok(())
872 }
873
874 #[test]
875 fn test_package_use() -> Result<()> {
876 let arena = Arena::new_with_global_interner();
877 let input = "Package foo[bar()]! Use baz[bar()]!";
878
879 let mut p = make_parser(&arena, input);
880 let unit = p.parse_unit()?;
881 match unit.decls {
882 &[
883 &ast::Decl {
884 atom:
885 &ast::Atom {
886 sym: got_package_sym,
887 ..
888 },
889 descr:
890 &[
891 &ast::Atom {
892 sym: got_name_sym1,
893 args: &[ast::BaseTerm::Const(ast::Const::String("foo"))],
894 },
895 &ast::Atom {
896 sym: got_bar_sym1,
897 args: &[],
898 },
899 ],
900 ..
901 },
902 &ast::Decl {
903 atom:
904 &ast::Atom {
905 sym: got_use_sym, ..
906 },
907 descr:
908 &[
909 &ast::Atom {
910 sym: got_name_sym2,
911 args: &[ast::BaseTerm::Const(ast::Const::String("baz"))],
912 },
913 &ast::Atom {
914 sym: got_bar_sym2,
915 args: &[],
916 },
917 ],
918 ..
919 },
920 ] => {
921 assert_eq!(got_use_sym, use_sym(&arena));
922 assert_eq!(got_package_sym, package_sym(&arena));
923 assert_eq!(got_name_sym1, name_sym(&arena));
924 assert_eq!(got_name_sym2, name_sym(&arena));
925 assert_eq!(got_bar_sym1, arena.predicate_sym("bar", None));
926 assert_eq!(got_bar_sym2, arena.predicate_sym("bar", None));
927 }
928 z => panic!("unexpected {z:?}"),
929 }
930 Ok(())
931 }
932
933 #[test]
934 fn test_decl() -> Result<()> {
935 let arena = Arena::new_with_global_interner();
936 let input = "Decl foo(X, Y).";
937 let mut p = make_parser(&arena, input);
938 match p.parse_decl()? {
939 &ast::Decl {
940 atom:
941 &ast::Atom {
942 sym: got_foo_sym,
943 args:
944 &[
945 &ast::BaseTerm::Variable(x_sym),
946 &ast::BaseTerm::Variable(y_sym),
947 ],
948 },
949 ..
950 } => {
951 assert_eq!(got_foo_sym, arena.predicate_sym("foo", None));
952 assert_eq!(x_sym, arena.variable_sym("X"));
953 assert_eq!(y_sym, arena.variable_sym("Y"))
954 }
955 decl => panic!("got {:?}", decl),
956 };
957 Ok(())
958 }
959
960 #[test]
961 fn test_base_term() -> googletest::Result<()> {
962 let arena = Arena::new_with_global_interner();
963 let input = "X 3 1.5 'foo' /foo fn:list() fn:list(/a) fn:list(/a, 3)"; let mut p = make_parser(&arena, input);
965 let mut got_base_terms = vec![];
966 loop {
967 if Token::Eof == p.token {
968 break;
969 }
970 let base_term = p.parse_base_term().unwrap();
972 got_base_terms.push(base_term);
973 }
974 let expected = vec![
975 arena.variable("X"),
976 arena.const_(ast::Const::Number(3)),
977 arena.const_(ast::Const::Float(1.5)),
978 arena.const_(ast::Const::String("foo")),
979 arena.const_(arena.name("/foo")),
980 arena.apply_fn(fn_list_sym(&arena), &[]),
981 arena.apply_fn(fn_list_sym(&arena), &[arena.const_(arena.name("/a"))]),
982 arena.apply_fn(
983 fn_list_sym(&arena),
984 &[
985 arena.const_(arena.name("/a")),
986 arena.const_(ast::Const::Number(3)),
987 ],
988 ),
989 ];
990 verify_that!(got_base_terms, eq(&expected))
991 }
992
993 #[test]
994 fn test_term() -> googletest::Result<()> {
995 let arena = Arena::new_with_global_interner();
996 let input = "foo(/bar) !bar() X = Z X != 3 3 < 1 3 <= 1";
997 let mut p = make_parser(&arena, input);
998 let mut got_terms = vec![];
999 loop {
1000 if Token::Eof == p.token {
1001 break;
1002 }
1003 got_terms.push(p.parse_term().unwrap());
1005 }
1006 let expected = [
1007 &ast::Term::Atom(arena.atom(
1008 arena.predicate_sym("foo", None),
1009 &[arena.const_(arena.name("/bar"))],
1010 )),
1011 &ast::Term::NegAtom(arena.atom(arena.predicate_sym("bar", None), &[])),
1012 &ast::Term::Eq(arena.variable("X"), arena.variable("Z")),
1013 &ast::Term::Ineq(
1014 arena.variable("X"),
1015 arena.alloc(ast::BaseTerm::Const(ast::Const::Number(3))),
1016 ),
1017 &ast::Term::Atom(arena.atom(
1018 arena.predicate_sym(":lt", Some(2)),
1019 &[
1020 arena.const_(ast::Const::Number(3)),
1021 arena.const_(ast::Const::Number(1)),
1022 ],
1023 )),
1024 &ast::Term::Atom(arena.atom(
1025 arena.predicate_sym(":le", Some(2)),
1026 &[
1027 arena.const_(ast::Const::Number(3)),
1028 arena.const_(ast::Const::Number(1)),
1029 ],
1030 )),
1031 ];
1032 verify_that!(got_terms, eq(&expected))
1033 }
1034
1035 #[gtest]
1036 fn test_structured_data_and_types() -> googletest::Result<()> {
1037 let arena = Arena::new_with_global_interner();
1038 let input =
1039 "[] [1,2,3] [1: 'one', 2: 'two'] {} {/foo: /bar} {/name: \"alice\", /age: 30} .List<.Option</name>, /string>";
1040 let mut p = make_parser(&arena, input);
1041 let mut got_base_terms = vec![];
1042 loop {
1043 if Token::Eof == p.token {
1044 break;
1045 }
1046 let base_term = p.parse_base_term().unwrap();
1048 got_base_terms.push(base_term);
1049 }
1050 let expected = vec![
1051 arena.apply_fn(fn_list_sym(&arena), &[]),
1052 arena.apply_fn(
1053 fn_list_sym(&arena),
1054 &[
1055 arena.const_(ast::Const::Number(1)),
1056 arena.const_(ast::Const::Number(2)),
1057 arena.const_(ast::Const::Number(3)),
1058 ],
1059 ),
1060 arena.apply_fn(
1061 fn_map_sym(&arena),
1062 &[
1063 arena.const_(ast::Const::Number(1)),
1064 arena.const_(ast::Const::String("one")),
1065 arena.const_(ast::Const::Number(2)),
1066 arena.const_(ast::Const::String("two")),
1067 ],
1068 ),
1069 arena.apply_fn(fn_struct_sym(&arena), &[]),
1070 arena.apply_fn(
1071 fn_struct_sym(&arena),
1072 &[
1073 arena.const_(arena.name("/foo")),
1074 arena.const_(arena.name("/bar")),
1075 ],
1076 ),
1077 arena.apply_fn(
1078 fn_struct_sym(&arena),
1079 &[
1080 arena.const_(arena.name("/name")),
1081 arena.const_(ast::Const::String("alice")),
1082 arena.const_(arena.name("/age")),
1083 arena.const_(ast::Const::Number(30)),
1084 ],
1085 ),
1086 arena.apply_fn(
1087 fn_list_type_sym(&arena),
1088 &[
1089 arena.apply_fn(
1090 fn_option_type_sym(&arena),
1091 &[arena.const_(arena.name("/name"))],
1092 ),
1093 arena.const_(arena.name("/string")),
1094 ],
1095 ),
1096 ];
1097 verify_that!(got_base_terms, eq(&expected))
1098 }
1099
1100 #[gtest]
1101 fn test_trailing_commas() -> googletest::Result<()> {
1102 let arena = Arena::new_with_global_interner();
1103 let input = "[1, 2, 3,] [1: 'one', 2: 'two',] {/a: 1, /b: 2,}";
1104 let mut p = make_parser(&arena, input);
1105 let mut got_base_terms = vec![];
1106 loop {
1107 if Token::Eof == p.token {
1108 break;
1109 }
1110 let base_term = p.parse_base_term().unwrap();
1111 got_base_terms.push(base_term);
1112 }
1113 let expected = vec![
1114 arena.apply_fn(
1115 fn_list_sym(&arena),
1116 &[
1117 arena.const_(ast::Const::Number(1)),
1118 arena.const_(ast::Const::Number(2)),
1119 arena.const_(ast::Const::Number(3)),
1120 ],
1121 ),
1122 arena.apply_fn(
1123 fn_map_sym(&arena),
1124 &[
1125 arena.const_(ast::Const::Number(1)),
1126 arena.const_(ast::Const::String("one")),
1127 arena.const_(ast::Const::Number(2)),
1128 arena.const_(ast::Const::String("two")),
1129 ],
1130 ),
1131 arena.apply_fn(
1132 fn_struct_sym(&arena),
1133 &[
1134 arena.const_(arena.name("/a")),
1135 arena.const_(ast::Const::Number(1)),
1136 arena.const_(arena.name("/b")),
1137 arena.const_(ast::Const::Number(2)),
1138 ],
1139 ),
1140 ];
1141 verify_that!(got_base_terms, eq(&expected))
1142 }
1143
1144 #[test]
1145 fn test_clause() -> Result<()> {
1146 let arena = Arena::new_with_global_interner();
1147 let mut p = make_parser(&arena, "foo(X).");
1148 let clause = p.parse_clause()?;
1149 match clause {
1150 &ast::Clause {
1151 head:
1152 &ast::Atom {
1153 args: &[ast::BaseTerm::Variable(x_sym)],
1154 ..
1155 },
1156 premises: &[],
1157 transform: &[],
1158 ..
1159 } => {
1160 assert_eq!(*x_sym, arena.variable_sym("X"));
1161 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1162 }
1163 _ => panic!("unexpected: {:?}", clause),
1164 }
1165 let mut p = make_parser(&arena, "foo(X) :- !bar(X).");
1166 let clause = p.parse_clause()?;
1167 match clause {
1168 &ast::Clause {
1169 head:
1170 &ast::Atom {
1171 sym: foo_sym,
1172 args: _,
1173 },
1174 premises:
1175 &[
1176 &ast::Term::NegAtom(&ast::Atom {
1177 sym: bar_sym,
1178 args: _,
1179 }),
1180 ],
1181 transform: &[],
1182 ..
1183 } => {
1184 assert_eq!(foo_sym, arena.predicate_sym("foo", None));
1185 assert_eq!(bar_sym, arena.predicate_sym("bar", None));
1186 }
1187 _ => panic!("unexpected: {:?}", clause),
1188 };
1189 let mut p = make_parser(
1190 &arena,
1191 "foo(Z) ⟸ bar(Y) |> do fn:group_by(); let X = fn:count(Y).",
1192 );
1193
1194 let clause = p.parse_clause()?;
1195 match clause {
1196 &ast::Clause {
1197 head: &ast::Atom { .. },
1198 premises: &[&ast::Term::Atom(ast::Atom { .. })],
1199 transform:
1200 &[
1201 &ast::TransformStmt {
1202 var: None,
1203 app: ast::BaseTerm::ApplyFn(first_sym, _),
1204 },
1205 &ast::TransformStmt {
1206 var: Some("X"),
1207 app: ast::BaseTerm::ApplyFn(second_sym, _),
1208 },
1209 ],
1210 ..
1211 } => {
1212 assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1213 assert_eq!(clause.transform.len(), 2);
1214 assert_eq!(*first_sym, arena.function_sym("fn:group_by", None));
1215 assert_eq!(*second_sym, arena.function_sym("fn:count", None));
1216 }
1217 _ => panic!("unexpected: {:?}", clause),
1218 }
1219
1220 Ok(())
1221 }
1222
1223 #[test]
1224 fn test_anonymous_variable_single() -> Result<()> {
1225 let arena = Arena::new_with_global_interner();
1226 let mut p = make_parser(&arena, "foo(_, X).");
1227 let clause = p.parse_clause()?;
1228 match clause.head.args {
1230 &[&ast::BaseTerm::Variable(anon), &ast::BaseTerm::Variable(x)] => {
1231 assert_eq!(anon, arena.variable_sym("_Anon0"));
1232 assert_eq!(x, arena.variable_sym("X"));
1233 }
1234 _ => panic!("unexpected args: {:?}", clause.head.args),
1235 }
1236 Ok(())
1237 }
1238
1239 #[test]
1240 fn test_anonymous_variable_multiple_distinct() -> Result<()> {
1241 let arena = Arena::new_with_global_interner();
1242 let mut p = make_parser(&arena, "foo(_, _, _).");
1243 let clause = p.parse_clause()?;
1244 match clause.head.args {
1246 &[
1247 &ast::BaseTerm::Variable(a0),
1248 &ast::BaseTerm::Variable(a1),
1249 &ast::BaseTerm::Variable(a2),
1250 ] => {
1251 assert_eq!(a0, arena.variable_sym("_Anon0"));
1252 assert_eq!(a1, arena.variable_sym("_Anon1"));
1253 assert_eq!(a2, arena.variable_sym("_Anon2"));
1254 assert_ne!(a0, a1);
1256 assert_ne!(a1, a2);
1257 }
1258 _ => panic!("unexpected args: {:?}", clause.head.args),
1259 }
1260 Ok(())
1261 }
1262
1263 #[test]
1264 fn test_anonymous_variable_in_rule_body() -> Result<()> {
1265 let arena = Arena::new_with_global_interner();
1266 let mut p = make_parser(&arena, "result(X) :- foo(X, _).");
1267 let clause = p.parse_clause()?;
1268 assert_eq!(clause.head.sym, arena.predicate_sym("result", None));
1269 match clause.premises {
1270 &[&ast::Term::Atom(&ast::Atom { args, .. })] => match args {
1271 &[&ast::BaseTerm::Variable(x), &ast::BaseTerm::Variable(anon)] => {
1272 assert_eq!(x, arena.variable_sym("X"));
1273 assert_eq!(anon, arena.variable_sym("_Anon0"));
1274 }
1275 _ => panic!("unexpected args: {:?}", args),
1276 },
1277 _ => panic!("unexpected premises: {:?}", clause.premises),
1278 }
1279 Ok(())
1280 }
1281
1282 #[test]
1283 fn test_anonymous_variable_with_negation() -> Result<()> {
1284 let arena = Arena::new_with_global_interner();
1285 let mut p = make_parser(&arena, "orphan(X) :- node(X, _), !has_parent(X).");
1286 let clause = p.parse_clause()?;
1287 assert_eq!(clause.head.sym, arena.predicate_sym("orphan", None));
1288 assert_eq!(clause.premises.len(), 2);
1289 match clause.premises[0] {
1291 &ast::Term::Atom(&ast::Atom { args, .. }) => match args {
1292 &[&ast::BaseTerm::Variable(_), &ast::BaseTerm::Variable(anon)] => {
1293 assert_eq!(anon, arena.variable_sym("_Anon0"));
1294 }
1295 _ => panic!("unexpected args: {:?}", args),
1296 },
1297 _ => panic!("expected Atom, got {:?}", clause.premises[0]),
1298 }
1299 match clause.premises[1] {
1301 &ast::Term::NegAtom(&ast::Atom { sym, .. }) => {
1302 assert_eq!(sym, arena.predicate_sym("has_parent", None));
1303 }
1304 _ => panic!("expected NegAtom, got {:?}", clause.premises[1]),
1305 }
1306 Ok(())
1307 }
1308
1309 #[test]
1315 fn test_temporal_fact_with_interval() -> Result<()> {
1316 let arena = Arena::new_with_global_interner();
1317 let mut p = make_parser(&arena, "foo(/bar)@[2024-01-15, 2024-06-30].");
1318 let clause = p.parse_clause()?;
1319 assert!(clause.head_time.is_some(), "expected temporal annotation");
1320 let interval = clause.head_time.unwrap();
1321 match interval.start {
1322 ast::TemporalBound::Timestamp(_) => {}
1323 _ => panic!("expected Timestamp start, got {:?}", interval.start),
1324 }
1325 match interval.end {
1326 ast::TemporalBound::Timestamp(_) => {}
1327 _ => panic!("expected Timestamp end, got {:?}", interval.end),
1328 }
1329 Ok(())
1330 }
1331
1332 #[test]
1334 fn test_temporal_fact_point_interval() -> Result<()> {
1335 let arena = Arena::new_with_global_interner();
1336 let mut p = make_parser(&arena, "event(/login)@[2024-03-15].");
1337 let clause = p.parse_clause()?;
1338 assert!(clause.head_time.is_some(), "expected temporal annotation");
1339 let interval = clause.head_time.unwrap();
1340 assert_eq!(interval.start, interval.end);
1342 Ok(())
1343 }
1344
1345 #[test]
1347 fn test_non_temporal_fact() -> Result<()> {
1348 let arena = Arena::new_with_global_interner();
1349 let mut p = make_parser(&arena, "regular(/fact).");
1350 let clause = p.parse_clause()?;
1351 assert!(clause.head_time.is_none(), "non-temporal fact should have no annotation");
1352 Ok(())
1353 }
1354
1355 #[test]
1357 fn test_temporal_declaration() -> Result<()> {
1358 let arena = Arena::new_with_global_interner();
1359 let mut p = make_parser(&arena, "Decl employee(X) temporal bound [/name].");
1360 let unit = p.parse_unit()?;
1361 assert_eq!(unit.decls.len(), 2);
1363 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1364 Ok(())
1365 }
1366
1367 #[test]
1369 fn test_non_temporal_declaration() -> Result<()> {
1370 let arena = Arena::new_with_global_interner();
1371 let mut p = make_parser(&arena, "Decl config(X) bound [/string].");
1372 let unit = p.parse_unit()?;
1373 assert_eq!(unit.decls.len(), 2);
1374 assert!(!unit.decls[1].is_temporal, "expected non-temporal declaration");
1375 Ok(())
1376 }
1377
1378 #[test]
1380 fn test_temporal_declaration_with_descr() -> Result<()> {
1381 let arena = Arena::new_with_global_interner();
1382 let input = r#"Decl status(X, Y) temporal
1383 descr [doc("Employee status over time")]
1384 bound [/name, /string]."#;
1385 let mut p = make_parser(&arena, input);
1386 let unit = p.parse_unit()?;
1387 assert_eq!(unit.decls.len(), 2);
1388 assert!(unit.decls[1].is_temporal, "expected temporal declaration");
1389 Ok(())
1390 }
1391
1392 #[test]
1394 fn test_backward_compat_no_temporal() -> Result<()> {
1395 let programs = [
1397 "edge(/a, /b). path(X, Y) :- edge(X, Y).",
1398 "all(/a). excluded(/a). included(X) :- all(X), !excluded(X).",
1399 "age(/alice, 30). adult(Name) :- age(Name, Age), Age >= 18 .",
1400 ];
1401 for prog in &programs {
1402 let arena = Arena::new_with_global_interner();
1403 let mut p = make_parser(&arena, prog);
1404 let unit = p.parse_unit()?;
1405 for clause in unit.clauses {
1406 assert!(clause.head_time.is_none(), "clause should not have temporal annotation in: {prog}");
1407 }
1408 }
1409 Ok(())
1410 }
1411
1412 #[test]
1414 fn test_temporal_rule_with_variable_interval() -> Result<()> {
1415 let arena = Arena::new_with_global_interner();
1416 let mut p = make_parser(&arena, "reachable(X, Y)@[T] :- link(X, Y)@[T].");
1417 let clause = p.parse_clause()?;
1418 assert!(clause.head_time.is_some());
1420 let interval = clause.head_time.unwrap();
1421 match interval.start {
1422 ast::TemporalBound::Variable(_) => {}
1423 _ => panic!("expected Variable start, got {:?}", interval.start),
1424 }
1425 assert_eq!(interval.start, interval.end);
1427 assert_eq!(clause.premises.len(), 1);
1429 match clause.premises[0] {
1430 ast::Term::TemporalAtom(_, _) => {}
1431 _ => panic!("expected TemporalAtom, got {:?}", clause.premises[0]),
1432 }
1433 Ok(())
1434 }
1435
1436 #[test]
1438 fn test_temporal_rule_with_interval_range() -> Result<()> {
1439 let arena = Arena::new_with_global_interner();
1440 let mut p = make_parser(&arena, "reachable(X, Y)@[S, E] :- link(X, Y)@[S, E].");
1441 let clause = p.parse_clause()?;
1442 let interval = clause.head_time.unwrap();
1443 match interval.start {
1444 ast::TemporalBound::Variable(v) => {
1445 assert_eq!(arena.lookup_name(v.0).unwrap(), "S");
1446 }
1447 _ => panic!("expected Variable start"),
1448 }
1449 match interval.end {
1450 ast::TemporalBound::Variable(v) => {
1451 assert_eq!(arena.lookup_name(v.0).unwrap(), "E");
1452 }
1453 _ => panic!("expected Variable end"),
1454 }
1455 Ok(())
1456 }
1457
1458 #[test]
1460 fn test_temporal_wildcard_bounds() -> Result<()> {
1461 let arena = Arena::new_with_global_interner();
1462 let mut p = make_parser(&arena, "always(/true)@[_, _].");
1463 let clause = p.parse_clause()?;
1464 let interval = clause.head_time.unwrap();
1465 assert_eq!(interval.start, ast::TemporalBound::NegInf);
1466 assert_eq!(interval.end, ast::TemporalBound::PosInf);
1467 Ok(())
1468 }
1469
1470 #[gtest]
1473 fn test_colon_syntax_in_angle_brackets() -> googletest::Result<()> {
1474 let arena = Arena::new_with_global_interner();
1475 let input = ".Struct</x : /number, /y : /string>";
1476 let mut p = make_parser(&arena, input);
1477 let got = p.parse_base_term().unwrap();
1478
1479 let struct_type_sym = arena.function_sym("fn:Struct", None);
1481 let expected = arena.apply_fn(
1482 struct_type_sym,
1483 &[
1484 arena.const_(arena.name("/x")),
1485 arena.const_(arena.name("/number")),
1486 arena.const_(arena.name("/y")),
1487 arena.const_(arena.name("/string")),
1488 ],
1489 );
1490 verify_that!(got, eq(expected))
1491 }
1492
1493 #[gtest]
1495 fn test_tagged_union_colon_syntax() -> googletest::Result<()> {
1496 let arena = Arena::new_with_global_interner();
1497 let input = ".TaggedUnion</kind, /move : .Struct</x : /number>, /quit : .Struct<>>";
1498 let mut p = make_parser(&arena, input);
1499 let got = p.parse_base_term().unwrap();
1500
1501 let tu_sym = arena.function_sym("fn:TaggedUnion", None);
1502 let struct_sym = arena.function_sym("fn:Struct", None);
1503 let expected = arena.apply_fn(
1504 tu_sym,
1505 &[
1506 arena.const_(arena.name("/kind")),
1507 arena.const_(arena.name("/move")),
1508 arena.apply_fn(
1509 struct_sym,
1510 &[
1511 arena.const_(arena.name("/x")),
1512 arena.const_(arena.name("/number")),
1513 ],
1514 ),
1515 arena.const_(arena.name("/quit")),
1516 arena.apply_fn(struct_sym, &[]),
1517 ],
1518 );
1519 verify_that!(got, eq(expected))
1520 }
1521
1522 #[gtest]
1524 fn test_mixed_colon_syntax() -> googletest::Result<()> {
1525 let arena = Arena::new_with_global_interner();
1526 let input = ".List</number>";
1528 let mut p = make_parser(&arena, input);
1529 let got = p.parse_base_term().unwrap();
1530
1531 let list_sym = arena.function_sym("fn:List", None);
1532 let expected = arena.apply_fn(list_sym, &[arena.const_(arena.name("/number"))]);
1533 verify_that!(got, eq(expected))
1534 }
1535
1536 #[gtest]
1540 fn test_paren_syntax_unchanged() -> googletest::Result<()> {
1541 let arena = Arena::new_with_global_interner();
1542 let input = "fn:Struct(/x, /number, /y, /string)";
1543 let mut p = make_parser(&arena, input);
1544 let got = p.parse_base_term().unwrap();
1545
1546 let struct_type_sym = arena.function_sym("fn:Struct", None);
1547 let expected = arena.apply_fn(
1548 struct_type_sym,
1549 &[
1550 arena.const_(arena.name("/x")),
1551 arena.const_(arena.name("/number")),
1552 arena.const_(arena.name("/y")),
1553 arena.const_(arena.name("/string")),
1554 ],
1555 );
1556 verify_that!(got, eq(expected))
1557 }
1558
1559 #[gtest]
1561 fn test_colon_syntax_trailing_comma() -> googletest::Result<()> {
1562 let arena = Arena::new_with_global_interner();
1563 let input = ".Struct</x : /number,>";
1564 let mut p = make_parser(&arena, input);
1565 let got = p.parse_base_term().unwrap();
1566
1567 let struct_type_sym = arena.function_sym("fn:Struct", None);
1568 let expected = arena.apply_fn(
1569 struct_type_sym,
1570 &[
1571 arena.const_(arena.name("/x")),
1572 arena.const_(arena.name("/number")),
1573 ],
1574 );
1575 verify_that!(got, eq(expected))
1576 }
1577}