1#![allow(dead_code)]
2use ast::Constraints;
17use bumpalo::Bump;
18use std::io;
24
25use anyhow::{anyhow, bail, Result};
26use mangle_ast as ast;
27
28mod error;
29mod quote;
30mod scan;
31mod token;
32
33pub use error::{ErrorContext, ParseError};
34use token::Token;
35
36pub struct Parser<'b, R>
37where
38 R: io::Read,
39{
40 sc: scan::Scanner<R>,
41 token: crate::token::Token,
42 bump: &'b Bump,
43}
44
45const PACKAGE_SYM: ast::PredicateSym = ast::PredicateSym {
46 name: "Package",
47 arity: Some(0),
48};
49
50const NAME_SYM: ast::PredicateSym = ast::PredicateSym {
51 name: "name",
52 arity: Some(1),
53};
54
55const USE_SYM: ast::PredicateSym = ast::PredicateSym {
56 name: "Use",
57 arity: Some(0),
58};
59
60const LT_SYM: ast::PredicateSym = ast::PredicateSym {
61 name: ":lt",
62 arity: Some(2),
63};
64
65const LE_SYM: ast::PredicateSym = ast::PredicateSym {
66 name: ":le",
67 arity: Some(2),
68};
69
70const FN_LIST_SYM: ast::FunctionSym = ast::FunctionSym {
71 name: "fn:list",
72 arity: None,
73};
74const FN_MAP_SYM: ast::FunctionSym = ast::FunctionSym {
75 name: "fn:map",
76 arity: None,
77};
78const FN_STRUCT_SYM: ast::FunctionSym = ast::FunctionSym {
79 name: "fn:struct",
80 arity: None,
81};
82
83const EMPTY_PACKAGE: ast::Decl<'_> = ast::Decl {
84 atom: &ast::Atom {
85 sym: PACKAGE_SYM,
86 args: &[],
87 },
88 descr: &[&ast::Atom {
89 sym: NAME_SYM,
90 args: &[&ast::BaseTerm::Const(ast::Const::String(""))],
91 }],
92 bounds: None,
93 constraints: None,
94};
95
96macro_rules! alloc {
97 ($self:expr, $e:expr) => {
98 &*$self.bump.alloc($e)
99 };
100}
101
102macro_rules! alloc_str {
103 ($self:expr, $e:expr) => {
104 &*$self.bump.alloc_str($e)
105 };
106}
107
108macro_rules! alloc_slice {
109 ($self:expr, $e:expr) => {
110 &*$self.bump.alloc_slice_copy($e)
111 };
112}
113
114impl<'b, R> Parser<'b, R>
115where
116 R: io::Read,
117{
118 pub fn new<P: ToString>(bump: &'b Bump, reader: R, path: P) -> Self
119 where
120 R: io::Read,
121 {
122 Self {
123 sc: scan::Scanner::new(reader, path),
124 token: token::Token::Illegal,
125 bump,
126 }
127 }
128
129 fn next_token(&mut self) -> Result<()> {
130 self.token = self.sc.next_token()?;
131 Ok(())
132 }
133
134 fn expect(&mut self, expected: Token) -> Result<()> {
136 if expected != self.token {
137 let error = ParseError::Unexpected(
138 self.sc.get_error_context(),
139 expected.clone(),
140 self.token.clone(),
141 );
142 return Err(anyhow!(error));
143 }
144 self.next_token()
145 }
146
147 pub fn parse_unit(&mut self) -> Result<&'b ast::Unit<'b>> {
148 let package = if matches!(self.token.clone(), Token::Package) {
149 self.parse_package_decl()?
150 } else {
151 &EMPTY_PACKAGE
152 };
153 let mut decls = vec![package];
154 while let Token::Use = self.token {
155 decls.push(self.parse_use_decl()?);
156 }
157 let decls: &'b [&'b ast::Decl<'b>] = &*self.bump.alloc_slice_copy(&decls);
158 let unit: &'b ast::Unit<'b> = &*self.bump.alloc(ast::Unit {
159 clauses: &[],
160 decls,
161 });
162 Ok(unit)
163 }
164
165 pub fn parse_package_decl(&mut self) -> Result<&'b ast::Decl<'b>> {
167 self.expect(Token::Package)?;
168 let name = match &self.token {
169 Token::Ident { name } => name.as_str(),
170 _ => bail!("expected identifer got {}", self.token),
171 };
172
173 let name_atom = ast::Atom {
174 sym: NAME_SYM,
175 args: &[&ast::BaseTerm::Const(ast::Const::String(name))],
176 };
177 let mut descr_atoms: Vec<&ast::Atom> = vec![ast::copy_atom(self.bump, &name_atom)];
178 self.next_token()?;
179 if Token::LBracket == self.token {
180 self.parse_bracket_atoms(&mut descr_atoms)?;
181 }
182
183 self.expect(Token::Bang)?;
184
185 let package_atom = alloc!(
186 self,
187 ast::Atom {
188 sym: PACKAGE_SYM,
189 args: &[],
190 }
191 );
192
193 let decl: &'b ast::Decl = alloc!(
195 self,
196 ast::Decl {
197 atom: package_atom,
198 bounds: None,
199 descr: alloc_slice!(self, &descr_atoms),
200 constraints: None,
201 }
202 );
203 Ok(decl)
204 }
205
206 fn parse_use_decl(&mut self) -> Result<&'b ast::Decl<'b>> {
207 self.expect(Token::Use)?;
208 let use_atom = alloc!(
209 self,
210 ast::Atom {
211 sym: USE_SYM,
212 args: &[],
213 }
214 );
215
216 let name = match &self.token {
217 Token::Ident { name } => name.as_str(),
218 _ => bail!("parse_use_decl: expected identifer got {}", self.token),
219 };
220
221 let name: &'b str = alloc_str!(self, name);
222 let name = alloc!(self, ast::BaseTerm::Const(ast::Const::String(name)));
223 let args = alloc_slice!(self, &[name]);
224
225 let mut descr_atoms: Vec<&ast::Atom> = vec![self.bump.alloc(ast::Atom {
226 sym: NAME_SYM,
227 args,
228 })];
229 self.next_token()?;
230 if Token::LBracket == self.token {
231 self.parse_bracket_atoms(&mut descr_atoms)?;
232 }
233
234 let descr_atoms = alloc_slice!(self, &descr_atoms);
235 Ok(alloc!(
236 self,
237 ast::Decl {
238 atom: use_atom,
239 descr: descr_atoms,
240 bounds: None,
241 constraints: None,
242 }
243 ))
244 }
245
246 fn parse_decl(&mut self) -> Result<&'b ast::Decl<'b>> {
247 self.expect(Token::Decl)?;
248 let atom = self.parse_atom()?;
249 let mut descr_atoms = vec![];
250 if Token::Descr == self.token {
251 self.next_token()?;
252 self.parse_bracket_atoms(&mut descr_atoms)?;
253 }
254 let mut bound_decls = vec![];
255 loop {
256 if Token::Bound != self.token {
257 break;
258 }
259 bound_decls.push(self.parse_bounds_decl()?);
260 }
261 let bounds = if bound_decls.is_empty() {
262 None
263 } else {
264 Some(alloc_slice!(self, &bound_decls))
265 };
266 let constraints = if Token::Inclusion == self.token {
267 Some(self.parse_inclusion_constraint()?)
268 } else {
269 None
270 };
271 self.expect(Token::Dot)?;
272 Ok(alloc!(
273 self,
274 ast::Decl {
275 atom,
276 descr: alloc_slice!(self, &descr_atoms),
277 bounds,
278 constraints,
279 }
280 ))
281 }
282
283 fn parse_bounds_decl(&mut self) -> Result<&'b ast::BoundDecl<'b>> {
285 self.expect(Token::Bound)?;
286 self.expect(Token::LBracket)?;
287 let mut base_terms = vec![];
288 self.parse_base_terms(&mut base_terms)?;
289 self.expect(Token::RBracket)?;
290 let base_terms = alloc_slice!(self, &base_terms);
291 let bound_decl = alloc!(self, ast::BoundDecl { base_terms });
292 Ok(bound_decl)
293 }
294
295 fn parse_inclusion_constraint(&mut self) -> Result<&'b ast::Constraints<'b>> {
296 self.expect(Token::Inclusion)?;
297 let mut consequences = vec![];
298 self.parse_bracket_atoms(&mut consequences)?;
299 let consequences = alloc_slice!(self, &consequences);
300 Ok(alloc!(
301 self,
302 Constraints {
303 consequences,
304 alternatives: &[]
305 }
306 ))
307 }
308
309 fn parse_clause(&mut self) -> Result<&'b ast::Clause<'b>> {
310 let head = self.parse_atom()?;
311 let mut premises = vec![];
312 let mut transform = vec![];
313 if let Token::ColonDash = self.token {
314 self.next_token()?;
315 self.parse_terms(&mut premises)?;
316 if let Token::PipeGt = self.token {
317 self.next_token()?;
318 self.parse_transforms(&mut transform)?;
319 }
320 }
321 self.expect(Token::Dot)?;
322 let premises = alloc_slice!(self, &premises);
323 let transform = alloc_slice!(self, &transform);
324 Ok(alloc!(
325 self,
326 ast::Clause {
327 head,
328 premises,
329 transform,
330 }
331 ))
332 }
333
334 fn parse_terms(&mut self, terms: &mut Vec<&'b ast::Term<'b>>) -> Result<()> {
336 terms.push(self.parse_term()?);
337 loop {
338 if Token::Comma != self.token {
339 return Ok(());
340 }
341 self.next_token()?;
342 terms.push(self.parse_term()?);
343 }
344 }
345
346 fn parse_term(&mut self) -> Result<&'b ast::Term<'b>> {
347 match &self.token {
348 Token::Bang => {
349 self.next_token()?;
350 let atom = self.parse_atom()?;
351 Ok(alloc!(self, ast::Term::NegAtom(atom)))
352 }
353 t if base_term_start(t) => {
354 let left_base_term = self.parse_base_term()?;
355 let op = self.token.clone();
356 match op {
357 Token::Eq | Token::BangEq | Token::Lt | Token::Le => self.next_token()?,
358 _ => bail!("parse_terms: expected `=` or `!=` got {}", self.token),
359 };
360 let right_base_term = self.parse_base_term()?;
361 let term = match op {
362 Token::Eq => ast::Term::Eq(left_base_term, right_base_term),
363 Token::BangEq => ast::Term::Ineq(left_base_term, right_base_term),
364 Token::Lt => ast::Term::Atom(alloc!(
365 self,
366 ast::Atom {
367 sym: LT_SYM,
368 args: alloc_slice!(self, &[left_base_term, right_base_term]),
369 }
370 )),
371 Token::Le => ast::Term::Atom(self.bump.alloc(ast::Atom {
372 sym: LE_SYM,
373 args: alloc_slice!(self, &[left_base_term, right_base_term]),
374 })),
375 _ => unreachable!(),
376 };
377 Ok(alloc!(self, term))
378 }
379 Token::Ident { .. } => {
380 let atom = self.parse_atom()?;
381 Ok(alloc!(self, ast::Term::Atom(atom)))
382 }
383 _ => bail!("parse_term: unexpected token {:?}", self.token),
384 }
385 }
386
387 fn parse_bracket_atoms(&mut self, atoms: &mut Vec<&'b ast::Atom<'b>>) -> Result<()> {
389 self.expect(Token::LBracket)?;
390 self.parse_atoms(atoms)?;
391 self.expect(Token::RBracket)?;
392 Ok(())
393 }
394
395 fn parse_atoms(&mut self, atoms: &mut Vec<&'b ast::Atom<'b>>) -> Result<()> {
397 if let Token::Ident { .. } = self.token {
398 atoms.push(self.parse_atom()?);
399 loop {
400 if Token::Comma != self.token {
401 break;
402 }
403 self.next_token()?;
404 let atom = self.parse_atom()?;
405 atoms.push(atom);
406 }
407 }
408 Ok(())
409 }
410
411 fn parse_atom(&mut self) -> Result<&'b ast::Atom<'b>> {
413 let name = match &self.token {
414 Token::Ident { name } => name.as_str(),
415 _ => bail!("parse_atom: expected identifer got {}", self.token),
416 };
417 let name = &*self.bump.alloc_str(name);
418
419 self.next_token()?;
420 self.expect(Token::LParen)?;
421 let mut args = vec![];
422 if Token::RParen != self.token {
423 self.parse_base_terms(&mut args)?;
424 }
425 self.expect(Token::RParen)?;
426 let args = alloc_slice!(self, &args);
427 Ok(alloc!(
428 self,
429 ast::Atom {
430 sym: ast::PredicateSym { name, arity: None },
431 args,
432 }
433 ))
434 }
435
436 fn parse_transforms(&mut self, transforms: &mut Vec<&'b ast::TransformStmt<'b>>) -> Result<()> {
437 if Token::Do == self.token {
438 self.next_token()?;
439 let expr = self.parse_base_term()?;
440 transforms.push(alloc!(
441 self,
442 ast::TransformStmt {
443 var: None,
444 app: expr
445 }
446 ));
447 self.expect(Token::Semi)?;
448 }
449 loop {
450 if Token::Let != self.token {
451 break;
452 }
453 self.next_token()?;
454 if let Token::Ident { name } = &self.token {
455 let name = alloc_str!(self, name.as_str());
456 self.next_token()?;
457 self.expect(Token::Eq)?;
458 let expr = self.parse_base_term()?;
459 transforms.push(alloc!(
460 self,
461 ast::TransformStmt {
462 var: Some(name),
463 app: expr
464 }
465 ))
466 }
467 if let Token::Dot = self.token {
468 break;
469 }
470 self.expect(Token::Semi)?;
471 }
472 Ok(())
473 }
474
475 fn parse_base_term(&mut self) -> Result<&'b ast::BaseTerm<'b>> {
483 match &self.token {
484 Token::LBracket => return self.parse_list_or_map(),
485 Token::LBrace => return self.parse_struct(),
486 _ => {}
487 }
488
489 let mut base_term = match &self.token {
490 Token::Ident { name } if is_variable(name) => {
491 let name = alloc_str!(self, &name);
492 ast::BaseTerm::Variable(name)
493 }
494 Token::Ident { name } if is_fn(name) => {
495 let name = self.bump.alloc_str(name);
496 ast::BaseTerm::ApplyFn(ast::FunctionSym { name, arity: None }, &[])
498 }
499 Token::String { decoded } => {
500 let value = self.bump.alloc_str(decoded.as_str());
501 ast::BaseTerm::Const(ast::Const::String(value))
502 }
503 Token::Bytes { decoded } => {
504 let value = self.bump.alloc_slice_copy(decoded);
505 ast::BaseTerm::Const(ast::Const::Bytes(value))
506 }
507 Token::Int { decoded } => ast::BaseTerm::Const(ast::Const::Number(*decoded)),
508 Token::Float { decoded } => ast::BaseTerm::Const(ast::Const::Float(*decoded)),
509 Token::Name { name } => {
510 let name = self.bump.alloc_str(name.as_str());
511 ast::BaseTerm::Const(ast::Const::Name(name))
512 }
513 _ => bail!("parse_base_term: unexpected token {:?}", self.token),
514 };
515 self.next_token()?;
516 if let ast::BaseTerm::ApplyFn(fn_sym, _) = base_term {
517 let mut fn_args = vec![];
518 self.parse_paren_base_terms(&mut fn_args)?;
519 let fn_args = self.bump.alloc_slice_copy(&fn_args);
520 base_term = ast::BaseTerm::ApplyFn(fn_sym, fn_args);
521 }
522 let base_term = alloc!(self, base_term);
523 Ok(base_term)
524 }
525
526 fn parse_list_or_map(&mut self) -> Result<&'b ast::BaseTerm<'b>> {
527 self.expect(Token::LBracket)?;
528 if Token::RBracket == self.token {
529 self.next_token()?;
530 return Ok(alloc!(self, ast::BaseTerm::ApplyFn(FN_MAP_SYM, &[])));
531 }
532 let first = self.parse_base_term()?;
533 let expr = if Token::Colon != self.token {
534 self.expect(Token::Comma)?;
535 let mut items = vec![first];
536 self.parse_base_terms(&mut items)?;
537 ast::BaseTerm::ApplyFn(FN_LIST_SYM, alloc_slice!(self, &items))
538 } else {
539 self.expect(Token::Colon)?; let first_val = self.parse_base_term()?;
541 let mut items = vec![first, first_val];
542 loop {
543 if Token::Comma != self.token {
544 break;
545 }
546 self.next_token()?;
547 items.push(self.parse_base_term()?);
548 self.expect(Token::Colon)?;
549 items.push(self.parse_base_term()?);
550 }
551 ast::BaseTerm::ApplyFn(FN_MAP_SYM, alloc_slice!(self, &items))
552 };
553 self.expect(Token::RBracket)?;
554 Ok(alloc!(self, expr))
555 }
556
557 fn parse_struct(&mut self) -> Result<&'b ast::BaseTerm<'b>> {
558 self.expect(Token::LBrace)?;
559 if Token::RBrace == self.token {
560 self.next_token()?;
561 return Ok(alloc!(self, ast::BaseTerm::ApplyFn(FN_STRUCT_SYM, &[])));
562 }
563 let mut items = vec![];
564 let name = self.parse_base_term()?;
565 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
566 items.push(name)
567 } else {
568 bail!(
569 "parse_base_term: expected name in struct expression {{ ... }} got {:?}",
570 name
571 );
572 }
573 self.expect(Token::Colon)?;
574 items.push(self.parse_base_term()?);
575 loop {
576 if Token::Comma != self.token {
577 break;
578 }
579 let name = self.parse_base_term()?;
580 if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
581 items.push(name)
582 } else {
583 bail!(
584 "parse_base_term: expected name in struct expression {{ ... }} got {:?}",
585 name
586 );
587 }
588 self.expect(Token::Colon)?;
589 items.push(self.parse_base_term()?);
590 }
591 self.expect(Token::RBrace)?;
592 Ok(alloc!(
593 self,
594 ast::BaseTerm::ApplyFn(FN_STRUCT_SYM, alloc_slice!(self, &items))
595 ))
596 }
597
598 fn parse_paren_base_terms(
600 &mut self,
601 base_terms: &mut Vec<&'b ast::BaseTerm<'b>>,
602 ) -> Result<()> {
603 self.expect(Token::LParen)?;
604 if Token::RParen != self.token {
605 self.parse_base_terms(base_terms)?;
606 }
607 self.expect(Token::RParen)?;
608 Ok(())
609 }
610
611 fn parse_base_terms(&mut self, base_terms: &mut Vec<&'b ast::BaseTerm<'b>>) -> Result<()> {
613 base_terms.push(self.parse_base_term()?);
614 while let Token::Comma = self.token {
615 self.next_token()?;
616 base_terms.push(self.parse_base_term()?);
617 }
618
619 Ok(())
620 }
621}
622
623fn is_variable(name: &str) -> bool {
624 name.chars().next().unwrap().is_ascii_uppercase()
625}
626
627fn is_fn(name: &str) -> bool {
628 name.starts_with("fn:")
629}
630
631fn base_term_start(t: &Token) -> bool {
632 match t {
633 Token::Name { .. }
634 | Token::Int { .. }
635 | Token::Float { .. }
636 | Token::String { .. }
637 | Token::Bytes { .. }
638 | Token::LBracket
639 | Token::LBrace => true,
640 Token::Ident { name } => is_variable(name) || is_fn(name),
641 _ => false,
642 }
643}
644
645#[cfg(test)]
646mod test {
647
648 use super::*;
649
650 fn make_parser<'b>(bump: &'b Bump, input: &'b str) -> Parser<'b, &'b [u8]> {
651 let mut p = Parser::new(bump, input.as_bytes(), "test");
652 p.next_token().unwrap();
653 p
654 }
655
656 #[test]
657 fn test_empty_unit() -> Result<()> {
658 let bump = Bump::new();
659 let mut p = make_parser(&bump, "");
660 match p.parse_unit()? {
661 ast::Unit { decls: &[pkg], .. } if *pkg == EMPTY_PACKAGE => {}
662 z => panic!("unexpected: {:?}", z),
663 }
664 Ok(())
665 }
666
667 #[test]
668 fn test_package_use() -> Result<()> {
669 let bump = Bump::new();
670 let input = "Package foo[bar()]! Use baz[bar()]!";
671
672 let mut p = make_parser(&bump, input);
674 let unit = p.parse_unit()?;
675 match unit.decls {
676 &[&ast::Decl {
677 atom: &ast::Atom {
678 sym: PACKAGE_SYM, ..
679 },
680 descr:
681 &[&ast::Atom {
682 sym: NAME_SYM,
683 args: &[ast::BaseTerm::Const(ast::Const::String("foo"))],
684 }, &ast::Atom {
685 sym:
686 ast::PredicateSym {
687 name: "bar",
688 arity: None,
689 },
690 args: &[],
691 }],
692 ..
693 }, &ast::Decl {
694 atom: &ast::Atom { sym: USE_SYM, .. },
695 descr:
696 &[&ast::Atom {
697 sym: NAME_SYM,
698 args: &[ast::BaseTerm::Const(ast::Const::String("baz"))],
699 }, &ast::Atom {
700 sym:
701 ast::PredicateSym {
702 name: "bar",
703 arity: None,
704 },
705 args: &[],
706 }],
707 ..
708 }] => {}
709 z => panic!("unexpcted {z:?}"),
710 }
711 Ok(())
712 }
713
714 #[test]
715 fn test_decl() -> Result<()> {
716 let bump = Bump::new();
717 let input = "Decl foo(X, Y).";
718 let mut p = make_parser(&bump, input);
719 match p.parse_decl()? {
720 ast::Decl {
721 atom:
722 &ast::Atom {
723 sym:
724 ast::PredicateSym {
725 name: "foo",
726 arity: None,
727 },
728 args: &[&ast::BaseTerm::Variable("X"), &ast::BaseTerm::Variable("Y")],
729 },
730 ..
731 } => {}
732 decl => panic!("got {:?}", decl),
733 };
734 Ok(())
735 }
736
737 #[test]
738 fn test_base_term() -> Result<()> {
739 let bump = Bump::new();
740 let input = "X 3 1.5 'foo' /foo fn:list() fn:list(/a) fn:list(/a, 3)"; let mut p = make_parser(&bump, input);
742 let mut got_base_terms = vec![];
743 loop {
744 if Token::Eof == p.token {
745 break;
746 }
747 match p.parse_base_term() {
748 Ok(base_term) => got_base_terms.push(base_term),
749 Err(err) => {
750 println!("err: {err:?}");
751 return Err(err);
752 }
753 }
754 }
755 let expected = vec![
756 &ast::BaseTerm::Variable("X"),
757 &ast::BaseTerm::Const(ast::Const::Number(3)),
758 &ast::BaseTerm::Const(ast::Const::Float(1.5)),
759 &ast::BaseTerm::Const(ast::Const::String("foo")),
760 &ast::BaseTerm::Const(ast::Const::Name("/foo")),
761 &ast::BaseTerm::ApplyFn(
762 ast::FunctionSym {
763 name: "fn:list",
764 arity: None,
765 },
766 &[],
767 ),
768 &ast::BaseTerm::ApplyFn(
769 ast::FunctionSym {
770 name: "fn:list",
771 arity: None,
772 },
773 &[&ast::BaseTerm::Const(ast::Const::Name("/a"))],
774 ),
775 &ast::BaseTerm::ApplyFn(
776 ast::FunctionSym {
777 name: "fn:list",
778 arity: None,
779 },
780 &[
781 &ast::BaseTerm::Const(ast::Const::Name("/a")),
782 &ast::BaseTerm::Const(ast::Const::Number(3)),
783 ],
784 ),
785 ];
786 assert!(
787 expected == got_base_terms,
788 "want: {expected:?}\n got: {got_base_terms:?}"
789 );
790 Ok(())
791 }
792
793 #[test]
794 fn test_term() -> Result<()> {
795 let bump = Bump::new();
796 let input = "foo(/bar) !bar() X = Z X != 3 3 < 1 3 <= 1";
797 let mut p = make_parser(&bump, input);
798 let mut got_terms = vec![];
799 loop {
800 if Token::Eof == p.token {
801 break;
802 }
803 match p.parse_term() {
804 Ok(term) => got_terms.push(term),
805 Err(err) => {
806 println!("err: {err:?}");
807 return Err(err);
808 }
809 }
810 }
811 let expected = vec![
812 &ast::Term::Atom(&ast::Atom {
813 sym: ast::PredicateSym {
814 name: "foo",
815 arity: None,
816 },
817 args: &[&ast::BaseTerm::Const(ast::Const::Name("/bar"))],
818 }),
819 &ast::Term::NegAtom(&ast::Atom {
820 sym: ast::PredicateSym {
821 name: "bar",
822 arity: None,
823 },
824 args: &[],
825 }),
826 &ast::Term::Eq(&ast::BaseTerm::Variable("X"), &ast::BaseTerm::Variable("Z")),
827 &ast::Term::Ineq(
828 &ast::BaseTerm::Variable("X"),
829 &ast::BaseTerm::Const(ast::Const::Number(3)),
830 ),
831 &ast::Term::Atom(&ast::Atom {
832 sym: ast::PredicateSym {
833 name: ":lt",
834 arity: Some(2),
835 },
836 args: &[
837 &ast::BaseTerm::Const(ast::Const::Number(3)),
838 &ast::BaseTerm::Const(ast::Const::Number(1)),
839 ],
840 }),
841 &ast::Term::Atom(&ast::Atom {
842 sym: ast::PredicateSym {
843 name: ":le",
844 arity: Some(2),
845 },
846 args: &[
847 &ast::BaseTerm::Const(ast::Const::Number(3)),
848 &ast::BaseTerm::Const(ast::Const::Number(1)),
849 ],
850 }),
851 ];
852 if expected != got_terms {
853 for (l, r) in expected.iter().zip(got_terms.iter()) {
854 println!("{:?} == {:?} ? {}", l, r, l == r)
855 }
856 }
857 assert!(
858 expected == got_terms,
859 "want: {expected:?}\n got: {got_terms:?}"
860 );
861 Ok(())
862 }
863
864 #[test]
865 fn test_base_terms() -> Result<()> {
866 let bump = Bump::new();
867 let input = "[] [1,2,3] [1: 'one', 2: 'two'] {} {/foo: /bar}";
868 let mut p = make_parser(&bump, input);
869 let mut got_base_terms = vec![];
870 loop {
871 if Token::Eof == p.token {
872 break;
873 }
874 match p.parse_base_term() {
875 Ok(term) => got_base_terms.push(term),
876 Err(err) => {
877 println!("err: {err:?}");
878 return Err(err);
879 }
880 }
881 }
882 Ok(())
883 }
884
885 #[test]
886 fn test_clause() -> Result<()> {
887 let bump = Bump::new();
888 let mut p = make_parser(&bump, "foo(X).");
889 let clause = p.parse_clause()?;
890 assert!(matches!(
891 clause,
892 ast::Clause {
893 head: ast::Atom {
894 sym: ast::PredicateSym {
895 name: "foo",
896 arity: None
897 },
898 args: &[ast::BaseTerm::Variable("X")]
899 },
900 premises: &[],
901 transform: &[],
902 }
903 ));
904 let mut p = make_parser(&bump, "foo(X) :- !bar(X).");
905 let clause = p.parse_clause()?;
906 assert!(matches!(
907 clause,
908 ast::Clause {
909 head: ast::Atom {
910 sym: ast::PredicateSym { name: "foo", .. },
911 args: _
912 },
913 premises: &[&ast::Term::NegAtom(ast::Atom {
914 sym: ast::PredicateSym { name: "bar", .. },
915 args: _
916 })],
917 transform: &[],
918 }
919 ));
920 let mut p = make_parser(
921 &bump,
922 "foo(Z) :- bar(Y) |> do fn:group_by(); let X = fn:count(Y).",
923 );
924
925 let clause = p.parse_clause()?;
926 assert!(matches!(
927 clause,
928 ast::Clause {
929 head: ast::Atom {
930 sym: ast::PredicateSym {
931 name: "foo",
932 arity: None
933 },
934 args: _
935 },
936 premises: &[&ast::Term::Atom(ast::Atom { .. })],
937 transform: &[
938 &ast::TransformStmt {
939 var: None,
940 app: ast::BaseTerm::ApplyFn(
941 ast::FunctionSym {
942 name: "fn:group_by",
943 arity: None
944 },
945 _
946 )
947 },
948 &ast::TransformStmt {
949 var: Some("X"),
950 app: ast::BaseTerm::ApplyFn(
951 ast::FunctionSym {
952 name: "fn:count",
953 arity: None
954 },
955 _
956 )
957 }
958 ],
959 }
960 ));
961
962 Ok(())
963 }
964}