Skip to content

Commit

Permalink
feat(cognitarium): add the 'not' expression
Browse files Browse the repository at this point in the history
  • Loading branch information
amimart committed Aug 22, 2024
1 parent 9f389d4 commit 41a0a78
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 0 deletions.
2 changes: 2 additions & 0 deletions contracts/axone-cognitarium/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ pub enum Expression {
Less(Box<Self>, Box<Self>),
/// Less or equal comparison.
LessOrEqual(Box<Self>, Box<Self>),
/// Negation of an expression.
Not(Box<Self>),
}

/// # TripleDeleteTemplate
Expand Down
199 changes: 199 additions & 0 deletions contracts/axone-cognitarium/src/querier/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum Expression {
GreaterOrEqual(Box<Self>, Box<Self>),
Less(Box<Self>, Box<Self>),
LessOrEqual(Box<Self>, Box<Self>),
Not(Box<Self>),
}

impl Expression {
Expand Down Expand Up @@ -64,6 +65,7 @@ impl Expression {
Expression::LessOrEqual(left, right) => Ok(Term::Boolean(
left.evaluate(vars, ns_solver)? <= right.evaluate(vars, ns_solver)?,
)),
Expression::Not(expr) => Ok(Term::Boolean(!expr.evaluate(vars, ns_solver)?.as_bool())),
}
}
}
Expand All @@ -88,6 +90,9 @@ impl HasBoundVariables for Expression {
left.lookup_bound_variables(callback);
right.lookup_bound_variables(callback);
}
Expression::Not(expr) => {
expr.lookup_bound_variables(callback);
}
}
}
}
Expand Down Expand Up @@ -146,3 +151,197 @@ impl PartialOrd<Term> for Term {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeSet;

#[test]
fn expression_bound_variables() {
let cases = vec![
(
Expression::Constant(Term::String("foo".to_string())),
vec![],
),
(Expression::Variable(0), vec![0]),
(
Expression::And(vec![Expression::Variable(0), Expression::Variable(1)]),
vec![0, 1],
),
(
Expression::Or(vec![Expression::Variable(0), Expression::Variable(1)]),
vec![0, 1],
),
(
Expression::Equal(
Box::new(Expression::Variable(0)),
Box::new(Expression::Variable(1)),
),
vec![0, 1],
),
(
Expression::Greater(
Box::new(Expression::Variable(0)),
Box::new(Expression::Variable(1)),
),
vec![0, 1],
),
(
Expression::GreaterOrEqual(
Box::new(Expression::Variable(0)),
Box::new(Expression::Variable(1)),
),
vec![0, 1],
),
(
Expression::Less(
Box::new(Expression::Variable(0)),
Box::new(Expression::Variable(1)),
),
vec![0, 1],
),
(
Expression::LessOrEqual(
Box::new(Expression::Variable(0)),
Box::new(Expression::Variable(1)),
),
vec![0, 1],
),
(Expression::Not(Box::new(Expression::Variable(0))), vec![0]),
];

for case in cases {
assert_eq!(case.0.bound_variables(), BTreeSet::from_iter(case.1));
}
}

#[test]
fn term_from_iri() {
let cases = vec![
(
msg::IRI::Prefixed("foo:bar".to_string()),
Ok(Term::String("http://example.com/bar".to_string())),
),
(
msg::IRI::Full("foo:bar".to_string()),
Ok(Term::String("foo:bar".to_string())),
),
(
msg::IRI::Prefixed("unknown:bar".to_string()),
Err(StdError::generic_err("Prefix not found: unknown")),
),
];

let mut prefixes = HashMap::new();
prefixes.insert("foo".to_string(), "http://example.com/".to_string());

for case in cases {
assert_eq!(Term::from_iri(case.0, &prefixes), case.1);
}
}

#[test]
fn term_from_literal() {
let cases = vec![
(
msg::Literal::Simple("foo".to_string()),
Ok(Term::String("foo".to_string())),
),
(
msg::Literal::LanguageTaggedString {
value: "foo".to_string(),
language: "en".to_string(),
},
Ok(Term::String("fooen".to_string())),
),
(
msg::Literal::TypedValue {
value: "foo".to_string(),
datatype: msg::IRI::Prefixed("foo:bar".to_string()),
},
Ok(Term::String("foohttp://example.com/bar".to_string())),
),
(
msg::Literal::TypedValue {
value: "foo".to_string(),
datatype: msg::IRI::Prefixed("unknown:bar".to_string()),
},
Err(StdError::generic_err("Prefix not found: unknown")),
),
];

let mut prefixes = HashMap::new();
prefixes.insert("foo".to_string(), "http://example.com/".to_string());

for case in cases {
assert_eq!(Term::from_literal(case.0, &prefixes), case.1);
}
}

#[test]
fn term_as_string() {
let cases = vec![
(Term::String("foo".to_string()), "foo"),
(Term::Boolean(true), "true"),
(Term::Boolean(false), "false"),
];
for case in cases {
assert_eq!(case.0.as_string(), case.1);
}
}

#[test]
fn term_as_bool() {
let cases = vec![
(Term::String("foo".to_string()), true),
(Term::String("".to_string()), false),
(Term::Boolean(true), true),
(Term::Boolean(false), false),
];
for case in cases {
assert_eq!(case.0.as_bool(), case.1);
}
}

#[test]
fn term_partial_cmp() {
let cases = vec![
(
Term::String("a".to_string()),
Term::String("b".to_string()),
Some(Ordering::Less),
),
(
Term::String("b".to_string()),
Term::String("a".to_string()),
Some(Ordering::Greater),
),
(
Term::String("a".to_string()),
Term::String("a".to_string()),
Some(Ordering::Equal),
),
(
Term::Boolean(true),
Term::Boolean(false),
Some(Ordering::Greater),
),
(
Term::Boolean(false),
Term::Boolean(true),
Some(Ordering::Less),
),
(
Term::Boolean(true),
Term::Boolean(true),
Some(Ordering::Equal),
),
(Term::String("a".to_string()), Term::Boolean(true), None),
(Term::Boolean(true), Term::String("a".to_string()), None),
];
for case in cases {
assert_eq!(case.0.partial_cmp(&case.1), case.2);
}
}
}
4 changes: 4 additions & 0 deletions contracts/axone-cognitarium/src/querier/plan_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ impl<'a> PlanBuilder<'a> {
Box::new(self.build_expression(left)?),
Box::new(self.build_expression(right)?),
)),
msg::Expression::Not(child) => self
.build_expression(child)
.map(Box::new)
.map(Expression::Not),
}
}

Expand Down

0 comments on commit 41a0a78

Please sign in to comment.