Skip to main content

mangle_parse/
lib.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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 fn_list_sym(arena: &Arena) -> ast::FunctionIndex {
59    arena.function_sym("fn:list", None)
60}
61
62fn fn_map_sym(arena: &Arena) -> ast::FunctionIndex {
63    arena.function_sym("fn:map", None)
64}
65
66fn fn_struct_sym(arena: &Arena) -> ast::FunctionIndex {
67    arena.function_sym("fn:struct", None)
68}
69
70fn fn_list_type_sym(arena: &Arena) -> ast::FunctionIndex {
71    arena.function_sym("fn:List", None)
72}
73
74fn fn_option_type_sym(arena: &Arena) -> ast::FunctionIndex {
75    arena.function_sym("fn:Option", None)
76}
77
78fn empty_package_decl(arena: &Arena) -> ast::Decl<'_> {
79    ast::Decl {
80        atom: arena.alloc(ast::Atom {
81            sym: package_sym(arena),
82            args: &[],
83        }),
84        descr: arena.alloc_slice_copy(&[arena.alloc(ast::Atom {
85            sym: name_sym(arena),
86            args: arena
87                .alloc_slice_copy(&[arena.alloc(ast::BaseTerm::Const(ast::Const::String("")))]),
88        })]),
89        bounds: None,
90        constraints: None,
91    }
92}
93
94macro_rules! alloc {
95    ($self:expr, $e:expr) => {
96        &*$self.arena.alloc($e)
97    };
98}
99
100macro_rules! alloc_str {
101    ($self:expr, $e:expr) => {
102        &*$self.arena.alloc_str($e)
103    };
104}
105
106macro_rules! alloc_slice {
107    ($self:expr, $e:expr) => {
108        &*$self.arena.alloc_slice_copy($e)
109    };
110}
111
112impl<'arena, R> Parser<'arena, R>
113where
114    R: io::Read,
115{
116    pub fn new<P: ToString>(arena: &'arena Arena, reader: R, path: P) -> Self
117    where
118        R: io::Read,
119    {
120        Self {
121            sc: scan::Scanner::new(reader, path),
122            token: token::Token::Illegal,
123            arena,
124            anon_counter: 0,
125        }
126    }
127
128    pub fn next_token(&mut self) -> Result<()> {
129        self.token = self.sc.next_token()?;
130        Ok(())
131    }
132
133    // Check that token is the expected one and advance.
134    fn expect(&mut self, expected: Token) -> Result<()> {
135        if expected != self.token {
136            let error = ParseError::Unexpected(
137                self.sc.get_error_context(),
138                expected.clone(),
139                self.token.clone(),
140            );
141            return Err(anyhow!(error));
142        }
143        self.next_token()
144    }
145
146    pub fn parse_unit(&mut self) -> Result<&'arena ast::Unit<'arena>> {
147        let package = if matches!(self.token.clone(), Token::Package) {
148            self.parse_package_decl()?
149        } else {
150            self.arena.alloc(empty_package_decl(self.arena))
151        };
152        let mut decls = vec![package];
153        while let Token::Use = self.token {
154            decls.push(self.parse_use_decl()?);
155        }
156        let mut clauses = vec![];
157        loop {
158            match self.token {
159                Token::Eof => break,
160                Token::Decl => decls.push(self.parse_decl()?),
161                _ => clauses.push(self.parse_clause()?),
162            }
163        }
164        let decls: &'arena [&'arena ast::Decl<'arena>] = self.arena.alloc_slice_copy(&decls);
165        let clauses: &'arena [&'arena ast::Clause<'arena>] = self.arena.alloc_slice_copy(&clauses);
166        let unit: &'arena ast::Unit<'arena> = &*self.arena.alloc(ast::Unit { clauses, decls });
167        Ok(unit)
168    }
169
170    /// package_decl ::= `package` name (`[` `]`)? `!`
171    pub fn parse_package_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
172        self.expect(Token::Package)?;
173        let package_name: &'arena str = if let Token::Ident { name } = &self.token {
174            self.arena.alloc_str(name.as_str())
175        } else {
176            bail!("expected identifer got {}", self.token);
177        };
178
179        let name_atom: &'arena ast::Atom<'arena> = self.arena.alloc(ast::Atom {
180            sym: name_sym(self.arena),
181            args: self.arena.alloc_slice_copy(&[self
182                .arena
183                .alloc(ast::BaseTerm::Const(ast::Const::String(package_name)))]),
184        });
185        let mut descr_atoms: Vec<&'arena ast::Atom<'arena>> = vec![name_atom];
186        self.next_token()?;
187        if Token::LBracket == self.token {
188            self.parse_bracket_atoms(&mut descr_atoms)?;
189        }
190        let descr = alloc_slice!(self, &descr_atoms);
191
192        self.expect(Token::Bang)?;
193
194        let package_atom = alloc!(
195            self,
196            ast::Atom {
197                sym: package_sym(self.arena),
198                args: &[]
199            }
200        );
201
202        //let descr_atoms = ;
203        let decl: &'arena ast::Decl = alloc!(
204            self,
205            ast::Decl {
206                atom: package_atom,
207                bounds: None,
208                descr,
209                constraints: None
210            }
211        );
212        Ok(decl)
213    }
214
215    fn parse_use_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
216        self.expect(Token::Use)?;
217        let use_atom = alloc!(
218            self,
219            ast::Atom {
220                sym: use_sym(self.arena),
221                args: &[]
222            }
223        );
224
225        let name = match &self.token {
226            Token::Ident { name } => name.as_str(),
227            _ => bail!("parse_use_decl: expected identifer got {}", self.token),
228        };
229
230        let name: &'arena str = alloc_str!(self, name);
231        let name = alloc!(self, ast::BaseTerm::Const(ast::Const::String(name)));
232        let args = alloc_slice!(self, &[name]);
233
234        let mut descr_atoms: Vec<&ast::Atom> = vec![self.arena.alloc(ast::Atom {
235            sym: name_sym(self.arena),
236            args,
237        })];
238        self.next_token()?;
239        if Token::LBracket == self.token {
240            self.parse_bracket_atoms(&mut descr_atoms)?;
241        }
242        self.expect(Token::Bang)?;
243
244        let descr_atoms = alloc_slice!(self, &descr_atoms);
245        Ok(alloc!(
246            self,
247            ast::Decl {
248                atom: use_atom,
249                descr: descr_atoms,
250                bounds: None,
251                constraints: None
252            }
253        ))
254    }
255
256    fn parse_decl(&mut self) -> Result<&'arena ast::Decl<'arena>> {
257        self.expect(Token::Decl)?;
258        let atom = self.parse_atom()?;
259        let mut descr_atoms = vec![];
260        if Token::Descr == self.token {
261            self.next_token()?;
262            self.parse_bracket_atoms(&mut descr_atoms)?;
263        }
264        let mut bound_decls = vec![];
265        loop {
266            if Token::Bound != self.token {
267                break;
268            }
269            bound_decls.push(self.parse_bounds_decl()?);
270        }
271        let bounds = if bound_decls.is_empty() {
272            None
273        } else {
274            Some(alloc_slice!(self, &bound_decls))
275        };
276        let constraints = if Token::Inclusion == self.token {
277            Some(self.parse_inclusion_constraint()?)
278        } else {
279            None
280        };
281        self.expect(Token::Dot)?;
282        Ok(alloc!(
283            self,
284            ast::Decl {
285                atom,
286                descr: alloc_slice!(self, &descr_atoms),
287                bounds,
288                constraints
289            }
290        ))
291    }
292
293    /// bound_decl ::= `bound` `[` base_term {`,` base_term} `]`
294    fn parse_bounds_decl(&mut self) -> Result<&'arena ast::BoundDecl<'arena>> {
295        self.expect(Token::Bound)?;
296        self.expect(Token::LBracket)?;
297        let mut base_terms = vec![];
298        self.parse_base_terms(&mut base_terms)?;
299        self.expect(Token::RBracket)?;
300        let base_terms = alloc_slice!(self, &base_terms);
301        let bound_decl = alloc!(self, ast::BoundDecl { base_terms });
302        Ok(bound_decl)
303    }
304
305    fn parse_inclusion_constraint(&mut self) -> Result<&'arena ast::Constraints<'arena>> {
306        self.expect(Token::Inclusion)?;
307        let mut consequences = vec![];
308        self.parse_bracket_atoms(&mut consequences)?;
309        let consequences = alloc_slice!(self, &consequences);
310        Ok(alloc!(
311            self,
312            Constraints {
313                consequences,
314                alternatives: &[]
315            }
316        ))
317    }
318
319    pub fn parse_clause(&mut self) -> Result<&'arena ast::Clause<'arena>> {
320        let head = self.parse_atom()?;
321        let mut premises = vec![];
322        let mut transform = vec![];
323        match self.token {
324            Token::ColonDash | Token::LongLeftDoubleArrow => {
325                self.next_token()?;
326                self.parse_terms(&mut premises)?;
327                if let Token::PipeGt = self.token {
328                    self.next_token()?;
329                    self.parse_transforms(&mut transform)?;
330                }
331            }
332            _ => {}
333        }
334        self.expect(Token::Dot)?;
335        let premises = alloc_slice!(self, &premises);
336        let transform = alloc_slice!(self, &transform);
337        Ok(alloc!(
338            self,
339            ast::Clause {
340                head,
341                premises,
342                transform
343            }
344        ))
345    }
346
347    /// terms ::= term { , term }
348    fn parse_terms(&mut self, terms: &mut Vec<&'arena ast::Term<'arena>>) -> Result<()> {
349        terms.push(self.parse_term()?);
350        loop {
351            if Token::Comma != self.token {
352                return Ok(());
353            }
354            self.next_token()?;
355            terms.push(self.parse_term()?);
356        }
357    }
358
359    pub fn parse_term(&mut self) -> Result<&'arena ast::Term<'arena>> {
360        match &self.token {
361            Token::Bang => {
362                self.next_token()?;
363                let atom = self.parse_atom()?;
364                Ok(alloc!(self, ast::Term::NegAtom(atom)))
365            }
366            t if base_term_start(t) => {
367                let left_base_term = self.parse_base_term()?;
368                let op = self.token.clone();
369                match op {
370                    Token::Eq | Token::BangEq | Token::Lt | Token::Le => self.next_token()?,
371                    _ => bail!("parse_terms: expected `=` or `!=` got {}", self.token),
372                };
373                let right_base_term = self.parse_base_term()?;
374                let term = match op {
375                    Token::Eq => ast::Term::Eq(left_base_term, right_base_term),
376                    Token::BangEq => ast::Term::Ineq(left_base_term, right_base_term),
377                    Token::Lt => ast::Term::Atom(alloc!(
378                        self,
379                        ast::Atom {
380                            sym: lt_sym(self.arena),
381                            args: alloc_slice!(self, &[left_base_term, right_base_term]),
382                        }
383                    )),
384                    Token::Le => ast::Term::Atom(self.arena.alloc(ast::Atom {
385                        sym: le_sym(self.arena),
386                        args: alloc_slice!(self, &[left_base_term, right_base_term]),
387                    })),
388                    _ => unreachable!(),
389                };
390                Ok(alloc!(self, term))
391            }
392            Token::Ident { .. } => {
393                let atom = self.parse_atom()?;
394                Ok(alloc!(self, ast::Term::Atom(atom)))
395            }
396            _ => bail!("parse_term: unexpected token {:?}", self.token),
397        }
398    }
399
400    // bracket_atoms ::= `[` [ atom {`,` atom } ] `]`
401    fn parse_bracket_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
402        self.expect(Token::LBracket)?;
403        self.parse_atoms(atoms)?;
404        self.expect(Token::RBracket)?;
405        Ok(())
406    }
407
408    // `atoms ::= [ atom {`,` atom } ]
409    fn parse_atoms(&mut self, atoms: &mut Vec<&'arena ast::Atom<'arena>>) -> Result<()> {
410        if let Token::Ident { .. } = self.token {
411            atoms.push(self.parse_atom()?);
412            loop {
413                if Token::Comma != self.token {
414                    break;
415                }
416                self.next_token()?;
417                let atom = self.parse_atom()?;
418                atoms.push(atom);
419            }
420        }
421        Ok(())
422    }
423
424    // atom ::= qualified_name `(` args `)`
425    // qualified_name ::= ident { `.` ident }
426    pub fn parse_atom(&mut self) -> Result<&'arena ast::Atom<'arena>> {
427        let mut name_buf = match &self.token {
428            Token::Ident { name } => name.clone(),
429            _ => bail!("parse_atom: expected identifer got {}", self.token),
430        };
431
432        self.next_token()?;
433
434        // Handle qualified names: ident.ident.ident(...)
435        while self.token == Token::Dot {
436            // Peek ahead: if the next token is an Ident followed by something
437            // that continues the atom (Dot or LParen), consume the dot+ident.
438            // We need to speculatively consume the Dot.
439            self.next_token()?;
440            match &self.token {
441                Token::Ident { name: next_name } => {
442                    name_buf.push('.');
443                    name_buf.push_str(next_name);
444                    self.next_token()?;
445                }
446                _ => {
447                    // The dot was actually a clause terminator or something else.
448                    // We can't put the dot back, so this is an error in the
449                    // qualified-name context. However, this path shouldn't be
450                    // reached in practice because the parser calls parse_atom
451                    // only when it knows an atom follows.
452                    bail!(
453                        "parse_atom: expected identifier after `.` in qualified name, got {}",
454                        self.token
455                    );
456                }
457            }
458        }
459
460        let name = self.arena.alloc_str(&name_buf);
461
462        self.expect(Token::LParen)?;
463        let mut args = vec![];
464        if Token::RParen != self.token {
465            self.parse_base_terms(&mut args)?;
466        }
467        self.expect(Token::RParen)?;
468        let args = alloc_slice!(self, &args);
469        Ok(alloc!(
470            self,
471            ast::Atom {
472                sym: self.arena.predicate_sym(name, None),
473                args
474            }
475        ))
476    }
477
478    fn parse_transforms(
479        &mut self,
480        transforms: &mut Vec<&'arena ast::TransformStmt<'arena>>,
481    ) -> Result<()> {
482        if Token::Do == self.token {
483            self.next_token()?;
484            let expr = self.parse_base_term()?;
485            transforms.push(alloc!(
486                self,
487                ast::TransformStmt {
488                    var: None,
489                    app: expr
490                }
491            ));
492            self.expect(Token::Semi)?;
493        }
494        loop {
495            if Token::Let != self.token {
496                break;
497            }
498            self.next_token()?;
499            if let Token::Ident { name } = &self.token {
500                let name = alloc_str!(self, name.as_str());
501                self.next_token()?;
502                self.expect(Token::Eq)?;
503                let expr = self.parse_base_term()?;
504                transforms.push(alloc!(
505                    self,
506                    ast::TransformStmt {
507                        var: Some(name),
508                        app: expr
509                    }
510                ))
511            }
512            if let Token::Dot = self.token {
513                break;
514            }
515            self.expect(Token::Semi)?;
516        }
517        Ok(())
518    }
519
520    // base_term ::= var
521    //             | fun`(`[base_term {',' base_term}`)`
522    //             | string_constant
523    //             | bytes_constant
524    //             | number_constant
525    //             | float_constant
526    //             | name_constant
527    pub fn parse_base_term(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
528        match &self.token {
529            Token::LBracket => return self.parse_list_or_map(),
530            Token::LBrace => return self.parse_struct(),
531            _ => {}
532        }
533
534        let mut is_type = false;
535        let mut base_term = match &self.token {
536            Token::Ident { name } if name == "_" => {
537                let unique = format!("_Anon{}", self.anon_counter);
538                self.anon_counter += 1;
539                ast::BaseTerm::Variable(self.arena.variable_sym(&unique))
540            }
541            Token::Ident { name } if is_variable(name) => {
542                ast::BaseTerm::Variable(self.arena.variable_sym(name))
543            }
544            Token::Ident { name } if is_fn(name) => {
545                let name = self.arena.alloc_str(name);
546                // Arguments parsed below.
547                ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
548            }
549            Token::DotIdent { name } => {
550                let name = self.arena.alloc_str(name);
551                is_type = true;
552                // Arguments parsed below.
553                ast::BaseTerm::ApplyFn(self.arena.function_sym(name, None), &[])
554            }
555            Token::String { decoded } => {
556                let value = self.arena.alloc_str(decoded.as_str());
557                ast::BaseTerm::Const(ast::Const::String(value))
558            }
559            Token::Bytes { decoded } => {
560                let value = self.arena.alloc_slice_copy(decoded);
561                ast::BaseTerm::Const(ast::Const::Bytes(value))
562            }
563            Token::Int { decoded } => ast::BaseTerm::Const(ast::Const::Number(*decoded)),
564            Token::Float { decoded } => ast::BaseTerm::Const(ast::Const::Float(*decoded)),
565            Token::Name { name } => {
566                let name = self.arena.intern(name);
567                ast::BaseTerm::Const(ast::Const::Name(name))
568            }
569            _ => bail!("parse_base_term: unexpected token {:?}", self.token),
570        };
571        self.next_token()?;
572        if let ast::BaseTerm::ApplyFn(fn_sym, _) = base_term {
573            let mut fn_args = vec![];
574            if is_type {
575                self.parse_langle_base_terms(&mut fn_args)?;
576            } else {
577                self.parse_paren_base_terms(&mut fn_args)?;
578            }
579            let fn_args = self.arena.alloc_slice_copy(&fn_args);
580            base_term = ast::BaseTerm::ApplyFn(fn_sym, fn_args);
581        }
582        let base_term = alloc!(self, base_term);
583        Ok(base_term)
584    }
585
586    fn parse_list_or_map(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
587        self.expect(Token::LBracket)?;
588        if Token::RBracket == self.token {
589            self.next_token()?;
590            return Ok(alloc!(
591                self,
592                ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), &[])
593            ));
594        }
595        let first = self.parse_base_term()?;
596        let expr = if Token::Colon != self.token {
597            self.expect(Token::Comma)?;
598            let mut items = vec![first];
599            self.parse_base_terms(&mut items)?;
600            ast::BaseTerm::ApplyFn(fn_list_sym(self.arena), alloc_slice!(self, &items))
601        } else {
602            self.expect(Token::Colon)?; // is a map
603            let first_val = self.parse_base_term()?;
604            let mut items = vec![first, first_val];
605            loop {
606                if Token::Comma != self.token {
607                    break;
608                }
609                self.next_token()?;
610                items.push(self.parse_base_term()?);
611                self.expect(Token::Colon)?;
612                items.push(self.parse_base_term()?);
613            }
614            ast::BaseTerm::ApplyFn(fn_map_sym(self.arena), alloc_slice!(self, &items))
615        };
616        self.expect(Token::RBracket)?;
617        Ok(alloc!(self, expr))
618    }
619
620    fn parse_struct(&mut self) -> Result<&'arena ast::BaseTerm<'arena>> {
621        self.expect(Token::LBrace)?;
622        if Token::RBrace == self.token {
623            self.next_token()?;
624            return Ok(alloc!(
625                self,
626                ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), &[])
627            ));
628        }
629        let mut items = vec![];
630        let name = self.parse_base_term()?;
631        if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
632            items.push(name)
633        } else {
634            bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}",);
635        }
636        self.expect(Token::Colon)?;
637        items.push(self.parse_base_term()?);
638        loop {
639            if Token::Comma != self.token {
640                break;
641            }
642            let name = self.parse_base_term()?;
643            if let ast::BaseTerm::Const(ast::Const::Name { .. }) = name {
644                items.push(name)
645            } else {
646                bail!("parse_base_term: expected name in struct expression {{ ... }} got {name:?}");
647            }
648            self.expect(Token::Colon)?;
649            items.push(self.parse_base_term()?);
650        }
651        self.expect(Token::RBrace)?;
652        Ok(alloc!(
653            self,
654            ast::BaseTerm::ApplyFn(fn_struct_sym(self.arena), alloc_slice!(self, &items))
655        ))
656    }
657
658    /// paren_langle_terms ::=  `<` [base_terms] `>`
659    fn parse_langle_base_terms(
660        &mut self,
661        base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
662    ) -> Result<()> {
663        self.expect(Token::Lt)?;
664        if Token::Gt != self.token {
665            self.parse_base_terms(base_terms)?;
666        }
667        self.expect(Token::Gt)?;
668        Ok(())
669    }
670
671    /// paren_base_terms ::=  `(` [base_terms] `)`
672    fn parse_paren_base_terms(
673        &mut self,
674        base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
675    ) -> Result<()> {
676        self.expect(Token::LParen)?;
677        if Token::RParen != self.token {
678            self.parse_base_terms(base_terms)?;
679        }
680        self.expect(Token::RParen)?;
681        Ok(())
682    }
683
684    /// base_terms ::= base_term { `,` base_term }
685    fn parse_base_terms(
686        &mut self,
687        base_terms: &mut Vec<&'arena ast::BaseTerm<'arena>>,
688    ) -> Result<()> {
689        base_terms.push(self.parse_base_term()?);
690        while let Token::Comma = self.token {
691            self.next_token()?;
692            base_terms.push(self.parse_base_term()?);
693        }
694
695        Ok(())
696    }
697}
698
699fn is_variable(name: &str) -> bool {
700    name.chars().next().unwrap().is_ascii_uppercase()
701}
702
703fn is_fn(name: &str) -> bool {
704    name.starts_with("fn:")
705}
706
707fn base_term_start(t: &Token) -> bool {
708    match t {
709        Token::Name { .. }
710        | Token::Int { .. }
711        | Token::Float { .. }
712        | Token::String { .. }
713        | Token::Bytes { .. }
714        | Token::LBracket
715        | Token::LBrace
716        | Token::DotIdent { .. } => true,
717        Token::Ident { name } => is_variable(name) || is_fn(name),
718        _ => false,
719    }
720}
721
722#[cfg(test)]
723mod test {
724
725    use super::*;
726    use googletest::prelude::{eq, gtest, verify_that};
727
728    fn make_parser<'arena>(
729        arena: &'arena Arena,
730        input: &'arena str,
731    ) -> Parser<'arena, &'arena [u8]> {
732        let mut p = Parser::new(arena, input.as_bytes(), "test");
733        p.next_token().unwrap();
734        p
735    }
736
737    #[test]
738    fn test_empty_unit() -> Result<()> {
739        let arena = Arena::new_with_global_interner();
740        let mut p = make_parser(&arena, "");
741        match p.parse_unit()? {
742            &ast::Unit { decls: &[pkg], .. } => {
743                assert_eq!(pkg, &empty_package_decl(&arena));
744            }
745            z => panic!("unexpected: {:?}", z),
746        }
747        Ok(())
748    }
749
750    #[test]
751    fn test_package_use() -> Result<()> {
752        let arena = Arena::new_with_global_interner();
753        let input = "Package foo[bar()]! Use baz[bar()]!";
754
755        let mut p = make_parser(&arena, input);
756        let unit = p.parse_unit()?;
757        match unit.decls {
758            &[
759                &ast::Decl {
760                    atom:
761                        &ast::Atom {
762                            sym: got_package_sym,
763                            ..
764                        },
765                    descr:
766                        &[
767                            &ast::Atom {
768                                sym: got_name_sym1,
769                                args: &[ast::BaseTerm::Const(ast::Const::String("foo"))],
770                            },
771                            &ast::Atom {
772                                sym: got_bar_sym1,
773                                args: &[],
774                            },
775                        ],
776                    ..
777                },
778                &ast::Decl {
779                    atom:
780                        &ast::Atom {
781                            sym: got_use_sym, ..
782                        },
783                    descr:
784                        &[
785                            &ast::Atom {
786                                sym: got_name_sym2,
787                                args: &[ast::BaseTerm::Const(ast::Const::String("baz"))],
788                            },
789                            &ast::Atom {
790                                sym: got_bar_sym2,
791                                args: &[],
792                            },
793                        ],
794                    ..
795                },
796            ] => {
797                assert_eq!(got_use_sym, use_sym(&arena));
798                assert_eq!(got_package_sym, package_sym(&arena));
799                assert_eq!(got_name_sym1, name_sym(&arena));
800                assert_eq!(got_name_sym2, name_sym(&arena));
801                assert_eq!(got_bar_sym1, arena.predicate_sym("bar", None));
802                assert_eq!(got_bar_sym2, arena.predicate_sym("bar", None));
803            }
804            z => panic!("unexpected {z:?}"),
805        }
806        Ok(())
807    }
808
809    #[test]
810    fn test_decl() -> Result<()> {
811        let arena = Arena::new_with_global_interner();
812        let input = "Decl foo(X, Y).";
813        let mut p = make_parser(&arena, input);
814        match p.parse_decl()? {
815            &ast::Decl {
816                atom:
817                    &ast::Atom {
818                        sym: got_foo_sym,
819                        args:
820                            &[
821                                &ast::BaseTerm::Variable(x_sym),
822                                &ast::BaseTerm::Variable(y_sym),
823                            ],
824                    },
825                ..
826            } => {
827                assert_eq!(got_foo_sym, arena.predicate_sym("foo", None));
828                assert_eq!(x_sym, arena.variable_sym("X"));
829                assert_eq!(y_sym, arena.variable_sym("Y"))
830            }
831            decl => panic!("got {:?}", decl),
832        };
833        Ok(())
834    }
835
836    #[test]
837    fn test_base_term() -> googletest::Result<()> {
838        let arena = Arena::new_with_global_interner();
839        let input = "X 3 1.5 'foo' /foo fn:list() fn:list(/a) fn:list(/a, 3)"; //.as_bytes();
840        let mut p = make_parser(&arena, input);
841        let mut got_base_terms = vec![];
842        loop {
843            if Token::Eof == p.token {
844                break;
845            }
846            // TODO: "err_to_test_failure".
847            let base_term = p.parse_base_term().unwrap();
848            got_base_terms.push(base_term);
849        }
850        let expected = vec![
851            arena.variable("X"),
852            arena.const_(ast::Const::Number(3)),
853            arena.const_(ast::Const::Float(1.5)),
854            arena.const_(ast::Const::String("foo")),
855            arena.const_(arena.name("/foo")),
856            arena.apply_fn(fn_list_sym(&arena), &[]),
857            arena.apply_fn(fn_list_sym(&arena), &[arena.const_(arena.name("/a"))]),
858            arena.apply_fn(
859                fn_list_sym(&arena),
860                &[
861                    arena.const_(arena.name("/a")),
862                    arena.const_(ast::Const::Number(3)),
863                ],
864            ),
865        ];
866        verify_that!(got_base_terms, eq(&expected))
867    }
868
869    #[test]
870    fn test_term() -> googletest::Result<()> {
871        let arena = Arena::new_with_global_interner();
872        let input = "foo(/bar) !bar() X = Z X != 3 3 < 1 3 <= 1";
873        let mut p = make_parser(&arena, input);
874        let mut got_terms = vec![];
875        loop {
876            if Token::Eof == p.token {
877                break;
878            }
879            // TODO: "err_to_test_failure".
880            got_terms.push(p.parse_term().unwrap());
881        }
882        let expected = [
883            &ast::Term::Atom(arena.atom(
884                arena.predicate_sym("foo", None),
885                &[arena.const_(arena.name("/bar"))],
886            )),
887            &ast::Term::NegAtom(arena.atom(arena.predicate_sym("bar", None), &[])),
888            &ast::Term::Eq(arena.variable("X"), arena.variable("Z")),
889            &ast::Term::Ineq(
890                arena.variable("X"),
891                arena.alloc(ast::BaseTerm::Const(ast::Const::Number(3))),
892            ),
893            &ast::Term::Atom(arena.atom(
894                arena.predicate_sym(":lt", Some(2)),
895                &[
896                    arena.const_(ast::Const::Number(3)),
897                    arena.const_(ast::Const::Number(1)),
898                ],
899            )),
900            &ast::Term::Atom(arena.atom(
901                arena.predicate_sym(":le", Some(2)),
902                &[
903                    arena.const_(ast::Const::Number(3)),
904                    arena.const_(ast::Const::Number(1)),
905                ],
906            )),
907        ];
908        verify_that!(got_terms, eq(&expected))
909    }
910
911    #[gtest]
912    fn test_structured_data_and_types() -> googletest::Result<()> {
913        let arena = Arena::new_with_global_interner();
914        let input =
915            "[] [1,2,3] [1: 'one', 2: 'two'] {} {/foo: /bar} .List<.Option</name>, /string>";
916        let mut p = make_parser(&arena, input);
917        let mut got_base_terms = vec![];
918        loop {
919            if Token::Eof == p.token {
920                break;
921            }
922            // TODO: "err_to_test_failure".
923            let base_term = p.parse_base_term().unwrap();
924            got_base_terms.push(base_term);
925        }
926        let expected = vec![
927            arena.apply_fn(fn_list_sym(&arena), &[]),
928            arena.apply_fn(
929                fn_list_sym(&arena),
930                &[
931                    arena.const_(ast::Const::Number(1)),
932                    arena.const_(ast::Const::Number(2)),
933                    arena.const_(ast::Const::Number(3)),
934                ],
935            ),
936            arena.apply_fn(
937                fn_map_sym(&arena),
938                &[
939                    arena.const_(ast::Const::Number(1)),
940                    arena.const_(ast::Const::String("one")),
941                    arena.const_(ast::Const::Number(2)),
942                    arena.const_(ast::Const::String("two")),
943                ],
944            ),
945            arena.apply_fn(fn_struct_sym(&arena), &[]),
946            arena.apply_fn(
947                fn_struct_sym(&arena),
948                &[
949                    arena.const_(arena.name("/foo")),
950                    arena.const_(arena.name("/bar")),
951                ],
952            ),
953            arena.apply_fn(
954                fn_list_type_sym(&arena),
955                &[
956                    arena.apply_fn(
957                        fn_option_type_sym(&arena),
958                        &[arena.const_(arena.name("/name"))],
959                    ),
960                    arena.const_(arena.name("/string")),
961                ],
962            ),
963        ];
964        verify_that!(got_base_terms, eq(&expected))
965    }
966
967    #[test]
968    fn test_clause() -> Result<()> {
969        let arena = Arena::new_with_global_interner();
970        let mut p = make_parser(&arena, "foo(X).");
971        let clause = p.parse_clause()?;
972        match clause {
973            &ast::Clause {
974                head:
975                    &ast::Atom {
976                        args: &[ast::BaseTerm::Variable(x_sym)],
977                        ..
978                    },
979                premises: &[],
980                transform: &[],
981            } => {
982                assert_eq!(*x_sym, arena.variable_sym("X"));
983                assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
984            }
985            _ => panic!("unexpected: {:?}", clause),
986        }
987        let mut p = make_parser(&arena, "foo(X) :- !bar(X).");
988        let clause = p.parse_clause()?;
989        match clause {
990            &ast::Clause {
991                head:
992                    &ast::Atom {
993                        sym: foo_sym,
994                        args: _,
995                    },
996                premises:
997                    &[
998                        &ast::Term::NegAtom(&ast::Atom {
999                            sym: bar_sym,
1000                            args: _,
1001                        }),
1002                    ],
1003                transform: &[],
1004            } => {
1005                assert_eq!(foo_sym, arena.predicate_sym("foo", None));
1006                assert_eq!(bar_sym, arena.predicate_sym("bar", None));
1007            }
1008            _ => panic!("unexpected: {:?}", clause),
1009        };
1010        let mut p = make_parser(
1011            &arena,
1012            "foo(Z) ⟸ bar(Y) |> do fn:group_by(); let X = fn:count(Y).",
1013        );
1014
1015        let clause = p.parse_clause()?;
1016        match clause {
1017            &ast::Clause {
1018                head: &ast::Atom { .. },
1019                premises: &[&ast::Term::Atom(ast::Atom { .. })],
1020                transform:
1021                    &[
1022                        &ast::TransformStmt {
1023                            var: None,
1024                            app: ast::BaseTerm::ApplyFn(first_sym, _),
1025                        },
1026                        &ast::TransformStmt {
1027                            var: Some("X"),
1028                            app: ast::BaseTerm::ApplyFn(second_sym, _),
1029                        },
1030                    ],
1031            } => {
1032                assert_eq!(clause.head.sym, arena.predicate_sym("foo", None));
1033                assert_eq!(clause.transform.len(), 2);
1034                assert_eq!(*first_sym, arena.function_sym("fn:group_by", None));
1035                assert_eq!(*second_sym, arena.function_sym("fn:count", None));
1036            }
1037            _ => panic!("unexpected: {:?}", clause),
1038        }
1039
1040        Ok(())
1041    }
1042
1043    #[test]
1044    fn test_anonymous_variable_single() -> Result<()> {
1045        let arena = Arena::new_with_global_interner();
1046        let mut p = make_parser(&arena, "foo(_, X).");
1047        let clause = p.parse_clause()?;
1048        // The `_` should parse as a variable with a generated name `_Anon0`
1049        match clause.head.args {
1050            &[&ast::BaseTerm::Variable(anon), &ast::BaseTerm::Variable(x)] => {
1051                assert_eq!(anon, arena.variable_sym("_Anon0"));
1052                assert_eq!(x, arena.variable_sym("X"));
1053            }
1054            _ => panic!("unexpected args: {:?}", clause.head.args),
1055        }
1056        Ok(())
1057    }
1058
1059    #[test]
1060    fn test_anonymous_variable_multiple_distinct() -> Result<()> {
1061        let arena = Arena::new_with_global_interner();
1062        let mut p = make_parser(&arena, "foo(_, _, _).");
1063        let clause = p.parse_clause()?;
1064        // Each `_` should produce a distinct variable name
1065        match clause.head.args {
1066            &[
1067                &ast::BaseTerm::Variable(a0),
1068                &ast::BaseTerm::Variable(a1),
1069                &ast::BaseTerm::Variable(a2),
1070            ] => {
1071                assert_eq!(a0, arena.variable_sym("_Anon0"));
1072                assert_eq!(a1, arena.variable_sym("_Anon1"));
1073                assert_eq!(a2, arena.variable_sym("_Anon2"));
1074                // All three must be distinct
1075                assert_ne!(a0, a1);
1076                assert_ne!(a1, a2);
1077            }
1078            _ => panic!("unexpected args: {:?}", clause.head.args),
1079        }
1080        Ok(())
1081    }
1082
1083    #[test]
1084    fn test_anonymous_variable_in_rule_body() -> Result<()> {
1085        let arena = Arena::new_with_global_interner();
1086        let mut p = make_parser(&arena, "result(X) :- foo(X, _).");
1087        let clause = p.parse_clause()?;
1088        assert_eq!(clause.head.sym, arena.predicate_sym("result", None));
1089        match clause.premises {
1090            &[&ast::Term::Atom(&ast::Atom { args, .. })] => match args {
1091                &[&ast::BaseTerm::Variable(x), &ast::BaseTerm::Variable(anon)] => {
1092                    assert_eq!(x, arena.variable_sym("X"));
1093                    assert_eq!(anon, arena.variable_sym("_Anon0"));
1094                }
1095                _ => panic!("unexpected args: {:?}", args),
1096            },
1097            _ => panic!("unexpected premises: {:?}", clause.premises),
1098        }
1099        Ok(())
1100    }
1101
1102    #[test]
1103    fn test_anonymous_variable_with_negation() -> Result<()> {
1104        let arena = Arena::new_with_global_interner();
1105        let mut p = make_parser(&arena, "orphan(X) :- node(X, _), !has_parent(X).");
1106        let clause = p.parse_clause()?;
1107        assert_eq!(clause.head.sym, arena.predicate_sym("orphan", None));
1108        assert_eq!(clause.premises.len(), 2);
1109        // First premise: node(X, _)
1110        match clause.premises[0] {
1111            &ast::Term::Atom(&ast::Atom { args, .. }) => match args {
1112                &[&ast::BaseTerm::Variable(_), &ast::BaseTerm::Variable(anon)] => {
1113                    assert_eq!(anon, arena.variable_sym("_Anon0"));
1114                }
1115                _ => panic!("unexpected args: {:?}", args),
1116            },
1117            _ => panic!("expected Atom, got {:?}", clause.premises[0]),
1118        }
1119        // Second premise: !has_parent(X)
1120        match clause.premises[1] {
1121            &ast::Term::NegAtom(&ast::Atom { sym, .. }) => {
1122                assert_eq!(sym, arena.predicate_sym("has_parent", None));
1123            }
1124            _ => panic!("expected NegAtom, got {:?}", clause.premises[1]),
1125        }
1126        Ok(())
1127    }
1128}