Skip to content

Commit

Permalink
Merge branch 'main' into msgspace
Browse files Browse the repository at this point in the history
  • Loading branch information
Necr0x0Der authored Nov 6, 2023
2 parents ef91eda + 5eb2d51 commit eb0c674
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 34 deletions.
4 changes: 3 additions & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ path = "src/lib.rs"
crate-type = ["lib"]

[features]
#default = ["minimal"]
default = ["variable_operation"]
#default = ["variable_operation", "minimal"]
minimal = []
variable_operation = []
43 changes: 35 additions & 8 deletions lib/src/metta/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ fn is_grounded_op(expr: &ExpressionAtom) -> bool {
}
}

fn is_variable_op(expr: &ExpressionAtom) -> bool {
match expr.children().get(0) {
Some(Atom::Variable(_)) => true,
_ => false,
}
}

fn has_grounded_sub_expr(expr: &Atom) -> bool {
return SubexprStream::from_expr(expr.clone(), TOP_DOWN_DEPTH_WALK)
.any(|sub| if let Atom::Expression(sub) = sub {
Expand Down Expand Up @@ -551,6 +558,12 @@ fn interpret_reducted_plan<'a, T: SpaceRef<'a>>(context: InterpreterContextRef<'
if let Atom::Expression(ref expr) = input.atom() {
if is_grounded_op(expr) {
Box::new(execute_plan(context, input))
} else if is_variable_op(expr) {
#[cfg(feature = "variable_operation")]
let result = Box::new(match_plan(context, input));
#[cfg(not(feature = "variable_operation"))]
let result = Box::new(StepResult::ret(vec![input]));
result
} else {
Box::new(match_plan(context, input))
}
Expand Down Expand Up @@ -745,16 +758,16 @@ mod tests {
space.add(expr!("=" ("and" "True" "True") "True"));
space.add(expr!("=" ("if" "True" then else) then));
space.add(expr!("=" ("if" "False" then else) else));
space.add(expr!("=" ("Fritz" "croaks") "True"));
space.add(expr!("=" ("Fritz" "eats-flies") "True"));
space.add(expr!("=" ("Tweety" "chirps") "True"));
space.add(expr!("=" ("Tweety" "yellow") "True"));
space.add(expr!("=" ("Tweety" "eats-flies") "True"));
let expr = expr!("if" ("and" (x "croaks") (x "eats-flies"))
("=" (x "frog") "True") "nop");
space.add(expr!("=" ("croaks" "Fritz") "True"));
space.add(expr!("=" ("eats-flies" "Fritz") "True"));
space.add(expr!("=" ("chirps" "Tweety") "True"));
space.add(expr!("=" ("yellow" "Tweety") "True"));
space.add(expr!("=" ("eats-flies" "Tweety") "True"));
let expr = expr!("if" ("and" ("croaks" x) ("eats-flies" x))
("=" ("frog" x) "True") "nop");

assert_eq!(interpret(&space, &expr),
Ok(vec![expr!("=" ("Fritz" "frog") "True")]));
Ok(vec![expr!("=" ("frog" "Fritz") "True")]));
}

fn results_are_equivalent(actual: &Result<Vec<Atom>, String>,
Expand Down Expand Up @@ -1081,5 +1094,19 @@ mod tests {
panic!("Non-empty result is expected");
}
}

#[test]
fn interpret_match_variable_operation() {
let mut space = GroundingSpace::new();
space.add(expr!("=" ("foo" x) ("foo result" x)));
space.add(expr!("=" ("bar" x) ("bar result" x)));

let actual = interpret(&space, &expr!(op "arg")).unwrap();

#[cfg(feature = "variable_operation")]
assert_eq_no_order!(actual, vec![expr!("foo result" "arg"), expr!("bar result" "arg")]);
#[cfg(not(feature = "variable_operation"))]
assert_eq!(actual, vec![expr!(op "arg")]);
}
}

138 changes: 113 additions & 25 deletions lib/src/metta/runner/arithmetics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ macro_rules! def_binary_number_op {
let b = args.get(1).ok_or_else(arg_error)?.as_gnd::<Number>().ok_or_else(arg_error)?;

let res = match (a, b) {
(Number::Integer(a), Number::Integer(b)) => $cast(a $op b),
(Number::Integer(a), Number::Float(b)) => $cast((*a as f64) $op *b),
(Number::Float(a), Number::Integer(b)) => $cast(*a $op (*b as f64)),
(Number::Float(a), Number::Float(b)) => $cast(a $op b),
(&Number::Integer(a), &Number::Integer(b)) => $cast(a $op b),
(&Number::Integer(a), &Number::Float(b)) => $cast((a as f64) $op b),
(&Number::Float(a), &Number::Integer(b)) => $cast(a $op (b as f64)),
(&Number::Float(a), &Number::Float(b)) => $cast(a $op b),
};

Ok(vec![Atom::gnd(res)])
Expand All @@ -159,6 +159,66 @@ def_binary_number_op!(GreaterOp, >, ATOM_TYPE_BOOL, Bool);
def_binary_number_op!(LessEqOp, <=, ATOM_TYPE_BOOL, Bool);
def_binary_number_op!(GreaterEqOp, >=, ATOM_TYPE_BOOL, Bool);

macro_rules! def_binary_bool_op {
($name:ident, $disp:ident, $op:tt) => {
#[derive(Clone, PartialEq, Debug)]
pub struct $name{}

impl Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, stringify!($disp))
}
}

impl Grounded for $name {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL])
}

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from(concat!(stringify!($disp), " expects two boolean arguments"));
let &Bool(a) = args.get(0).ok_or_else(arg_error)?.as_gnd::<Bool>().ok_or_else(arg_error)?;
let &Bool(b) = args.get(1).ok_or_else(arg_error)?.as_gnd::<Bool>().ok_or_else(arg_error)?;

Ok(vec![Atom::gnd(Bool(a $op b))])
}

fn match_(&self, other: &Atom) -> MatchResultIter {
match_by_equality(self, other)
}
}
}
}

def_binary_bool_op!(AndOp, and, &&);
def_binary_bool_op!(OrOp, or, ||);

#[derive(Clone, PartialEq, Debug)]
pub struct NotOp{}

impl Display for NotOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "not")
}
}

impl Grounded for NotOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL])
}

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("not expects one boolean arguments");
let &Bool(a) = args.get(0).ok_or_else(arg_error)?.as_gnd::<Bool>().ok_or_else(arg_error)?;

Ok(vec![Atom::gnd(Bool(!a))])
}

fn match_(&self, other: &Atom) -> MatchResultIter {
match_by_equality(self, other)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -181,49 +241,77 @@ mod tests {
assert_eq!(format!("{}", Bool(false)), "False");
}

macro_rules! assert_number_binary_op {
macro_rules! assert_binary_op {
($name:ident, $a: expr, $b: expr, $r: expr) => {
assert_eq!($name{}.execute(&mut vec![Atom::gnd($a), Atom::gnd($b)]), Ok(vec![Atom::gnd($r)]));
}
}

macro_rules! assert_unary_op {
($name:ident, $a: expr, $r: expr) => {
assert_eq!($name{}.execute(&mut vec![Atom::gnd($a)]), Ok(vec![Atom::gnd($r)]));
}
}

#[test]
fn and() {
assert_binary_op!(AndOp, Bool(true), Bool(true), Bool(true));
assert_binary_op!(AndOp, Bool(true), Bool(false), Bool(false));
assert_binary_op!(AndOp, Bool(false), Bool(true), Bool(false));
assert_binary_op!(AndOp, Bool(false), Bool(false), Bool(false));
}

#[test]
fn or() {
assert_binary_op!(OrOp, Bool(true), Bool(true), Bool(true));
assert_binary_op!(OrOp, Bool(true), Bool(false), Bool(true));
assert_binary_op!(OrOp, Bool(false), Bool(true), Bool(true));
assert_binary_op!(OrOp, Bool(false), Bool(false), Bool(false));
}

#[test]
fn not() {
assert_unary_op!(NotOp, Bool(true), Bool(false));
assert_unary_op!(NotOp, Bool(false), Bool(true));
}

#[test]
fn sum_op() {
assert_number_binary_op!(SumOp, Number::Integer(40), Number::Integer(2), Number::Integer(42));
assert_number_binary_op!(SumOp, Number::Integer(40), Number::Float(2.42), Number::Float(42.42));
assert_number_binary_op!(SumOp, Number::Float(40.42), Number::Integer(2), Number::Float(42.42));
assert_number_binary_op!(SumOp, Number::Float(40.40), Number::Float(2.02), Number::Float(42.42));
assert_binary_op!(SumOp, Number::Integer(40), Number::Integer(2), Number::Integer(42));
assert_binary_op!(SumOp, Number::Integer(40), Number::Float(2.42), Number::Float(42.42));
assert_binary_op!(SumOp, Number::Float(40.42), Number::Integer(2), Number::Float(42.42));
assert_binary_op!(SumOp, Number::Float(40.40), Number::Float(2.02), Number::Float(42.42));
}

#[test]
fn sub_op() {
assert_number_binary_op!(SubOp, Number::Integer(44), Number::Integer(2), Number::Integer(42));
assert_number_binary_op!(SubOp, Number::Integer(44), Number::Float(2.42), Number::Float(41.58));
assert_number_binary_op!(SubOp, Number::Float(44.42), Number::Integer(2), Number::Float(42.42));
assert_number_binary_op!(SubOp, Number::Float(44.5), Number::Float(2.5), Number::Float(42.0));
assert_binary_op!(SubOp, Number::Integer(44), Number::Integer(2), Number::Integer(42));
assert_binary_op!(SubOp, Number::Integer(44), Number::Float(2.42), Number::Float(41.58));
assert_binary_op!(SubOp, Number::Float(44.42), Number::Integer(2), Number::Float(42.42));
assert_binary_op!(SubOp, Number::Float(44.5), Number::Float(2.5), Number::Float(42.0));
}

#[test]
fn mul_op() {
assert_number_binary_op!(MulOp, Number::Integer(6), Number::Integer(7), Number::Integer(42));
assert_number_binary_op!(MulOp, Number::Integer(4), Number::Float(10.5), Number::Float(42.0));
assert_number_binary_op!(MulOp, Number::Float(10.5), Number::Integer(4), Number::Float(42.0));
assert_number_binary_op!(MulOp, Number::Float(2.5), Number::Float(16.8), Number::Float(42.0));
assert_binary_op!(MulOp, Number::Integer(6), Number::Integer(7), Number::Integer(42));
assert_binary_op!(MulOp, Number::Integer(4), Number::Float(10.5), Number::Float(42.0));
assert_binary_op!(MulOp, Number::Float(10.5), Number::Integer(4), Number::Float(42.0));
assert_binary_op!(MulOp, Number::Float(2.5), Number::Float(16.8), Number::Float(42.0));
}

#[test]
fn div_op() {
assert_number_binary_op!(DivOp, Number::Integer(84), Number::Integer(2), Number::Integer(42));
assert_number_binary_op!(DivOp, Number::Integer(441), Number::Float(10.5), Number::Float(42.0));
assert_number_binary_op!(DivOp, Number::Float(84.0), Number::Integer(2), Number::Float(42.0));
assert_number_binary_op!(DivOp, Number::Float(430.5), Number::Float(10.25), Number::Float(42.0));
assert_binary_op!(DivOp, Number::Integer(84), Number::Integer(2), Number::Integer(42));
assert_binary_op!(DivOp, Number::Integer(441), Number::Float(10.5), Number::Float(42.0));
assert_binary_op!(DivOp, Number::Float(84.0), Number::Integer(2), Number::Float(42.0));
assert_binary_op!(DivOp, Number::Float(430.5), Number::Float(10.25), Number::Float(42.0));
}

#[test]
fn mod_op() {
assert_number_binary_op!(ModOp, Number::Integer(85), Number::Integer(43), Number::Integer(42));
assert_number_binary_op!(ModOp, Number::Integer(85), Number::Float(43.5), Number::Float(41.5));
assert_number_binary_op!(ModOp, Number::Float(85.5), Number::Integer(43), Number::Float(42.5));
assert_number_binary_op!(ModOp, Number::Float(85.5), Number::Float(43.5), Number::Float(42.0));
assert_binary_op!(ModOp, Number::Integer(85), Number::Integer(43), Number::Integer(42));
assert_binary_op!(ModOp, Number::Integer(85), Number::Float(43.5), Number::Float(41.5));
assert_binary_op!(ModOp, Number::Float(85.5), Number::Integer(43), Number::Float(42.5));
assert_binary_op!(ModOp, Number::Float(85.5), Number::Float(43.5), Number::Float(42.0));
}
}
11 changes: 11 additions & 0 deletions lib/src/metta/runner/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,12 @@ pub fn register_rust_tokens(metta: &Metta) {
tref.register_token(regex(r">="), move |_| { ge_op.clone() });
let eq_op = Atom::gnd(EqualOp{});
tref.register_token(regex(r"=="), move |_| { eq_op.clone() });
let and_op = Atom::gnd(AndOp{});
tref.register_token(regex(r"and"), move |_| { and_op.clone() });
let or_op = Atom::gnd(OrOp{});
tref.register_token(regex(r"or"), move |_| { or_op.clone() });
let not_op = Atom::gnd(NotOp{});
tref.register_token(regex(r"not"), move |_| { not_op.clone() });

metta.tokenizer().borrow_mut().move_front(&mut rust_tokens);
}
Expand Down Expand Up @@ -1626,4 +1632,9 @@ mod tests {
fn test_stdlib_uses_rust_grounded_tokens() {
assert_eq!(run_program("!(if True ok nok)"), Ok(vec![vec![Atom::sym("ok")]]));
}

#[test]
fn test_let_op_inside_other_operation() {
assert_eq!(run_program("!(and True (let $x False $x))"), Ok(vec![vec![expr!({Bool(false)})]]));
}
}

0 comments on commit eb0c674

Please sign in to comment.