From b852ee0a884bceac47c60f3d5053464d32988eeb Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Fri, 29 Nov 2024 11:17:12 +0100 Subject: [PATCH 1/3] disambiguate empty sets from empty maps according to the grammar, `{}` could either mean *empty set* or *empty map*. Due to how the parser was written, it was always an empty set, and there was no way to have an empty map literal. This is an issue because empty maps would still be displayed as `{}`, and maps have a different api than sets, resulting in evaluation errors. This commit introduces `{,}` as the empty set literal, while `{}` is the empty map. The goal here is to be consistent with JSON, the reason why we chose the current syntax for arrays and maps. --- biscuit-auth/examples/testcases.rs | 3 +++ biscuit-auth/samples/README.md | 4 ++- biscuit-auth/samples/samples.json | 5 ++-- biscuit-auth/samples/test017_expressions.bc | Bin 1687 -> 1721 bytes biscuit-auth/src/datalog/symbol.rs | 14 +++++++---- biscuit-auth/src/token/builder.rs | 4 +++ biscuit-auth/src/token/builder/term.rs | 8 ++++-- biscuit-parser/src/parser.rs | 26 ++++++++++++++++++-- 8 files changed, 52 insertions(+), 12 deletions(-) diff --git a/biscuit-auth/examples/testcases.rs b/biscuit-auth/examples/testcases.rs index 482dbb2a..bf5fe1b3 100644 --- a/biscuit-auth/examples/testcases.rs +++ b/biscuit-auth/examples/testcases.rs @@ -1378,6 +1378,9 @@ fn expressions(target: &str, root: &KeyPair, test: bool) -> TestResult { check if {1, 2, 3}.intersection({1, 2}).contains(1); // chained method calls with unary method check if {1, 2, 3}.intersection({1, 2}).length() === 2; + + // empty set literal + check if {,}.length() === 0; "#) .build_with_rng(&root, SymbolTable::default(), &mut rng) .unwrap(); diff --git a/biscuit-auth/samples/README.md b/biscuit-auth/samples/README.md index 0d378295..4a7ad09e 100644 --- a/biscuit-auth/samples/README.md +++ b/biscuit-auth/samples/README.md @@ -1321,6 +1321,7 @@ check if {1, 2}.intersection({2, 3}) === {2}; check if {1, 2}.union({2, 3}) === {1, 2, 3}; check if {1, 2, 3}.intersection({1, 2}).contains(1); check if {1, 2, 3}.intersection({1, 2}).length() === 2; +check if {,}.length() === 0; ``` ### validation @@ -1331,7 +1332,7 @@ allow if true; ``` revocation ids: -- `d0420227266e3583a42dfaa0e38550d99f681d150dd18856f3af9a697bc9c5c8bf06b4b0fe5b9df0377d1b963574e2fd210a0a76a8b0756a65f640c602bebd07` +- `fa358e4e3bea896415b1859e6cd347e64e1918fb86e31ae3fe208628321576a47f7a269760357e291c827ec9cbe322074f6860a546207a64e133c83a214bb505` authorizer world: ``` @@ -1372,6 +1373,7 @@ World { "check if true", "check if true === true", "check if {\"abc\", \"def\"}.contains(\"abc\")", + "check if {,}.length() === 0", "check if {1, 2, 3}.intersection({1, 2}).contains(1)", "check if {1, 2, 3}.intersection({1, 2}).length() === 2", "check if {1, 2} === {1, 2}", diff --git a/biscuit-auth/samples/samples.json b/biscuit-auth/samples/samples.json index 539d5e18..6a713179 100644 --- a/biscuit-auth/samples/samples.json +++ b/biscuit-auth/samples/samples.json @@ -1276,7 +1276,7 @@ ], "public_keys": [], "external_key": null, - "code": "check if true;\ncheck if !false;\ncheck if true === true;\ncheck if false === false;\ncheck if 1 < 2;\ncheck if 2 > 1;\ncheck if 1 <= 2;\ncheck if 1 <= 1;\ncheck if 2 >= 1;\ncheck if 2 >= 2;\ncheck if 3 === 3;\ncheck if 1 + 2 * 3 - 4 / 2 === 5;\ncheck if \"hello world\".starts_with(\"hello\"), \"hello world\".ends_with(\"world\");\ncheck if \"aaabde\".matches(\"a*c?.e\");\ncheck if \"aaabde\".contains(\"abd\");\ncheck if \"aaabde\" === \"aaa\" + \"b\" + \"de\";\ncheck if \"abcD12\" === \"abcD12\";\ncheck if \"abcD12\".length() === 6;\ncheck if \"é\".length() === 2;\ncheck if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z;\ncheck if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z === 2020-12-04T09:46:41Z;\ncheck if hex:12ab === hex:12ab;\ncheck if {1, 2}.contains(2);\ncheck if {2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z}.contains(2020-12-04T09:46:41Z);\ncheck if {false, true}.contains(true);\ncheck if {\"abc\", \"def\"}.contains(\"abc\");\ncheck if {hex:12ab, hex:34de}.contains(hex:34de);\ncheck if {1, 2}.contains({2});\ncheck if {1, 2} === {1, 2};\ncheck if {1, 2}.intersection({2, 3}) === {2};\ncheck if {1, 2}.union({2, 3}) === {1, 2, 3};\ncheck if {1, 2, 3}.intersection({1, 2}).contains(1);\ncheck if {1, 2, 3}.intersection({1, 2}).length() === 2;\n", + "code": "check if true;\ncheck if !false;\ncheck if true === true;\ncheck if false === false;\ncheck if 1 < 2;\ncheck if 2 > 1;\ncheck if 1 <= 2;\ncheck if 1 <= 1;\ncheck if 2 >= 1;\ncheck if 2 >= 2;\ncheck if 3 === 3;\ncheck if 1 + 2 * 3 - 4 / 2 === 5;\ncheck if \"hello world\".starts_with(\"hello\"), \"hello world\".ends_with(\"world\");\ncheck if \"aaabde\".matches(\"a*c?.e\");\ncheck if \"aaabde\".contains(\"abd\");\ncheck if \"aaabde\" === \"aaa\" + \"b\" + \"de\";\ncheck if \"abcD12\" === \"abcD12\";\ncheck if \"abcD12\".length() === 6;\ncheck if \"é\".length() === 2;\ncheck if 2019-12-04T09:46:41Z < 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z > 2019-12-04T09:46:41Z;\ncheck if 2019-12-04T09:46:41Z <= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2019-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z >= 2020-12-04T09:46:41Z;\ncheck if 2020-12-04T09:46:41Z === 2020-12-04T09:46:41Z;\ncheck if hex:12ab === hex:12ab;\ncheck if {1, 2}.contains(2);\ncheck if {2019-12-04T09:46:41Z, 2020-12-04T09:46:41Z}.contains(2020-12-04T09:46:41Z);\ncheck if {false, true}.contains(true);\ncheck if {\"abc\", \"def\"}.contains(\"abc\");\ncheck if {hex:12ab, hex:34de}.contains(hex:34de);\ncheck if {1, 2}.contains({2});\ncheck if {1, 2} === {1, 2};\ncheck if {1, 2}.intersection({2, 3}) === {2};\ncheck if {1, 2}.union({2, 3}) === {1, 2, 3};\ncheck if {1, 2, 3}.intersection({1, 2}).contains(1);\ncheck if {1, 2, 3}.intersection({1, 2}).length() === 2;\ncheck if {,}.length() === 0;\n", "version": 3 } ], @@ -1317,6 +1317,7 @@ "check if true", "check if true === true", "check if {\"abc\", \"def\"}.contains(\"abc\")", + "check if {,}.length() === 0", "check if {1, 2, 3}.intersection({1, 2}).contains(1)", "check if {1, 2, 3}.intersection({1, 2}).length() === 2", "check if {1, 2} === {1, 2}", @@ -1339,7 +1340,7 @@ }, "authorizer_code": "allow if true;\n", "revocation_ids": [ - "d0420227266e3583a42dfaa0e38550d99f681d150dd18856f3af9a697bc9c5c8bf06b4b0fe5b9df0377d1b963574e2fd210a0a76a8b0756a65f640c602bebd07" + "fa358e4e3bea896415b1859e6cd347e64e1918fb86e31ae3fe208628321576a47f7a269760357e291c827ec9cbe322074f6860a546207a64e133c83a214bb505" ] } } diff --git a/biscuit-auth/samples/test017_expressions.bc b/biscuit-auth/samples/test017_expressions.bc index 7be7a4fda92f3feecff08e8c3a68d1c53bea2100..5b28d3609afbfeade00001e1228898200c837181 100644 GIT binary patch delta 116 zcmbQvyOWnyXc8~i@{O!)Y%)d)Tyk7Y9MVz}Tr6BnRt#J$LQEV?AhrO*WL7pkhhL_B ze%7x#Q$#nm&da&%{>)EO;&>5P}!eo9Tg4Hh*tBj tnrVB<#mK(~w6OkLo$xn(8Ty$6$(1|$ZpCIA2c diff --git a/biscuit-auth/src/datalog/symbol.rs b/biscuit-auth/src/datalog/symbol.rs index 996804a8..01c7af7c 100644 --- a/biscuit-auth/src/datalog/symbol.rs +++ b/biscuit-auth/src/datalog/symbol.rs @@ -198,11 +198,15 @@ impl SymbolTable { } } Term::Set(s) => { - let terms = s - .iter() - .map(|term| self.print_term(term)) - .collect::>(); - format!("{{{}}}", terms.join(", ")) + if s.is_empty() { + "{,}".to_string() + } else { + let terms = s + .iter() + .map(|term| self.print_term(term)) + .collect::>(); + format!("{{{}}}", terms.join(", ")) + } } Term::Null => "null".to_string(), Term::Array(a) => { diff --git a/biscuit-auth/src/token/builder.rs b/biscuit-auth/src/token/builder.rs index aa80194a..2f33ea63 100644 --- a/biscuit-auth/src/token/builder.rs +++ b/biscuit-auth/src/token/builder.rs @@ -333,4 +333,8 @@ check if true trusting ed25519/6e9e6d5a75cf0c0e87ec1256b4dfed0ca3ba452912d213fcc }) ); } + #[test] + fn empty_set_display() { + assert_eq!(Term::Set(BTreeSet::new()).to_string(), "{,}"); + } } diff --git a/biscuit-auth/src/token/builder/term.rs b/biscuit-auth/src/token/builder/term.rs index e1e6e4e7..a8459e4c 100644 --- a/biscuit-auth/src/token/builder/term.rs +++ b/biscuit-auth/src/token/builder/term.rs @@ -359,8 +359,12 @@ impl fmt::Display for Term { } } Term::Set(s) => { - let terms = s.iter().map(|term| term.to_string()).collect::>(); - write!(f, "{{{}}}", terms.join(", ")) + if s.is_empty() { + write!(f, "{{,}}") + } else { + let terms = s.iter().map(|term| term.to_string()).collect::>(); + write!(f, "{{{}}}", terms.join(", ")) + } } Term::Parameter(s) => { write!(f, "{{{}}}", s) diff --git a/biscuit-parser/src/parser.rs b/biscuit-parser/src/parser.rs index 649c65c2..4daeee68 100644 --- a/biscuit-parser/src/parser.rs +++ b/biscuit-parser/src/parser.rs @@ -864,8 +864,16 @@ fn null(i: &str) -> IResult<&str, builder::Term, Error> { } fn set(i: &str) -> IResult<&str, builder::Term, Error> { + alt((empty_set, non_empty_set))(i) +} + +fn empty_set(i: &str) -> IResult<&str, builder::Term, Error> { + tag("{,}")(i).map(|(i, _)| (i, builder::set(BTreeSet::new()))) +} + +fn non_empty_set(i: &str) -> IResult<&str, builder::Term, Error> { let (i, _) = preceded(space0, char('{'))(i)?; - let (i, mut list) = cut(separated_list0(preceded(space0, char(',')), term_in_set))(i)?; + let (i, mut list) = cut(separated_list1(preceded(space0, char(',')), term_in_set))(i)?; let mut set = BTreeSet::new(); @@ -960,7 +968,7 @@ fn term(i: &str) -> IResult<&str, builder::Term, Error> { preceded( space0, alt(( - parameter, string, date, variable, integer, bytes, boolean, null, set, array, parse_map, + parameter, string, date, variable, integer, bytes, boolean, null, array, parse_map, set, )), )(i) } @@ -2659,4 +2667,18 @@ mod tests { )) ); } + + #[test] + fn empty_set_map() { + use builder::{map, set}; + + assert_eq!( + super::expr("{,}").map(|(i, o)| (i, o.opcodes())), + Ok(("", vec![Op::Value(set(Default::default()))],)) + ); + assert_eq!( + super::expr("{}").map(|(i, o)| (i, o.opcodes())), + Ok(("", vec![Op::Value(map(Default::default()))],)) + ); + } } From a1cdb6b47f6506736abfeeb95faf8fc64e25922b Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Fri, 29 Nov 2024 11:36:35 +0100 Subject: [PATCH 2/3] bump runtime limits due to slow github actions workers --- biscuit-auth/examples/third_party.rs | 12 +++++++++++- biscuit-auth/src/token/authorizer.rs | 2 +- biscuit-auth/src/token/mod.rs | 4 ++++ biscuit-auth/tests/macros.rs | 16 +++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/biscuit-auth/examples/third_party.rs b/biscuit-auth/examples/third_party.rs index f7fe6b72..dcd2bd68 100644 --- a/biscuit-auth/examples/third_party.rs +++ b/biscuit-auth/examples/third_party.rs @@ -1,7 +1,9 @@ +use std::time::Duration; + use biscuit_auth::{ builder::{Algorithm, AuthorizerBuilder, BlockBuilder}, builder_ext::AuthorizerExt, - datalog::SymbolTable, + datalog::{RunLimits, SymbolTable}, Biscuit, KeyPair, }; use rand::{prelude::StdRng, SeedableRng}; @@ -36,6 +38,10 @@ fn main() { let mut authorizer = AuthorizerBuilder::new() .allow_all() + .limits(RunLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) .build(&biscuit1) .unwrap(); @@ -44,6 +50,10 @@ fn main() { let mut authorizer = AuthorizerBuilder::new() .allow_all() + .limits(RunLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) .build(&biscuit2) .unwrap(); diff --git a/biscuit-auth/src/token/authorizer.rs b/biscuit-auth/src/token/authorizer.rs index 077cfafd..8c96d305 100644 --- a/biscuit-auth/src/token/authorizer.rs +++ b/biscuit-auth/src/token/authorizer.rs @@ -1093,7 +1093,7 @@ mod tests { ) .unwrap() .limits(AuthorizerLimits { - max_time: Duration::from_millis(10), //Set 10 milliseconds as the maximum time allowed for the authorization due to "cheap" worker on GitHub Actions + max_time: Duration::from_secs(10), //Set 10 seconds as the maximum time allowed for the authorization due to "cheap" worker on GitHub Actions ..Default::default() }) .build(&biscuit2) diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index 6a963200..85800076 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -1384,6 +1384,10 @@ mod tests { .check("check if bytes($0), { hex:00000000, hex:0102AB }.contains($0)") .unwrap() .allow_all() + .limits(AuthorizerLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) .build(&biscuit2) .unwrap(); diff --git a/biscuit-auth/tests/macros.rs b/biscuit-auth/tests/macros.rs index 1696724f..9b0079e3 100644 --- a/biscuit-auth/tests/macros.rs +++ b/biscuit-auth/tests/macros.rs @@ -75,7 +75,13 @@ fn authorizer_macro() { "# ); - let authorizer = b.build_unauthenticated().unwrap(); + let authorizer = b + .limits(RunLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) + .build_unauthenticated() + .unwrap(); assert_eq!( authorizer.dump_code(), r#"appended(true); @@ -95,6 +101,10 @@ allow if true; #[test] fn authorizer_macro_trailing_comma() { let a = authorizer!(r#"fact("test", {my_key});"#, my_key = "my_value",) + .limits(RunLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) .build_unauthenticated() .unwrap(); assert_eq!( @@ -261,6 +271,10 @@ fn json() { $value.get("id") == $id, $value.get("roles").contains("admin");"# ) + .limits(RunLimits { + max_time: Duration::from_secs(10), + ..Default::default() + }) .build(&biscuit) .unwrap(); assert_eq!( From cd5d154ab57186b161e97843b2766fe416b06d38 Mon Sep 17 00:00:00 2001 From: Clement Delafargue Date: Fri, 29 Nov 2024 13:36:26 +0100 Subject: [PATCH 3/3] remove dead code, uncomment tests --- biscuit-parser/src/builder.rs | 18 ++-- biscuit-parser/src/parser.rs | 166 +++++++--------------------------- 2 files changed, 41 insertions(+), 143 deletions(-) diff --git a/biscuit-parser/src/builder.rs b/biscuit-parser/src/builder.rs index 54646234..27c9de07 100644 --- a/biscuit-parser/src/builder.rs +++ b/biscuit-parser/src/builder.rs @@ -622,30 +622,26 @@ pub fn pred>(name: &str, terms: &[I]) -> Predicate { } /// creates a rule -pub fn rule, P: AsRef>( - head_name: &str, - head_terms: &[T], - predicates: &[P], -) -> Rule { +pub fn rule>(head_name: &str, head_terms: &[T], predicates: &[Predicate]) -> Rule { Rule::new( pred(head_name, head_terms), - predicates.iter().map(|p| p.as_ref().clone()).collect(), + predicates.to_vec(), Vec::new(), vec![], ) } /// creates a rule with constraints -pub fn constrained_rule, P: AsRef, E: AsRef>( +pub fn constrained_rule>( head_name: &str, head_terms: &[T], - predicates: &[P], - expressions: &[E], + predicates: &[Predicate], + expressions: &[Expression], ) -> Rule { Rule::new( pred(head_name, head_terms), - predicates.iter().map(|p| p.as_ref().clone()).collect(), - expressions.iter().map(|c| c.as_ref().clone()).collect(), + predicates.to_vec(), + expressions.to_vec(), vec![], ) } diff --git a/biscuit-parser/src/parser.rs b/biscuit-parser/src/parser.rs index 4daeee68..d17d02b1 100644 --- a/biscuit-parser/src/parser.rs +++ b/biscuit-parser/src/parser.rs @@ -202,78 +202,6 @@ pub fn rule_inner(i: &str) -> IResult<&str, builder::Rule, Error> { Ok((i, rule)) } -/* -impl TryFrom<&str> for builder::Fact { - type Error = error::Token; - - fn try_from(value: &str) -> Result { - Ok(fact(value).finish().map(|(_, o)| o)?) - } -} - -impl TryFrom<&str> for builder::Rule { - type Error = error::Token; - - fn try_from(value: &str) -> Result { - Ok(rule(value).finish().map(|(_, o)| o)?) - } -} - -impl FromStr for builder::Fact { - type Err = error::Token; - - fn from_str(s: &str) -> Result { - Ok(fact(s).finish().map(|(_, o)| o)?) - } -} - -impl FromStr for builder::Rule { - type Err = error::Token; - - fn from_str(s: &str) -> Result { - Ok(rule(s).finish().map(|(_, o)| o)?) - } -} - -impl TryFrom<&str> for builder::Check { - type Error = error::Token; - - fn try_from(value: &str) -> Result { - Ok(check(value).finish().map(|(_, o)| o)?) - } -} - -impl FromStr for builder::Check { - type Err = error::Token; - - fn from_str(s: &str) -> Result { - Ok(check(s).finish().map(|(_, o)| o)?) - } -} - -impl TryFrom<&str> for builder::Policy { - type Error = error::Token; - - fn try_from(value: &str) -> Result { - Ok(policy(value).finish().map(|(_, o)| o)?) - } -} - -impl FromStr for builder::Policy { - type Err = error::Token; - - fn from_str(s: &str) -> Result { - Ok(policy(s).finish().map(|(_, o)| o)?) - } -} - -impl FromStr for builder::Predicate { - type Err = error::Token; - - fn from_str(s: &str) -> Result { - Ok(predicate(s).finish().map(|(_, o)| o)?) - } -}*/ fn predicate(i: &str) -> IResult<&str, builder::Predicate, Error> { let (i, _) = space0(i)?; @@ -1335,7 +1263,12 @@ where #[cfg(test)] mod tests { - use crate::builder::{self, array, int, var, Binary, Op, Unary}; + use nom::error::ErrorKind; + + use crate::{ + builder::{self, array, int, var, Binary, CheckKind, Op, Unary}, + parser::Error, + }; #[test] fn name() { @@ -1991,7 +1924,7 @@ mod tests { )) ); } - /* + #[test] fn rule() { assert_eq!( @@ -2004,7 +1937,7 @@ mod tests { &[ builder::pred("resource", &[builder::variable("0")]), builder::pred("operation", &[builder::string("read")]), - ] + ], ) )) ); @@ -2021,9 +1954,7 @@ mod tests { "", builder::constrained_rule( "valid_date", - &[ - builder::string("file1"), - ], + &[builder::string("file1")], &[ builder::pred("time", &[builder::variable("0")]), builder::pred("resource", &[builder::string("file1")]), @@ -2075,9 +2006,9 @@ mod tests { assert_eq!( super::rule("right($0, $test) <- resource($0), operation(\"read\")"), Err( nom::Err::Failure(Error { - input: "right($0, $test)", + input: "right($0, $test) <- resource($0), operation(\"read\")", code: ErrorKind::Satisfy, - message: Some("rule head contains variables that are not used in predicates of the rule's body: $test".to_string()), + message: Some("the rule contains variables that are not bound by predicates in the rule's body: $test".to_string()), })) ); } @@ -2090,6 +2021,7 @@ mod tests { Ok(( "", builder::Check { + kind: builder::CheckKind::One, queries: vec![ builder::rule( "query", @@ -2147,12 +2079,9 @@ mod tests { #[test] fn expression() { use super::Expr; - use crate::datalog::SymbolTable; use builder::{date, int, string, var, Binary, Op, Term}; use std::time::{Duration, SystemTime}; - let mut syms = SymbolTable::new(); - let input = " -1 "; println!("parsing: {}", input); let res = super::expr(input); @@ -2160,8 +2089,6 @@ mod tests { let ops = res.unwrap().1.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops }.convert(&mut syms); - println!("print: {}", e.print(&syms).unwrap()); let input = " $0 <= 2019-12-04T09:46:41+00:00"; println!("parsing: {}", input); @@ -2182,9 +2109,6 @@ mod tests { let ops = res.unwrap().1.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops }.convert(&mut syms); - println!("print: {}", e.print(&syms).unwrap()); - let input = " 1 < $test + 2 "; println!("parsing: {}", input); let res = super::expr(input); @@ -2206,8 +2130,6 @@ mod tests { let ops = res.unwrap().1.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops }.convert(&mut syms); - println!("print: {}", e.print(&syms).unwrap()); let input = " 2 < $test && $var2.starts_with(\"test\") && true "; println!("parsing: {}", input); @@ -2217,40 +2139,37 @@ mod tests { Ok(( " ", Expr::Binary( - Op::Binary(Binary::And), + Op::Binary(Binary::LazyAnd), Box::new(Expr::Binary( - Op::Binary(Binary::And), + Op::Binary(Binary::LazyAnd), Box::new(Expr::Binary( Op::Binary(Binary::LessThan), Box::new(Expr::Value(int(2))), Box::new(Expr::Value(var("test"))), )), - Box::new(Expr::Binary( - Op::Binary(Binary::Prefix), - Box::new(Expr::Value(var("var2"))), - Box::new(Expr::Value(string("test"))), + Box::new(Expr::Closure( + vec![], + Box::new(Expr::Binary( + Op::Binary(Binary::Prefix), + Box::new(Expr::Value(var("var2"))), + Box::new(Expr::Value(string("test"))), + )) )), )), - Box::new(Expr::Value(Term::Bool(true))), + Box::new(Expr::Closure( + vec![], + Box::new(Expr::Value(Term::Bool(true))), + )) ) )) ); - let ops = res.unwrap().1.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops }.convert(&mut syms); - println!("print: {}", e.print(&syms).unwrap()); - - //panic!(); } #[test] fn parens() { - use crate::datalog::{SymbolTable, TemporarySymbolTable}; use builder::{int, Binary, Op, Unary}; - use std::collections::HashMap; - - let mut syms = SymbolTable::new(); let input = " 1 + 2 * 3 "; println!("parsing: {}", input); @@ -2258,15 +2177,6 @@ mod tests { let ops = res.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops: ops.clone() }.convert(&mut syms); - - let printed = e.print(&syms).unwrap(); - println!("print: {}", e.print(&syms).unwrap()); - let h = HashMap::new(); - let result = e - .evaluate(&h, &mut TemporarySymbolTable::new(&syms)) - .unwrap(); - println!("evaluates to: {:?}", result); assert_eq!( ops, @@ -2278,8 +2188,6 @@ mod tests { Op::Binary(Binary::Add), ] ); - assert_eq!(&printed, "1 + 2 * 3"); - assert_eq!(result, datalog::Term::Integer(7)); let input = " (1 + 2) * 3 "; println!("parsing: {}", input); @@ -2287,15 +2195,6 @@ mod tests { let ops = res.opcodes(); println!("ops: {:#?}", ops); - let e = builder::Expression { ops: ops.clone() }.convert(&mut syms); - - let printed = e.print(&syms).unwrap(); - println!("print: {}", e.print(&syms).unwrap()); - let h = HashMap::new(); - let result = e - .evaluate(&h, &mut TemporarySymbolTable::new(&syms)) - .unwrap(); - println!("evaluates to: {:?}", result); assert_eq!( ops, @@ -2308,8 +2207,6 @@ mod tests { Op::Binary(Binary::Mul), ] ); - assert_eq!(&printed, "(1 + 2) * 3"); - assert_eq!(result, datalog::Term::Integer(9)); } #[test] @@ -2327,7 +2224,7 @@ mod tests { rule_head($var0) <- fact($var0, $var1), 1 < 2; // line comment - check if 1 == 2; + check if 1 === 2; allow if rule_head("string"); @@ -2369,6 +2266,7 @@ mod tests { let expected_checks = vec![ Check { + kind: CheckKind::One, queries: vec![constrained_rule( "query", empty_terms, @@ -2383,6 +2281,7 @@ mod tests { )], }, Check { + kind: CheckKind::One, queries: vec![ rule("query", empty_terms, &[pred("fact", &[int(5678)])]), constrained_rule( @@ -2400,6 +2299,7 @@ mod tests { ], }, Check { + kind: CheckKind::One, queries: vec![constrained_rule( "query", empty_terms, @@ -2476,7 +2376,7 @@ mod tests { fact2(1234); rule_head($var0) <- fact($var0, $var1), 1 < 2; // line comment - check if 1 == 2; /* + check if 1 === 2; /* other comment */ check if @@ -2512,6 +2412,7 @@ mod tests { let expected_checks = vec![ Check { + kind: CheckKind::One, queries: vec![constrained_rule( "query", empty_terms, @@ -2526,6 +2427,7 @@ mod tests { )], }, Check { + kind: CheckKind::One, queries: vec![ rule("query", empty_terms, &[pred("fact", &[int(5678)])]), constrained_rule( @@ -2543,6 +2445,7 @@ mod tests { ], }, Check { + kind: CheckKind::One, queries: vec![constrained_rule( "query", empty_terms, @@ -2575,8 +2478,7 @@ mod tests { result.checks.drain(..).map(|(_, r)| r).collect::>(), expected_checks ); - }*/ - + } #[test] fn chained_calls() {