polar_core/
parser.rs

1use std::sync::Arc;
2
3use lalrpop_util::{lalrpop_mod, ParseError};
4
5use super::{
6    error::{self, PolarResult},
7    lexer::{self, Lexer, Token},
8    resource_block::Production,
9    rules::*,
10    sources::Source,
11    terms::*,
12};
13
14/// Used to denote whether an enclosed value is a value or a logical operator
15pub enum ValueOrLogical {
16    Value(Term),
17    Logical(Term),
18    Either(Term),
19}
20
21lalrpop_mod!(
22    #[allow(clippy::all, dead_code, unused_imports, unused_mut)]
23    polar
24);
25
26#[derive(Clone, Debug, PartialEq)]
27pub enum Line {
28    Rule(Rule),
29    RuleType(Rule),
30    Query(Term),
31    ResourceBlock {
32        keyword: Option<Term>,
33        resource: Term,
34        productions: Vec<Production>,
35    },
36}
37
38fn lalrpop_error_to_polar_error(
39    e: ParseError<usize, lexer::Token, error::ParseErrorKind>,
40    source: Arc<Source>,
41) -> error::PolarError {
42    let kind = match e {
43        ParseError::InvalidToken { location: loc } => error::ParseErrorKind::InvalidToken { loc },
44        ParseError::UnrecognizedEOF { location: loc, .. } => {
45            error::ParseErrorKind::UnrecognizedEOF { loc }
46        }
47        ParseError::UnrecognizedToken {
48            token: (loc, t, _), ..
49        } => match t {
50            Token::Debug | Token::Cut | Token::In | Token::New => {
51                error::ParseErrorKind::ReservedWord {
52                    token: t.to_string(),
53                    loc,
54                }
55            }
56            _ => error::ParseErrorKind::UnrecognizedToken {
57                token: t.to_string(),
58                loc,
59            },
60        },
61        ParseError::ExtraToken { token: (loc, t, _) } => error::ParseErrorKind::ExtraToken {
62            token: t.to_string(),
63            loc,
64        },
65        ParseError::User { error } => error,
66    };
67    error::ParseError { source, kind }.into()
68}
69
70pub fn parse_lines(source: Source) -> PolarResult<Vec<Line>> {
71    let source = Arc::new(source);
72    polar::LinesParser::new()
73        .parse(&source, Lexer::new(&source.src))
74        .map_err(|e| lalrpop_error_to_polar_error(e, source))
75}
76
77pub fn parse_query(query: &str) -> PolarResult<Term> {
78    let source = Arc::new(Source::new(query));
79    polar::TermParser::new()
80        .parse(&source, Lexer::new(query))
81        .map_err(|e| lalrpop_error_to_polar_error(e, source))
82}
83
84#[cfg(test)]
85pub fn parse_rules(rules: &str) -> PolarResult<Vec<Rule>> {
86    let source = Arc::new(Source::new(rules));
87    polar::RulesParser::new()
88        .parse(&source, Lexer::new(rules))
89        .map_err(|e| lalrpop_error_to_polar_error(e, source))
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::error::ParseErrorKind::*;
96    use pretty_assertions::assert_eq;
97
98    #[track_caller]
99    fn parse_term(src: &str) -> Term {
100        super::parse_query(src).unwrap()
101    }
102
103    #[track_caller]
104    fn parse_term_error(src: &str) -> error::ParseErrorKind {
105        super::parse_query(src).unwrap_err().unwrap_parse()
106    }
107
108    #[track_caller]
109    fn parse_rule(src: &str) -> Rule {
110        super::parse_rules(src).unwrap().pop().unwrap()
111    }
112
113    #[track_caller]
114    fn parse_lines(src: &str) -> Vec<Line> {
115        super::parse_lines(Source::new(src)).unwrap()
116    }
117
118    #[test]
119    fn test_dot_lookups() {
120        let exp = parse_term("a.b");
121        assert_eq!(exp, term!(op!(Dot, term!(sym!("a")), term!("b"))));
122    }
123
124    #[test]
125    fn try_it_with_macros() {
126        let int = parse_term(" 123");
127        assert_eq!(int, term!(123));
128        assert_eq!(int.parsed_context().map(|context| context.left), Some(1));
129        let s = parse_term(r#""string literal""#);
130        assert_eq!(s, term!("string literal"));
131
132        let t = parse_term("true");
133        assert_eq!(t, term!(true));
134
135        let sym = parse_term("foo_qwe");
136        assert_eq!(sym, term!(sym!("foo_qwe")));
137
138        let l = parse_term("[foo, bar, baz]");
139        assert_eq!(l, term!([sym!("foo"), sym!("bar"), sym!("baz")]));
140
141        super::parse_rules(r#"bar(a, c) if foo(a, b(c), "d")"#).expect_err("parse error");
142
143        let exp2 = parse_term("foo.a(b)");
144        assert_eq!(
145            exp2,
146            term!(op!(Dot, term!(sym!("foo")), term!(call!("a", [sym!("b")])))),
147            "{}",
148            exp2
149        );
150        let rule = parse_rule("f(x) if g(x);");
151        assert_eq!(rule, rule!("f", [sym!("x")] => call!("g", [sym!("x")])));
152        let rule = parse_rule("f(x);");
153        assert_eq!(rule, rule!("f", [sym!("x")]));
154    }
155
156    #[test]
157    fn parse_booleans() {
158        assert_eq!(parse_term("true"), term!(true));
159        assert_eq!(parse_term("false"), term!(false));
160    }
161
162    #[test]
163    fn parse_integers() {
164        assert_eq!(parse_term("123"), term!(123));
165        assert_eq!(parse_term("0"), term!(0));
166        assert_eq!(parse_term("+123"), term!(123));
167        assert_eq!(parse_term("-123"), term!(-123));
168    }
169
170    #[test]
171    fn parse_floats() {
172        assert_eq!(parse_term("0.123"), term!(0.123));
173        assert_eq!(parse_term("1.234"), term!(1.234));
174        assert_eq!(parse_term("+1.234"), term!(1.234));
175        assert_eq!(parse_term("-1.234"), term!(-1.234));
176        assert_eq!(parse_term("-1.234e-56"), term!(-1.234e-56));
177        assert_eq!(parse_term("-1.234e56"), term!(-1.234e56));
178        assert_eq!(parse_term("inf"), term!(f64::INFINITY));
179        assert_eq!(parse_term("-inf"), term!(f64::NEG_INFINITY));
180        assert!(
181            matches!(parse_term("nan").value(), Value::Number(crate::numerics::Numeric::Float(f)) if f.is_nan())
182        );
183    }
184
185    #[test]
186    fn test_parse_specializers() {
187        let rule = parse_rule("f(x: 1);");
188        assert_eq!(rule, rule!("f", ["x"; 1]));
189
190        let rule = parse_rule("f(x: 1, y: [x]) if y = 2;");
191        assert_eq!(
192            rule,
193            rule!("f", ["x" ; 1 , "y" ; value!([sym!("x")])] => op!(Unify, term!(sym!("y")), term!(2)))
194        );
195
196        // parse specializer as a type
197        let rule = parse_rule("f(x: y);");
198        assert_eq!(rule, rule!("f", ["x"; value!(instance!("y"))]));
199    }
200
201    #[test]
202    fn test_parse_file() {
203        let f = "a(1);b(2);c(3);";
204        let results = super::parse_rules(f).unwrap();
205        assert_eq!(results[0].to_string(), "a(1);");
206        assert_eq!(results[1].to_string(), "b(2);");
207        assert_eq!(results[2].to_string(), "c(3);");
208    }
209
210    #[test]
211    fn test_parse_line() {
212        let kb = "f(x) if x = 1;";
213        let line = parse_lines(kb);
214        assert_eq!(
215            line[0],
216            Line::Rule(rule!("f", [sym!("x")] => op!(Unify, term!(sym!("x")), term!(1))))
217        );
218        let f = "?= f(1);";
219        let line = parse_lines(f);
220
221        assert_eq!(line[0], Line::Query(term!(call!("f", [1]))));
222
223        let rule_type = "type f(x: String);";
224        let line = parse_lines(rule_type);
225        assert_eq!(
226            line[0],
227            Line::RuleType(rule!("f", ["x"; value!(instance!("String"))]))
228        );
229    }
230
231    #[test]
232    fn test_rule_type_error() {
233        let rule_type = r#"type f(x: String) if x = "bad";"#;
234        super::parse_lines(Source::new(rule_type)).unwrap_err();
235    }
236
237    #[test]
238    fn test_parse_new() {
239        let f = "a(x) if x = new Foo(a: 1);";
240        let results = super::parse_rules(f).unwrap();
241        assert_eq!(results[0].to_string(), "a(x) if x = new Foo(a: 1);");
242    }
243
244    #[test]
245    fn test_parse_new_boa_constructor() {
246        let f = "a(x) if x = new Foo(1, 2);";
247        let results = super::parse_rules(f).unwrap();
248        assert_eq!(results[0].to_string(), "a(x) if x = new Foo(1, 2);");
249
250        // test trailing comma
251        let f = "a(x) if x = new Foo(1,);";
252        super::parse_rules(f).expect_err("parse error");
253    }
254
255    #[test]
256    fn test_parse_new_mixed_args() {
257        let f = "a(x) if x = new Foo(1, 2, bar: 3, baz:4);";
258        let results = super::parse_rules(f).unwrap();
259        assert_eq!(
260            results[0].to_string(),
261            "a(x) if x = new Foo(1, 2, bar: 3, baz: 4);"
262        );
263        let f = "a(x) if x = new Foo(bar: 3, baz: 4);";
264        let results = super::parse_rules(f).unwrap();
265        assert_eq!(
266            results[0].to_string(),
267            "a(x) if x = new Foo(bar: 3, baz: 4);"
268        );
269
270        let f = "a(x) if x = new Foo(bar: 3, baz: 4, 1, 2);";
271        super::parse_rules(f).expect_err("parse error");
272
273        // Don't allow kwargs in calls or dot ops.
274        let f = "a(x) if f(x: 1)";
275        super::parse_rules(f).expect_err("parse error");
276        let f = "a(x) if x.f(x: 1)";
277        super::parse_rules(f).expect_err("parse error");
278    }
279
280    #[test]
281    fn test_parse_matches() {
282        let term = parse_term("{} matches {}");
283        assert_eq!(term.to_string(), "{} matches {}");
284        let term = parse_term("{x: 1} matches {}");
285        assert_eq!(term.to_string(), "{x: 1} matches {}");
286    }
287
288    #[test]
289    fn test_parse_rest_vars() {
290        let q = "[1, 2, *x] = [*rest]";
291        assert_eq!(parse_term(q).to_string(), q);
292
293        let e = parse_term_error("[1, 2, 3] = [*rest, 3]");
294        assert!(matches!(e, UnrecognizedToken { .. }));
295
296        let e = parse_term_error("[1, 2, *3] = [*rest]");
297        assert!(matches!(e, UnrecognizedToken { .. }));
298
299        let e = parse_term_error("[1, *x, *y] = [*rest]");
300        assert!(matches!(e, UnrecognizedToken { .. }));
301
302        let q = "[1, 2, 3] matches [1, 2, 3]";
303        assert_eq!(parse_term(q).to_string(), q, "{} -- {}", q, parse_term(q));
304
305        let q = "[1, 2, 3] matches [1, *rest]";
306        assert_eq!(parse_term(q).to_string(), q, "{} -- {}", q, parse_term(q));
307    }
308
309    #[test]
310    fn test_primitive_methods() {
311        let q = r#""abc".startswith("a")"#;
312        assert_eq!(
313            parse_term(q),
314            term!(op!(Dot, term!("abc"), term!(call!("startswith", ["a"])))),
315        );
316
317        let q = r#"x.("invalid-key")"#;
318        assert_eq!(
319            parse_term(q),
320            term!(op!(Dot, term!(sym!("x")), term!("invalid-key"))),
321        );
322    }
323
324    #[test]
325    fn test_catching_wrong_types() {
326        for bad_query in &[
327            "f(x=1)",
328            "x in [1, 2] < 2",
329            "{x: 1 < 2}",
330            "{x: 1 < 2}",
331            "not 1",
332            "1 and 2",
333            "1 + print(\"x\")",
334            "forall([1, 2, 3], x < 1)",
335            "x = (1 or 2)",
336            "x = (1 = 2)",
337            "foo.bar(x or y)",
338            "foo.bar(z: x or y)",
339            "x = y = z",
340            "x = y = 1",
341            "x = 1 = z",
342            "1 = y = z",
343            "x = (1 and 2)",
344            "(1 or 2) = x",
345            "x = (not x)",
346            "y matches z = x",
347        ] {
348            assert!(matches!(parse_term_error(bad_query), WrongValueType { .. }));
349        }
350    }
351
352    #[test]
353    fn trailing_commas() {
354        let q = "{a: 1,}";
355        let dict = term!(btreemap! { sym!("a") => term!(1)});
356        assert_eq!(parse_term(q), dict);
357
358        let q = "[1, 2,]";
359        let list = term!([1, 2]);
360        assert_eq!(parse_term(q), list);
361
362        assert_eq!(
363            parse_term("{a: 1,} = [1, 2,]"),
364            term!(op!(Unify, dict, list))
365        );
366    }
367
368    #[test]
369    fn duplicate_keys() {
370        let q = "{a: 1, a: 2}";
371        assert!(matches!(parse_term_error(q), DuplicateKey { .. }));
372    }
373}