From e8034c0726d7aa9f050efa1407d6f5c2f9312b2b Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Sat, 8 Apr 2023 08:07:51 +0300 Subject: [PATCH 1/4] Add unit test for SuperposeOp with non deterministic argument --- lib/src/metta/runner/stdlib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 7442bb893..59186c783 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1163,6 +1163,7 @@ pub fn metta_code() -> &'static str { #[cfg(test)] mod tests { use super::*; + use crate::metta::runner::*; use crate::metta::types::validate_atom; #[test] @@ -1365,6 +1366,23 @@ mod tests { {Number::Integer(1)}))); } + #[test] + fn superpose_op_multiple_interpretations() { + let metta = new_metta_rust(); + let mut parser = SExprParser::new(" + (= (f) A) + (= (f) B) + (= (g) C) + (= (g) D) + + !(superpose ((f) (g))) + "); + + assert_eq_metta_results!(metta.run(&mut parser), + Ok(vec![vec![expr!("A"), expr!("B"), expr!("C"), expr!("D"), + expr!("A"), expr!("B"), expr!("C"), expr!("D")]])); + } + #[test] fn get_type_op() { let space = Shared::new(metta_space(" From 7b4b3b1410579347ec6985932a19b538967dcc2f Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Sat, 8 Apr 2023 08:11:30 +0300 Subject: [PATCH 2/4] Pop SuperposeOp arg instead of copying --- lib/src/metta/runner/stdlib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 59186c783..375f8c40a 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -629,9 +629,12 @@ impl Grounded for SuperposeOp { let arg_error = || ExecError::from("superpose expects single expression as an argument"); // `superpose` receives one atom (expression) in order to make composition // `(superpose (collapse ...))` possible - let expr = atom_as_expr(args.get(0).ok_or_else(arg_error)?).ok_or(arg_error())?; + let expr = match args.pop().ok_or_else(arg_error)? { + Atom::Expression(expr) => Ok(expr), + _ => Err(arg_error()), + }?; - Ok(expr.children().clone()) + Ok(expr.into_children()) } fn match_(&self, other: &Atom) -> MatchResultIter { From 76d1ffa382a0e4649bd4e9490889c7b954d9bac7 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Mon, 10 Apr 2023 12:59:15 +0300 Subject: [PATCH 3/4] Add type to the Error to not interpret errors further --- lib/src/metta/runner/stdlib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 375f8c40a..9f154d2b4 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1160,6 +1160,7 @@ pub fn metta_code() -> &'static str { (: if (-> Bool Atom Atom $t)) (= (if True $then $else) $then) (= (if False $then $else) $else) + (: Error (-> Atom Atom ErrorType)) " } From 46b350ded11381b2584d7828c68128ea6094f974 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Mon, 10 Apr 2023 12:59:52 +0300 Subject: [PATCH 4/4] Implement SuperposOp by interpreting each of sub-expressions Check (let $x (collapse ...) (superpose $x)) works. --- lib/src/metta/runner/stdlib.rs | 77 +++++++++++++++++++------ python/tests/scripts/b4_nondeterm.metta | 2 +- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 9f154d2b4..5f3e7d8b3 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -612,7 +612,15 @@ impl Grounded for CollapseOp { } #[derive(Clone, PartialEq, Debug)] -pub struct SuperposeOp { } +pub struct SuperposeOp { + space: Shared, +} + +impl SuperposeOp { + fn new(space: Shared) -> Self { + Self{ space } + } +} impl Display for SuperposeOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -622,19 +630,22 @@ impl Display for SuperposeOp { impl Grounded for SuperposeOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_UNDEFINED]) } fn execute(&self, args: &mut Vec) -> Result, ExecError> { let arg_error = || ExecError::from("superpose expects single expression as an argument"); - // `superpose` receives one atom (expression) in order to make composition - // `(superpose (collapse ...))` possible - let expr = match args.pop().ok_or_else(arg_error)? { - Atom::Expression(expr) => Ok(expr), - _ => Err(arg_error()), - }?; + let atom = args.get(0).ok_or_else(arg_error)?; + let expr = atom_as_expr(&atom).ok_or(arg_error())?; - Ok(expr.into_children()) + let mut superposed = Vec::new(); + for atom in expr.children() { + match interpret_no_error(self.space.clone(), atom) { + Ok(results) => { superposed.extend(results); }, + Err(message) => { return Err(format!("Error: {}", message).into()) }, + } + } + Ok(superposed) } fn match_(&self, other: &Atom) -> MatchResultIter { @@ -1072,8 +1083,6 @@ pub fn register_common_tokens(metta: &Metta) { tref.register_token(regex(r"cdr-atom"), move |_| { cdr_atom_op.clone() }); let cons_atom_op = Atom::gnd(ConsAtomOp{}); tref.register_token(regex(r"cons-atom"), move |_| { cons_atom_op.clone() }); - let superpose_op = Atom::gnd(SuperposeOp{}); - tref.register_token(regex(r"superpose"), move |_| { superpose_op.clone() }); let println_op = Atom::gnd(PrintlnOp{}); tref.register_token(regex(r"println!"), move |_| { println_op.clone() }); let trace_op = Atom::gnd(TraceOp{}); @@ -1106,6 +1115,8 @@ pub fn register_runner_tokens(metta: &Metta, cwd: PathBuf) { tref.register_token(regex(r"assertEqualToResult"), move |_| { assert_equal_to_result_op.clone() }); let collapse_op = Atom::gnd(CollapseOp::new(space.clone())); tref.register_token(regex(r"collapse"), move |_| { collapse_op.clone() }); + let superpose_op = Atom::gnd(SuperposeOp::new(space.clone())); + tref.register_token(regex(r"superpose"), move |_| { superpose_op.clone() }); let get_type_op = Atom::gnd(GetTypeOp::new(space.clone())); tref.register_token(regex(r"get-type"), move |_| { get_type_op.clone() }); // TODO: here clone of the metta is moved into separate location in memory. @@ -1357,16 +1368,17 @@ mod tests { #[test] fn superpose_op() { - let superpose_op = SuperposeOp{}; + let space = Shared::new(GroundingSpace::new()); + let superpose_op = SuperposeOp::new(space); assert_eq!(superpose_op.execute(&mut vec![expr!("A" ("B" "C"))]), Ok(vec![sym!("A"), expr!("B" "C")])); } #[test] fn superpose_op_type() { - let space = GroundingSpace::new(); - assert!(validate_atom(&space, &expr!({SumOp{}} - ({SuperposeOp{}} ({Number::Integer(1)} {Number::Integer(2)} {Number::Integer(3)})) + let space = Shared::new(GroundingSpace::new()); + assert!(validate_atom(space.borrow().deref(), &expr!({SumOp{}} + ({SuperposeOp::new(space.clone())} ({Number::Integer(1)} {Number::Integer(2)} {Number::Integer(3)})) {Number::Integer(1)}))); } @@ -1383,8 +1395,39 @@ mod tests { "); assert_eq_metta_results!(metta.run(&mut parser), - Ok(vec![vec![expr!("A"), expr!("B"), expr!("C"), expr!("D"), - expr!("A"), expr!("B"), expr!("C"), expr!("D")]])); + Ok(vec![vec![expr!("A"), expr!("B"), expr!("C"), expr!("D")]])); + } + + #[test] + fn superpose_op_superposed_with_collapse() { + let metta = new_metta_rust(); + let mut parser = SExprParser::new(" + (= (f) A) + (= (f) B) + + !(let $x (collapse (f)) (superpose $x)) + "); + + assert_eq_metta_results!(metta.run(&mut parser), + Ok(vec![vec![expr!("A"), expr!("B")]])); + } + + #[test] + fn superpose_op_consumes_interpreter_errors() { + let metta = new_metta_rust(); + let mut parser = SExprParser::new(" + (: f (-> A B)) + (= (f $x) $x) + + (: a A) + (: b B) + + !(superpose ((f (nop)) (f a) (f b))) + "); + + assert_eq!(metta.run(&mut parser), Ok(vec![vec![ + expr!("Error" ("f" ({NopOp{}})) "NoValidAlternatives"), + expr!("a"), expr!("Error" "b" "BadType")]])); } #[test] diff --git a/python/tests/scripts/b4_nondeterm.metta b/python/tests/scripts/b4_nondeterm.metta index f7e737932..08937003b 100644 --- a/python/tests/scripts/b4_nondeterm.metta +++ b/python/tests/scripts/b4_nondeterm.metta @@ -27,7 +27,7 @@ ; `superpose` reverts `collapse` !(assertEqual (color) - (superpose (collapse (color)))) + (let $x (collapse (color)) (superpose $x))) ; In contrast to `match`, if the equality query returns an empty result ; the interpreter doesn't reduce a symbolic expression