mangle_parse/
lib.rs

1#![allow(dead_code)]
2// Copyright 2024 Google LLC
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use ast::Constraints;
17use bumpalo::Bump;
18/// Open the file in read-only mode with buffer.
19///
20/// let file = std::fs::File::open(path)?;
21/// let reader = std::io::BufReader::new(file);
22///
23use 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    // Check that token is the expected one and advance.
135    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    /// package_decl ::= `package` name (`[` `]`)? `!`
166    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 descr_atoms = ;
194        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    /// bound_decl ::= `bound` `[` base_term {`,` base_term} `]`
284    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    /// terms ::= term { , term }
335    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    // bracket_atoms ::= `[` [ atom {`,` atom } ] `]`
388    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    // `atoms ::= [ atom {`,` atom } ]
396    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    // atom ::= name `(` args `)`
412    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    // base_term ::= var
476    //             | fun`(`[base_term {',' base_term}`)`
477    //             | string_constant
478    //             | bytes_constant
479    //             | number_constant
480    //             | float_constant
481    //             | name_constant
482    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                // Arguments parsed below.
497                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)?; // is a map
540            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    /// paren_base_terms ::=  `(` [base_terms] `)`
599    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    /// base_terms ::= base_term { `,` base_term }
612    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 = Parser::new(&bump, input);
673        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)"; //.as_bytes();
741        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}