From c1177e9720a692e4491250a0f224085116dfa8bf Mon Sep 17 00:00:00 2001 From: Innokenty Date: Wed, 11 Dec 2024 14:46:29 +0300 Subject: [PATCH 1/8] assertAlphaEqual added --- lib/src/atom/matcher.rs | 3 ++ lib/src/metta/runner/stdlib/debug.rs | 55 +++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/src/atom/matcher.rs b/lib/src/atom/matcher.rs index 102bcf58c..fa7af10d9 100644 --- a/lib/src/atom/matcher.rs +++ b/lib/src/atom/matcher.rs @@ -1343,6 +1343,9 @@ mod test { #[test] fn test_atoms_are_equivalent() { + assert!(atoms_are_equivalent(&expr!(x "b" {"c"}), &expr!(x "b" {"c"}))); + assert!(atoms_are_equivalent(&expr!(x "b" x), &expr!(x "b" x))); + assert!(atoms_are_equivalent(&expr!(a a "b" {"c"}), &expr!(x x "b" {"c"}))); assert!(atoms_are_equivalent(&expr!(a "b" {"c"}), &expr!(x "b" {"c"}))); assert!(atoms_are_equivalent(&expr!(a b), &expr!(c d))); assert!(!atoms_are_equivalent(&expr!(a "b" {"c"}), &expr!(a "x" {"c"}))); diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index 5f8006784..03bf284ab 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -3,7 +3,7 @@ use crate::metta::*; use crate::metta::text::Tokenizer; use crate::space::*; use crate::common::assert::vec_eq_no_order; - +use crate::atom::matcher::atoms_are_equivalent; use crate::metta::runner::stdlib::{grounded_op, atom_to_string, regex, interpret_no_error, unit_result}; use std::convert::TryInto; @@ -18,6 +18,15 @@ fn assert_results_equal(actual: &Vec, expected: &Vec, atom: &Atom) - } } +fn assert_alpha_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> Result, ExecError> { + log::debug!("assert_alpha_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); + let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); + match atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap()) { + true => unit_result(), + false => Err(ExecError::Runtime(format!("{}", report))) + } +} + /// Implement trace! built-in. /// /// It is equivalent to Idris or Haskell Trace, that is, it prints a @@ -141,6 +150,42 @@ impl CustomExecute for AssertEqualOp { } } +#[derive(Clone, Debug)] +pub struct AssertAlphaEqualOp { + space: DynSpace, +} + +grounded_op!(AssertAlphaEqualOp, "assertAlphaEqual"); + +impl AssertAlphaEqualOp { + pub fn new(space: DynSpace) -> Self { + Self{ space } + } +} + +impl Grounded for AssertAlphaEqualOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for AssertAlphaEqualOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + log::debug!("AssertAlphaEqualOp::execute: {:?}", args); + let arg_error = || ExecError::from("assertAlphaEqual expects two atoms as arguments: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected_atom = args.get(1).ok_or_else(arg_error)?; + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + let expected = interpret_no_error(self.space.clone(), expected_atom)?; + assert_alpha_equal(&actual, &expected, actual_atom) + } +} + #[derive(Clone, Debug)] pub struct AssertEqualToResultOp { space: DynSpace, @@ -191,6 +236,8 @@ pub fn register_runner_tokens(tref: &mut Tokenizer, space: &DynSpace) { tref.register_token(regex(r"assertEqual"), move |_| { assert_equal_op.clone() }); let assert_equal_to_result_op = Atom::gnd(AssertEqualToResultOp::new(space.clone())); tref.register_token(regex(r"assertEqualToResult"), move |_| { assert_equal_to_result_op.clone() }); + let assert_alpha_equal_op = Atom::gnd(AssertAlphaEqualOp::new(space.clone())); + tref.register_token(regex(r"assertAlphaEqual"), move |_| { assert_alpha_equal_op.clone() }); } #[cfg(test)] @@ -198,6 +245,7 @@ mod tests { use super::*; use crate::metta::runner::{Metta, EnvBuilder, SExprParser}; use crate::common::test_utils::metta_space; + use crate::metta::runner::stdlib::tests::run_program; use regex::Regex; @@ -229,6 +277,11 @@ mod tests { ])); } + #[test] + fn metta_assert_alpha_equal_op() { + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) (= (bar) (R $z $x)) !(assertAlphaEqual ((foo) (bar)) ((R $x $y) (R $z $a)))")), Ok(vec![vec![expr!(())]])); + } + #[test] fn metta_assert_equal_to_result_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); From 27a88e34436dd66d1bf6823049cc46f836061227 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Fri, 13 Dec 2024 08:29:53 +0300 Subject: [PATCH 2/8] =alpha added --- lib/src/metta/runner/stdlib/debug.rs | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index 03bf284ab..e0dca776e 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -5,6 +5,7 @@ use crate::space::*; use crate::common::assert::vec_eq_no_order; use crate::atom::matcher::atoms_are_equivalent; use crate::metta::runner::stdlib::{grounded_op, atom_to_string, regex, interpret_no_error, unit_result}; +use crate::metta::runner::arithmetics::Bool; use std::convert::TryInto; @@ -186,6 +187,42 @@ impl CustomExecute for AssertAlphaEqualOp { } } +#[derive(Clone, Debug)] +pub struct AlphaEqOp { + space: DynSpace, +} + +grounded_op!(AlphaEqOp, "=alpha"); + +impl AlphaEqOp { + pub fn new(space: DynSpace) -> Self { + Self{ space } + } +} + +impl Grounded for AlphaEqOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for AlphaEqOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + log::debug!("AlphaEqOp::execute: {:?}", args); + let arg_error = || ExecError::from("=alpha expects two atoms as arguments: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected_atom = args.get(1).ok_or_else(arg_error)?; + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + let expected = interpret_no_error(self.space.clone(), expected_atom)?; + Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap())))]) + } +} + #[derive(Clone, Debug)] pub struct AssertEqualToResultOp { space: DynSpace, @@ -238,6 +275,8 @@ pub fn register_runner_tokens(tref: &mut Tokenizer, space: &DynSpace) { tref.register_token(regex(r"assertEqualToResult"), move |_| { assert_equal_to_result_op.clone() }); let assert_alpha_equal_op = Atom::gnd(AssertAlphaEqualOp::new(space.clone())); tref.register_token(regex(r"assertAlphaEqual"), move |_| { assert_alpha_equal_op.clone() }); + let alpha_eq_op = Atom::gnd(AlphaEqOp::new(space.clone())); + tref.register_token(regex(r"=alpha"), move |_| { alpha_eq_op.clone() }); } #[cfg(test)] @@ -277,6 +316,12 @@ mod tests { ])); } + #[test] + fn metta_alpha_eq_op() { + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $y))")), Ok(vec![vec![expr!({Bool(true)})]])); + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $x))")), Ok(vec![vec![expr!({Bool(false)})]])); + } + #[test] fn metta_assert_alpha_equal_op() { assert_eq!(run_program(&format!("(= (foo) (R $x $y)) (= (bar) (R $z $x)) !(assertAlphaEqual ((foo) (bar)) ((R $x $y) (R $z $a)))")), Ok(vec![vec![expr!(())]])); From 042e6b20246a9f8d328cb8011f0ed7a8594b7eb8 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Fri, 13 Dec 2024 08:49:41 +0300 Subject: [PATCH 3/8] minor fix --- lib/src/metta/runner/stdlib/debug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index e0dca776e..12e9ac6d9 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -5,7 +5,7 @@ use crate::space::*; use crate::common::assert::vec_eq_no_order; use crate::atom::matcher::atoms_are_equivalent; use crate::metta::runner::stdlib::{grounded_op, atom_to_string, regex, interpret_no_error, unit_result}; -use crate::metta::runner::arithmetics::Bool; +use crate::metta::runner::arithmetics::*; use std::convert::TryInto; From 6b664d18a3a8b9d3e144a72d41dc7de470c862c6 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Fri, 13 Dec 2024 09:52:32 +0300 Subject: [PATCH 4/8] changes after recent pushes to main --- lib/src/metta/runner/stdlib/debug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index 12e9ac6d9..aed981c5f 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -5,7 +5,7 @@ use crate::space::*; use crate::common::assert::vec_eq_no_order; use crate::atom::matcher::atoms_are_equivalent; use crate::metta::runner::stdlib::{grounded_op, atom_to_string, regex, interpret_no_error, unit_result}; -use crate::metta::runner::arithmetics::*; +use crate::metta::runner::bool::*; use std::convert::TryInto; From 0823d822519b59ae825302446ed267fe89cfe7ef Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 13 Dec 2024 20:34:42 +0300 Subject: [PATCH 5/8] Add detailed diff to the assertAlphaEqual Rework vec_eq_no_order function to be able compare atoms using custom equality logic. Allow finding out whether difference is present without printing the explanation. --- lib/src/atom/matcher.rs | 5 +- lib/src/common/assert.rs | 105 ++++++++++++++++++++------- lib/src/common/collections.rs | 33 ++++++--- lib/src/metta/runner/stdlib/debug.rs | 37 ++++++---- 4 files changed, 129 insertions(+), 51 deletions(-) diff --git a/lib/src/atom/matcher.rs b/lib/src/atom/matcher.rs index fa7af10d9..93732346e 100644 --- a/lib/src/atom/matcher.rs +++ b/lib/src/atom/matcher.rs @@ -921,10 +921,7 @@ pub struct BindingsSet(smallvec::SmallVec<[Bindings; 1]>); // BindingsSets are conceptually unordered impl PartialEq for BindingsSet { fn eq(&self, other: &Self) -> bool { - match crate::common::assert::vec_eq_no_order(self.iter(), other.iter()) { - Ok(()) => true, - Err(_) => false - } + !crate::common::assert::compare_vec_no_order(self.iter(), other.iter(), crate::common::collections::DefaultEquality{}).has_diff() } } diff --git a/lib/src/common/assert.rs b/lib/src/common/assert.rs index 501f5983d..a65cc17e9 100644 --- a/lib/src/common/assert.rs +++ b/lib/src/common/assert.rs @@ -1,50 +1,105 @@ -use super::collections::ListMap; +use super::collections::{ListMap, Equality, DefaultEquality}; use std::cmp::Ordering; -pub fn vec_eq_no_order<'a, T: PartialEq + std::fmt::Debug + 'a, A: Iterator, B: Iterator>(left: A, right: B) -> Result<(), String> { - let mut left_count: ListMap<&T, usize> = ListMap::new(); - let mut right_count: ListMap<&T, usize> = ListMap::new(); +pub fn vec_eq_no_order<'a, T, A, B>(left: A, right: B) -> Option +where + T: 'a + PartialEq + std::fmt::Debug, + A: Iterator, + B: Iterator, +{ + compare_vec_no_order(left, right, DefaultEquality{}).as_string() +} + +pub fn compare_vec_no_order<'a, T, A, B, E>(left: A, right: B, _cmp: E) -> VecDiff<'a, T, E> +where + A: Iterator, + B: Iterator, + E: Equality<&'a T>, +{ + let mut left_count: ListMap<&T, usize, E> = ListMap::new(); + let mut right_count: ListMap<&T, usize, E> = ListMap::new(); for i in left { *left_count.entry(&i).or_insert(0) += 1; } for i in right { *right_count.entry(&i).or_insert(0) += 1; } - counter_eq_explanation(&left_count, &right_count) + VecDiff{ left_count, right_count } } -fn counter_eq_explanation(left: &ListMap<&T, usize>, right: &ListMap<&T, usize>) -> Result<(), String> { - for e in right.iter() { - if let Some(count) = left.get(e.0) { - match count.cmp(e.1) { - Ordering::Less => return Err(format!("Missed result: {:?}", e.0)), - Ordering::Greater => return Err(format!("Excessive result: {:?}", e.0)), - Ordering::Equal => {}, +pub struct VecDiff<'a, T, E: Equality<&'a T>> { + left_count: ListMap<&'a T, usize, E>, + right_count: ListMap<&'a T, usize, E>, +} + +trait DiffVisitor<'a, T> { + fn diff(&mut self, item: &'a T, left: usize, right: usize) -> bool; +} + +impl<'a, T: std::fmt::Debug, E: Equality<&'a T>> VecDiff<'a, T, E> { + pub fn has_diff(&self) -> bool { + #[derive(Default)] + struct FindDiff { + diff: bool, + } + impl DiffVisitor<'_, T> for FindDiff { + fn diff(&mut self, _item: &T, left: usize, right: usize) -> bool { + if left == right { + false + } else { + self.diff = true; + true + } } - } else { - return Err(format!("Missed result: {:?}", e.0)); } + let mut f = FindDiff::default(); + self.visit(&mut f); + f.diff } - for e in left.iter() { - if let Some(count) = right.get(e.0) { - match e.1.cmp(count) { - Ordering::Less => return Err(format!("Missed result: {:?}", e.0)), - Ordering::Greater => return Err(format!("Excessive result: {:?}", e.0)), - Ordering::Equal => {}, + + pub fn as_string(&self) -> Option { + #[derive(Default)] + struct StringDiff { + diff: Option, + } + impl<'a, T: std::fmt::Debug> DiffVisitor<'a, T> for StringDiff { + fn diff(&mut self, item: &'a T, left: usize, right: usize) -> bool { + match left.cmp(&right) { + Ordering::Less => { + self.diff = Some(format!("Missed result: {:?}", item)); + true + }, + Ordering::Greater => { + self.diff = Some(format!("Excessive result: {:?}", item)); + true + }, + Ordering::Equal => false, + } } - } else { - return Err(format!("Excessive result: {:?}", e.0)); + } + let mut d = StringDiff{ diff: None }; + self.visit(&mut d); + d.diff + } + + fn visit<'b, V: DiffVisitor<'b, T>>(&'b self, visitor: &mut V) { + for e in self.right_count.iter() { + let count = self.left_count.get(e.0).unwrap_or(&0); + if visitor.diff(e.0, *count, *e.1) { return } + } + for e in self.left_count.iter() { + let count = self.right_count.get(e.0).unwrap_or(&0); + if visitor.diff(e.0, *e.1, *count) { return } } } - Ok(()) } #[macro_export] macro_rules! assert_eq_no_order { ($left:expr, $right:expr) => { { - assert!($crate::common::assert::vec_eq_no_order($left.iter(), $right.iter()) == Ok(()), + assert!($crate::common::assert::vec_eq_no_order($left.iter(), $right.iter()) == None, "(left == right some order)\n left: {:?}\n right: {:?}", $left, $right); } } @@ -56,7 +111,7 @@ pub fn metta_results_eq( match (left, right) { (Ok(left), Ok(right)) if left.len() == right.len() => { for (left, right) in left.iter().zip(right.iter()) { - if let Err(_) = vec_eq_no_order(left.iter(), right.iter()) { + if vec_eq_no_order(left.iter(), right.iter()).is_some() { return false; } } diff --git a/lib/src/common/collections.rs b/lib/src/common/collections.rs index b1fb834cd..720a46f93 100644 --- a/lib/src/common/collections.rs +++ b/lib/src/common/collections.rs @@ -1,16 +1,31 @@ use std::fmt::Display; +pub trait Equality { + fn eq(a: &T, b: &T) -> bool; +} + +#[derive(Debug)] +pub struct DefaultEquality {} + +impl Equality for DefaultEquality { + fn eq(a: &T, b: &T) -> bool { + a == b + } +} + #[derive(Clone, Debug)] -pub struct ListMap { +pub struct ListMap = DefaultEquality> +{ list: Vec<(K, V)>, + _phantom: std::marker::PhantomData, } -pub enum ListMapEntry<'a, K, V> { - Occupied(K, &'a mut ListMap), - Vacant(K, &'a mut ListMap), +pub enum ListMapEntry<'a, K, V, E: Equality> { + Occupied(K, &'a mut ListMap), + Vacant(K, &'a mut ListMap), } -impl<'a, K: PartialEq, V> ListMapEntry<'a, K, V> { +impl<'a, K, V, E: Equality> ListMapEntry<'a, K, V, E> { pub fn or_insert(self, default: V) -> &'a mut V { match self { ListMapEntry::Occupied(key, map) => map.get_mut(&key).unwrap(), @@ -44,7 +59,7 @@ macro_rules! list_map_get { ($get:ident, {$( $mut_:tt )?}) => { pub fn $get(& $( $mut_ )? self, key: &K) -> Option<& $( $mut_ )? V> { for (k, v) in & $( $mut_ )? self.list { - if *k == *key { + if E::eq(k, key) { return Some(v) } } @@ -53,12 +68,12 @@ macro_rules! list_map_get { } } -impl ListMap { +impl> ListMap { pub fn new() -> Self { - Self{ list: vec![] } + Self{ list: vec![], _phantom: std::marker::PhantomData } } - pub fn entry<'a>(&'a mut self, key: K) -> ListMapEntry<'a, K, V> { + pub fn entry<'a>(&'a mut self, key: K) -> ListMapEntry<'a, K, V, E> { match self.get_mut(&key) { Some(_) => ListMapEntry::Occupied(key, self), None => ListMapEntry::Vacant(key, self) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index aed981c5f..c93a55bb4 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -2,7 +2,8 @@ use crate::*; use crate::metta::*; use crate::metta::text::Tokenizer; use crate::space::*; -use crate::common::assert::vec_eq_no_order; +use crate::common::collections::Equality; +use crate::common::assert::{vec_eq_no_order, compare_vec_no_order}; use crate::atom::matcher::atoms_are_equivalent; use crate::metta::runner::stdlib::{grounded_op, atom_to_string, regex, interpret_no_error, unit_result}; use crate::metta::runner::bool::*; @@ -14,17 +15,8 @@ fn assert_results_equal(actual: &Vec, expected: &Vec, atom: &Atom) - log::debug!("assert_results_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); match vec_eq_no_order(actual.iter(), expected.iter()) { - Ok(()) => unit_result(), - Err(diff) => Err(ExecError::Runtime(format!("{}\n{}", report, diff))) - } -} - -fn assert_alpha_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> Result, ExecError> { - log::debug!("assert_alpha_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); - let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); - match atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap()) { - true => unit_result(), - false => Err(ExecError::Runtime(format!("{}", report))) + None => unit_result(), + Some(diff) => Err(ExecError::Runtime(format!("{}\n{}", report, diff))) } } @@ -151,6 +143,25 @@ impl CustomExecute for AssertEqualOp { } } +struct AlphaEquality{} + +impl Equality<&Atom> for AlphaEquality { + fn eq(a: &&Atom, b: &&Atom) -> bool { + atoms_are_equivalent(*a, *b) + } +} + +fn assert_alpha_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> Result, ExecError> { + log::debug!("assert_alpha_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); + let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); + let res = compare_vec_no_order(actual.iter(), expected.iter(), AlphaEquality{}); + match res.as_string() { + None => unit_result(), + Some(diff) => Err(ExecError::Runtime(format!("{}\n{}", report, diff))) + } +} + + #[derive(Clone, Debug)] pub struct AssertAlphaEqualOp { space: DynSpace, @@ -391,4 +402,4 @@ mod tests { assert_eq!(TraceOp{}.execute(&mut vec![sym!("\"Here?\""), sym!("42")]), Ok(vec![sym!("42")])); } -} \ No newline at end of file +} From 800e9a124d89b03b453a0507226e57e9c6ca1189 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Tue, 17 Dec 2024 11:10:47 +0300 Subject: [PATCH 6/8] current state of asserts --- lib/src/metta/runner/stdlib/debug.rs | 251 +++++++++-------------- lib/src/metta/runner/stdlib/stdlib.metta | 55 ++++- 2 files changed, 154 insertions(+), 152 deletions(-) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index c93a55bb4..d0fc6d3d4 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -11,8 +11,7 @@ use crate::metta::runner::bool::*; use std::convert::TryInto; -fn assert_results_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> Result, ExecError> { - log::debug!("assert_results_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); +fn assert_results_equal(actual: &Vec, expected: &Vec) -> Result, ExecError> { let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); match vec_eq_no_order(actual.iter(), expected.iter()) { None => unit_result(), @@ -106,43 +105,6 @@ impl CustomExecute for PrintAlternativesOp { } } -#[derive(Clone, Debug)] -pub struct AssertEqualOp { - space: DynSpace, -} - -grounded_op!(AssertEqualOp, "assertEqual"); - -impl AssertEqualOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - -impl Grounded for AssertEqualOp { - fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) - } - - fn as_execute(&self) -> Option<&dyn CustomExecute> { - Some(self) - } -} - -impl CustomExecute for AssertEqualOp { - fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AssertEqualOp::execute: {:?}", args); - let arg_error = || ExecError::from("assertEqual expects two atoms as arguments: actual and expected"); - let actual_atom = args.get(0).ok_or_else(arg_error)?; - let expected_atom = args.get(1).ok_or_else(arg_error)?; - - let actual = interpret_no_error(self.space.clone(), actual_atom)?; - let expected = interpret_no_error(self.space.clone(), expected_atom)?; - - assert_results_equal(&actual, &expected, actual_atom) - } -} - struct AlphaEquality{} impl Equality<&Atom> for AlphaEquality { @@ -151,8 +113,7 @@ impl Equality<&Atom> for AlphaEquality { } } -fn assert_alpha_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> Result, ExecError> { - log::debug!("assert_alpha_equal: actual: {:?}, expected: {:?}, actual atom: {:?}", actual, expected, atom); +fn assert_alpha_equal(actual: &Vec, expected: &Vec) -> Result, ExecError> { let report = format!("\nExpected: {:?}\nGot: {:?}", expected, actual); let res = compare_vec_no_order(actual.iter(), expected.iter(), AlphaEquality{}); match res.as_string() { @@ -161,23 +122,15 @@ fn assert_alpha_equal(actual: &Vec, expected: &Vec, atom: &Atom) -> } } - #[derive(Clone, Debug)] -pub struct AssertAlphaEqualOp { - space: DynSpace, +struct AssertResultsEqual { } -grounded_op!(AssertAlphaEqualOp, "assertAlphaEqual"); +grounded_op!(AssertResultsEqual, "assertResultsEqual"); -impl AssertAlphaEqualOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - -impl Grounded for AssertAlphaEqualOp { +impl Grounded for AssertResultsEqual { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -185,35 +138,27 @@ impl Grounded for AssertAlphaEqualOp { } } -impl CustomExecute for AssertAlphaEqualOp { +impl CustomExecute for AssertResultsEqual { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AssertAlphaEqualOp::execute: {:?}", args); - let arg_error = || ExecError::from("assertAlphaEqual expects two atoms as arguments: actual and expected"); - let actual_atom = args.get(0).ok_or_else(arg_error)?; - let expected_atom = args.get(1).ok_or_else(arg_error)?; + log::debug!("AssertResultsEqual::execute: {:?}", args); + let arg_error = || ExecError::from("assertResultsEqual expects three arguments: two expressions (actual and expected) and atom (actual in form of atom) arguments"); + let actual: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into(); + let expected: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().into(); - let actual = interpret_no_error(self.space.clone(), actual_atom)?; - let expected = interpret_no_error(self.space.clone(), expected_atom)?; - assert_alpha_equal(&actual, &expected, actual_atom) + assert_results_equal(&actual, &expected) } } + #[derive(Clone, Debug)] -pub struct AlphaEqOp { - space: DynSpace, +struct AssertResultsAlphaEqual { } -grounded_op!(AlphaEqOp, "=alpha"); +grounded_op!(AssertResultsAlphaEqual, "assertResultsAlphaEqual"); -impl AlphaEqOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - -impl Grounded for AlphaEqOp { +impl Grounded for AssertResultsAlphaEqual { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -221,33 +166,31 @@ impl Grounded for AlphaEqOp { } } -impl CustomExecute for AlphaEqOp { +impl CustomExecute for AssertResultsAlphaEqual { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AlphaEqOp::execute: {:?}", args); - let arg_error = || ExecError::from("=alpha expects two atoms as arguments: actual and expected"); - let actual_atom = args.get(0).ok_or_else(arg_error)?; - let expected_atom = args.get(1).ok_or_else(arg_error)?; + log::debug!("AssertResultsAlphaEqual::execute: {:?}", args); + let arg_error = || ExecError::from("assertResultsAlphaEqual expects three arguments: two expressions (actual and expected) and atom (actual in form of atom) arguments"); + let actual: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into(); + let expected: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().into(); - let actual = interpret_no_error(self.space.clone(), actual_atom)?; - let expected = interpret_no_error(self.space.clone(), expected_atom)?; - Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap())))]) + assert_alpha_equal(&actual, &expected) } } #[derive(Clone, Debug)] -pub struct AssertEqualToResultOp { +pub struct AlphaEqOp { space: DynSpace, } -grounded_op!(AssertEqualToResultOp, "assertEqualToResult"); +grounded_op!(AlphaEqOp, "=alpha"); -impl AssertEqualToResultOp { +impl AlphaEqOp { pub fn new(space: DynSpace) -> Self { Self{ space } } } -impl Grounded for AssertEqualToResultOp { +impl Grounded for AlphaEqOp { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } @@ -257,18 +200,16 @@ impl Grounded for AssertEqualToResultOp { } } -impl CustomExecute for AssertEqualToResultOp { +impl CustomExecute for AlphaEqOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AssertEqualToResultOp::execute: {:?}", args); - let arg_error = || ExecError::from("assertEqualToResult expects two atoms as arguments: actual and expected"); + log::debug!("AlphaEqOp::execute: {:?}", args); + let arg_error = || ExecError::from("=alpha expects two atoms as arguments: actual and expected"); let actual_atom = args.get(0).ok_or_else(arg_error)?; - let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?) - .map_err(|_| arg_error())? - .children(); + let expected_atom = args.get(1).ok_or_else(arg_error)?; let actual = interpret_no_error(self.space.clone(), actual_atom)?; - - assert_results_equal(&actual, &expected.into(), actual_atom) + let expected = interpret_no_error(self.space.clone(), expected_atom)?; + Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap())))]) } } @@ -280,12 +221,10 @@ pub fn register_common_tokens(tref: &mut Tokenizer) { } pub fn register_runner_tokens(tref: &mut Tokenizer, space: &DynSpace) { - let assert_equal_op = Atom::gnd(AssertEqualOp::new(space.clone())); - tref.register_token(regex(r"assertEqual"), move |_| { assert_equal_op.clone() }); - let assert_equal_to_result_op = Atom::gnd(AssertEqualToResultOp::new(space.clone())); - tref.register_token(regex(r"assertEqualToResult"), move |_| { assert_equal_to_result_op.clone() }); - let assert_alpha_equal_op = Atom::gnd(AssertAlphaEqualOp::new(space.clone())); - tref.register_token(regex(r"assertAlphaEqual"), move |_| { assert_alpha_equal_op.clone() }); + let assert_results_alpha_equal_op = Atom::gnd(AssertResultsAlphaEqual{}); + tref.register_token(regex(r"assertResultsAlphaEqual"), move |_| { assert_results_alpha_equal_op.clone() }); + let assert_results_equal_op = Atom::gnd(AssertResultsEqual{}); + tref.register_token(regex(r"assertResultsEqual"), move |_| { assert_results_equal_op.clone() }); let alpha_eq_op = Atom::gnd(AlphaEqOp::new(space.clone())); tref.register_token(regex(r"=alpha"), move |_| { alpha_eq_op.clone() }); } @@ -310,7 +249,6 @@ mod tests { #[test] fn metta_assert_equal_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); - let assert = AssertEqualOp::new(metta.space().clone()); let program = " (= (foo $x) $x) (= (bar $x) $x) @@ -320,28 +258,41 @@ mod tests { vec![UNIT_ATOM], ])); assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) (bar B))")), Ok(vec![ - vec![expr!("Error" ({assert.clone()} ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], + vec![expr!("Error" ("assertEqual" ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], ])); assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) Empty)")), Ok(vec![ - vec![expr!("Error" ({assert.clone()} ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] + vec![expr!("Error" ("assertEqual" ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] ])); } #[test] - fn metta_alpha_eq_op() { - assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $y))")), Ok(vec![vec![expr!({Bool(true)})]])); - assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $x))")), Ok(vec![vec![expr!({Bool(false)})]])); + fn metta_assert_alpha_equal_op() { + let metta = Metta::new(Some(EnvBuilder::test_env())); + let program = " + (= (foo $x) $x) + (= (bar $x) $x) + "; + assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo $x) (bar $x))")), Ok(vec![ + vec![UNIT_ATOM], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) (bar B))")), Ok(vec![ + vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) Empty)")), Ok(vec![ + vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] + ])); } #[test] - fn metta_assert_alpha_equal_op() { - assert_eq!(run_program(&format!("(= (foo) (R $x $y)) (= (bar) (R $z $x)) !(assertAlphaEqual ((foo) (bar)) ((R $x $y) (R $z $a)))")), Ok(vec![vec![expr!(())]])); + fn metta_alpha_eq_op() { + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $y))")), Ok(vec![vec![expr!({Bool(true)})]])); + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $x))")), Ok(vec![vec![expr!({Bool(false)})]])); } #[test] fn metta_assert_equal_to_result_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); - let assert = AssertEqualToResultOp::new(metta.space().clone()); let program = " (= (foo) A) (= (foo) B) @@ -353,49 +304,49 @@ mod tests { assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (foo) (A B))")), Ok(vec![ vec![UNIT_ATOM], ])); - assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (bar) (A))")), Ok(vec![ - vec![expr!("Error" ({assert.clone()} ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], - ])); - assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (baz) (D))")), Ok(vec![ - vec![expr!("Error" ({assert.clone()} ("baz") ("D")) "\nExpected: [D]\nGot: [D, D]\nExcessive result: D")] - ])); - } - - #[test] - fn assert_equal_op() { - let space = DynSpace::new(metta_space(" - (= (foo) (A B)) - (= (foo) (B C)) - (= (bar) (B C)) - (= (bar) (A B)) - (= (err) (A B)) - ")); - - let assert_equal_op = AssertEqualOp::new(space); - - assert_eq!(assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]), unit_result()); - - let actual = assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("err"))]); - let expected = Regex::new("\nExpected: \\[(A B)\\]\nGot: \\[\\((B C)|, |(A B)\\){3}\\]\nExcessive result: (B C)").unwrap(); - assert_runtime_error(actual, expected); - - let actual = assert_equal_op.execute(&mut vec![expr!(("err")), expr!(("foo"))]); - let expected = Regex::new("\nExpected: \\[\\((B C)|, |(A B)\\){3}\\]\nGot: \\[(A B)\\]\nMissed result: (B C)").unwrap(); - assert_runtime_error(actual, expected); - } - - #[test] - fn assert_equal_to_result_op() { - let space = DynSpace::new(metta_space(" - (= (foo) (A B)) - (= (foo) (B C)) - ")); - let assert_equal_to_result_op = AssertEqualToResultOp::new(space); - - assert_eq!(assert_equal_to_result_op.execute(&mut vec![ - expr!(("foo")), expr!(("B" "C") ("A" "B"))]), - unit_result()); - } + // assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (bar) (A))")), Ok(vec![ + // vec![expr!("Error" ("assertEqualToResult" ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], + // ])); + // assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (baz) (D))")), Ok(vec![ + // vec![expr!("Error" ("assertEqualToResult" ("baz") ("D")) "\nExpected: [D]\nGot: [D, D]\nExcessive result: D")] + // ])); + } + + // #[test] + // fn assert_equal_op() { + // let space = DynSpace::new(metta_space(" + // (= (foo) (A B)) + // (= (foo) (B C)) + // (= (bar) (B C)) + // (= (bar) (A B)) + // (= (err) (A B)) + // ")); + // + // let assert_equal_op = AssertEqualOp::new(space); + // + // assert_eq!(assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]), unit_result()); + // + // let actual = assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("err"))]); + // let expected = Regex::new("\nExpected: \\[(A B)\\]\nGot: \\[\\((B C)|, |(A B)\\){3}\\]\nExcessive result: (B C)").unwrap(); + // assert_runtime_error(actual, expected); + // + // let actual = assert_equal_op.execute(&mut vec![expr!(("err")), expr!(("foo"))]); + // let expected = Regex::new("\nExpected: \\[\\((B C)|, |(A B)\\){3}\\]\nGot: \\[(A B)\\]\nMissed result: (B C)").unwrap(); + // assert_runtime_error(actual, expected); + // } + + // #[test] + // fn assert_equal_to_result_op() { + // let space = DynSpace::new(metta_space(" + // (= (foo) (A B)) + // (= (foo) (B C)) + // ")); + // let assert_equal_to_result_op = AssertEqualToResultOp::new(space); + // + // assert_eq!(assert_equal_to_result_op.execute(&mut vec![ + // expr!(("foo")), expr!(("B" "C") ("A" "B"))]), + // unit_result()); + // } #[test] fn trace_op() { diff --git a/lib/src/metta/runner/stdlib/stdlib.metta b/lib/src/metta/runner/stdlib/stdlib.metta index 43fe597e4..e7cf0e360 100644 --- a/lib/src/metta/runner/stdlib/stdlib.metta +++ b/lib/src/metta/runner/stdlib/stdlib.metta @@ -974,19 +974,70 @@ (@params ()) (@return "Unit atom")) +(@doc =alpha + (@desc "Checks alpha equality of two expressions") + (@params ( + (@param "First expression") + (@param "Second expression"))) + (@return "True if both expressions are alpha equal, False - otherwise")) + (@doc assertEqual (@desc "Compares (sets of) results of evaluation of two expressions") (@params ( (@param "First expression") (@param "Second expression"))) - (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + (@return "Unit atom if both expressions after evaluation are equal, error - otherwise")) +(: assertEqual (-> Atom Atom (->))) +(= (assertEqual $actual-atom $expected-atom) + (let $actual (collapse $actual-atom) + (let $expected (collapse $expected-atom) + (chain (eval (assertResultsEqual $actual $expected)) $res + (if-error $res + (unify (Error $_ $msg) $res (Error (assertEqual $actual-atom $expected-atom) $msg) $res) + $res))))) + +(@doc assertAlphaEqual + (@desc "Compares (sets of) results of evaluation of two expressions using alpha equality") + (@params ( + (@param "First expression") + (@param "Second expression"))) + (@return "Unit atom if both expressions after evaluation are alpha equal, error - otherwise")) +(: assertAlphaEqual (-> Atom Atom (->))) +(= (assertAlphaEqual $actual-atom $expected-atom) + (let $actual (collapse $actual-atom) + (let $expected (collapse $expected-atom) + (chain (eval (assertResultsAlphaEqual $actual $expected)) $res + (if-error $res + (unify (Error $_ $msg) $res (Error (assertAlphaEqual $actual-atom $expected-atom) $msg) $res) + $res))))) (@doc assertEqualToResult (@desc "Same as assertEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") (@params ( (@param "First expression (it will be evaluated)") (@param "Second expression (it won't be evaluated)"))) - (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + (@return "Unit atom if both expressions after evaluation of the first argument are equal, error - otherwise")) +(: assertEqualToResult (-> Atom Atom (->))) +(= (assertEqualToResult $actual-atom $expected) + (let $actual (collapse $actual-atom) + (chain (eval (assertResultsEqual $actual $expected)) $res + (if-error $res + (unify (Error $_ $msg) $res (Error (assertEqualToResult $actual-atom $expected) $msg) $res) + $res)))) + +(@doc assertAlphaEqualToResult + (@desc "Same as assertAlphaEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") + (@params ( + (@param "First expression (it will be evaluated)") + (@param "Second expression (it won't be evaluated)"))) + (@return "Unit atom if both expressions after evaluation of the first argument are alpha equal, error - otherwise")) +(: assertAlphaEqualToResult (-> Atom Atom (->))) +(= (assertAlphaEqualToResult $actual-atom $expected) + (let $actual (collapse $actual-atom) + (chain (eval (assertResultsAlphaEqual $actual $expected)) $res + (if-error $res + (unify (Error $_ $msg) $res (Error (assertAlphaEqualToResult $actual-atom $expected) $msg) $res) + $res)))) (@doc superpose (@desc "Turns a tuple (first argument) into a nondeterministic result") From 67e17f9ca4079a953180f300d8887adae30a6c26 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Wed, 18 Dec 2024 11:16:00 +0300 Subject: [PATCH 7/8] asserts once again on Rust --- lib/src/metta/runner/stdlib/debug.rs | 272 +++++++++++++++++------ lib/src/metta/runner/stdlib/stdlib.metta | 30 --- 2 files changed, 202 insertions(+), 100 deletions(-) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index d0fc6d3d4..2f025e9e4 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -123,14 +123,21 @@ fn assert_alpha_equal(actual: &Vec, expected: &Vec) -> Result Self { + Self{ space } + } +} + +impl Grounded for AssertEqualOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -138,27 +145,36 @@ impl Grounded for AssertResultsEqual { } } -impl CustomExecute for AssertResultsEqual { +impl CustomExecute for AssertEqualOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AssertResultsEqual::execute: {:?}", args); - let arg_error = || ExecError::from("assertResultsEqual expects three arguments: two expressions (actual and expected) and atom (actual in form of atom) arguments"); - let actual: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into(); - let expected: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().into(); + log::debug!("AssertEqualOp::execute: {:?}", args); + let arg_error = || ExecError::from("assertEqual expects two atoms: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected_atom = args.get(1).ok_or_else(arg_error)?; + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + let expected = interpret_no_error(self.space.clone(), expected_atom)?; assert_results_equal(&actual, &expected) } } - #[derive(Clone, Debug)] -struct AssertResultsAlphaEqual { +pub struct AssertAlphaEqualOp { + space: DynSpace, } -grounded_op!(AssertResultsAlphaEqual, "assertResultsAlphaEqual"); +grounded_op!(AssertAlphaEqualOp, "assertAlphaEqual"); -impl Grounded for AssertResultsAlphaEqual { +impl AssertAlphaEqualOp { + pub fn new(space: DynSpace) -> Self { + Self{ space } + } +} + +impl Grounded for AssertAlphaEqualOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -166,12 +182,15 @@ impl Grounded for AssertResultsAlphaEqual { } } -impl CustomExecute for AssertResultsAlphaEqual { +impl CustomExecute for AssertAlphaEqualOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - log::debug!("AssertResultsAlphaEqual::execute: {:?}", args); - let arg_error = || ExecError::from("assertResultsAlphaEqual expects three arguments: two expressions (actual and expected) and atom (actual in form of atom) arguments"); - let actual: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().into(); - let expected: Vec = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().into(); + log::debug!("AssertAlphaEqualOp::execute: {:?}", args); + let arg_error = || ExecError::from("assertAlphaEqual expects two atoms: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected_atom = args.get(1).ok_or_else(arg_error)?; + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + let expected = interpret_no_error(self.space.clone(), expected_atom)?; assert_alpha_equal(&actual, &expected) } @@ -213,6 +232,83 @@ impl CustomExecute for AlphaEqOp { } } +#[derive(Clone, Debug)] +pub struct AssertEqualToResultOp { + space: DynSpace, +} + +grounded_op!(AssertEqualToResultOp, "assertEqualToResult"); + +impl AssertEqualToResultOp { + pub fn new(space: DynSpace) -> Self { + Self{ space } + } +} + +impl Grounded for AssertEqualToResultOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for AssertEqualToResultOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + log::debug!("AssertEqualToResultOp::execute: {:?}", args); + let arg_error = || ExecError::from("assertEqualToResult expects two atoms as arguments: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?) + .map_err(|_| arg_error())? + .children(); + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + + assert_results_equal(&actual, &expected.into()) + } +} + +#[derive(Clone, Debug)] +pub struct AssertAlphaEqualToResultOp { + space: DynSpace, +} + +grounded_op!(AssertAlphaEqualToResultOp, "assertAlphaEqualToResult"); + +impl AssertAlphaEqualToResultOp { + pub fn new(space: DynSpace) -> Self { + Self{ space } + } +} + +impl Grounded for AssertAlphaEqualToResultOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for AssertAlphaEqualToResultOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + log::debug!("AssertAlphaEqualToResultOp::execute: {:?}", args); + let arg_error = || ExecError::from("assertAlphaEqualToResultOp expects two atoms as arguments: actual and expected"); + let actual_atom = args.get(0).ok_or_else(arg_error)?; + let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?) + .map_err(|_| arg_error())? + .children(); + + let actual = interpret_no_error(self.space.clone(), actual_atom)?; + + assert_alpha_equal(&actual, &expected.into()) + } +} + + pub fn register_common_tokens(tref: &mut Tokenizer) { let trace_op = Atom::gnd(TraceOp{}); tref.register_token(regex(r"trace!"), move |_| { trace_op.clone() }); @@ -221,10 +317,18 @@ pub fn register_common_tokens(tref: &mut Tokenizer) { } pub fn register_runner_tokens(tref: &mut Tokenizer, space: &DynSpace) { - let assert_results_alpha_equal_op = Atom::gnd(AssertResultsAlphaEqual{}); - tref.register_token(regex(r"assertResultsAlphaEqual"), move |_| { assert_results_alpha_equal_op.clone() }); - let assert_results_equal_op = Atom::gnd(AssertResultsEqual{}); - tref.register_token(regex(r"assertResultsEqual"), move |_| { assert_results_equal_op.clone() }); + let assert_alpha_equal_to_result_op = Atom::gnd(AssertAlphaEqualToResultOp::new(space.clone())); + tref.register_token(regex(r"assertAlphaEqualToResult"), move |_| { assert_alpha_equal_to_result_op.clone() }); + + let assert_equal_to_result_op = Atom::gnd(AssertEqualToResultOp::new(space.clone())); + tref.register_token(regex(r"assertEqualToResult"), move |_| { assert_equal_to_result_op.clone() }); + + let assert_alpha_equal_op = Atom::gnd(AssertAlphaEqualOp::new(space.clone())); + tref.register_token(regex(r"assertAlphaEqual"), move |_| { assert_alpha_equal_op.clone() }); + + let assert_equal_op = Atom::gnd(AssertEqualOp::new(space.clone())); + tref.register_token(regex(r"assertEqual"), move |_| { assert_equal_op.clone() }); + let alpha_eq_op = Atom::gnd(AlphaEqOp::new(space.clone())); tref.register_token(regex(r"=alpha"), move |_| { alpha_eq_op.clone() }); } @@ -249,6 +353,7 @@ mod tests { #[test] fn metta_assert_equal_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); + let assert = AssertEqualOp::new(metta.space().clone()); let program = " (= (foo $x) $x) (= (bar $x) $x) @@ -258,16 +363,17 @@ mod tests { vec![UNIT_ATOM], ])); assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) (bar B))")), Ok(vec![ - vec![expr!("Error" ("assertEqual" ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], + vec![expr!("Error" ({assert.clone()} ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], ])); assert_eq!(metta.run(SExprParser::new("!(assertEqual (foo A) Empty)")), Ok(vec![ - vec![expr!("Error" ("assertEqual" ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] + vec![expr!("Error" ({assert.clone()} ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] ])); } #[test] fn metta_assert_alpha_equal_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); + let assert = AssertAlphaEqualOp::new(metta.space().clone()); let program = " (= (foo $x) $x) (= (bar $x) $x) @@ -277,10 +383,10 @@ mod tests { vec![UNIT_ATOM], ])); assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) (bar B))")), Ok(vec![ - vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], + vec![expr!("Error" ({assert.clone()} ("foo" "A") ("bar" "B")) "\nExpected: [B]\nGot: [A]\nMissed result: B")], ])); assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqual (foo A) Empty)")), Ok(vec![ - vec![expr!("Error" ("assertAlphaEqual" ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] + vec![expr!("Error" ({assert.clone()} ("foo" "A") "Empty") "\nExpected: []\nGot: [A]\nExcessive result: A")] ])); } @@ -293,6 +399,7 @@ mod tests { #[test] fn metta_assert_equal_to_result_op() { let metta = Metta::new(Some(EnvBuilder::test_env())); + let assert = AssertEqualToResultOp::new(metta.space().clone()); let program = " (= (foo) A) (= (foo) B) @@ -304,49 +411,74 @@ mod tests { assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (foo) (A B))")), Ok(vec![ vec![UNIT_ATOM], ])); - // assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (bar) (A))")), Ok(vec![ - // vec![expr!("Error" ("assertEqualToResult" ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], - // ])); - // assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (baz) (D))")), Ok(vec![ - // vec![expr!("Error" ("assertEqualToResult" ("baz") ("D")) "\nExpected: [D]\nGot: [D, D]\nExcessive result: D")] - // ])); - } - - // #[test] - // fn assert_equal_op() { - // let space = DynSpace::new(metta_space(" - // (= (foo) (A B)) - // (= (foo) (B C)) - // (= (bar) (B C)) - // (= (bar) (A B)) - // (= (err) (A B)) - // ")); - // - // let assert_equal_op = AssertEqualOp::new(space); - // - // assert_eq!(assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]), unit_result()); - // - // let actual = assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("err"))]); - // let expected = Regex::new("\nExpected: \\[(A B)\\]\nGot: \\[\\((B C)|, |(A B)\\){3}\\]\nExcessive result: (B C)").unwrap(); - // assert_runtime_error(actual, expected); - // - // let actual = assert_equal_op.execute(&mut vec![expr!(("err")), expr!(("foo"))]); - // let expected = Regex::new("\nExpected: \\[\\((B C)|, |(A B)\\){3}\\]\nGot: \\[(A B)\\]\nMissed result: (B C)").unwrap(); - // assert_runtime_error(actual, expected); - // } - - // #[test] - // fn assert_equal_to_result_op() { - // let space = DynSpace::new(metta_space(" - // (= (foo) (A B)) - // (= (foo) (B C)) - // ")); - // let assert_equal_to_result_op = AssertEqualToResultOp::new(space); - // - // assert_eq!(assert_equal_to_result_op.execute(&mut vec![ - // expr!(("foo")), expr!(("B" "C") ("A" "B"))]), - // unit_result()); - // } + assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (bar) (A))")), Ok(vec![ + vec![expr!("Error" ({assert.clone()} ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertEqualToResult (baz) (D))")), Ok(vec![ + vec![expr!("Error" ({assert.clone()} ("baz") ("D")) "\nExpected: [D]\nGot: [D, D]\nExcessive result: D")] + ])); + } + + #[test] + fn metta_assert_alpha_equal_to_result_op() { + let metta = Metta::new(Some(EnvBuilder::test_env())); + let assert = AssertAlphaEqualToResultOp::new(metta.space().clone()); + let program = " + (= (foo) $x) + (= (bar) C) + (= (baz) D) + (= (baz) D) + "; + assert_eq!(metta.run(SExprParser::new(program)), Ok(vec![])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (foo) ($x))")), Ok(vec![ + vec![UNIT_ATOM], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult ((foo) (foo)) (($x $y)))")), Ok(vec![ + vec![UNIT_ATOM], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (bar) (A))")), Ok(vec![ + vec![expr!("Error" ({assert.clone()} ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], + ])); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (baz) (D))")), Ok(vec![ + vec![expr!("Error" ({assert.clone()} ("baz") ("D")) "\nExpected: [D]\nGot: [D, D]\nExcessive result: D")] + ])); + } + + #[test] + fn assert_equal_op() { + let space = DynSpace::new(metta_space(" + (= (foo) (A B)) + (= (foo) (B C)) + (= (bar) (B C)) + (= (bar) (A B)) + (= (err) (A B)) + ")); + + let assert_equal_op = AssertEqualOp::new(space); + + assert_eq!(assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]), unit_result()); + + let actual = assert_equal_op.execute(&mut vec![expr!(("foo")), expr!(("err"))]); + let expected = Regex::new("\nExpected: \\[(A B)\\]\nGot: \\[\\((B C)|, |(A B)\\){3}\\]\nExcessive result: (B C)").unwrap(); + assert_runtime_error(actual, expected); + + let actual = assert_equal_op.execute(&mut vec![expr!(("err")), expr!(("foo"))]); + let expected = Regex::new("\nExpected: \\[\\((B C)|, |(A B)\\){3}\\]\nGot: \\[(A B)\\]\nMissed result: (B C)").unwrap(); + assert_runtime_error(actual, expected); + } + + #[test] + fn assert_equal_to_result_op() { + let space = DynSpace::new(metta_space(" + (= (foo) (A B)) + (= (foo) (B C)) + ")); + let assert_equal_to_result_op = AssertEqualToResultOp::new(space); + + assert_eq!(assert_equal_to_result_op.execute(&mut vec![ + expr!(("foo")), expr!(("B" "C") ("A" "B"))]), + unit_result()); + } #[test] fn trace_op() { diff --git a/lib/src/metta/runner/stdlib/stdlib.metta b/lib/src/metta/runner/stdlib/stdlib.metta index e7cf0e360..28ef2a7de 100644 --- a/lib/src/metta/runner/stdlib/stdlib.metta +++ b/lib/src/metta/runner/stdlib/stdlib.metta @@ -987,14 +987,6 @@ (@param "First expression") (@param "Second expression"))) (@return "Unit atom if both expressions after evaluation are equal, error - otherwise")) -(: assertEqual (-> Atom Atom (->))) -(= (assertEqual $actual-atom $expected-atom) - (let $actual (collapse $actual-atom) - (let $expected (collapse $expected-atom) - (chain (eval (assertResultsEqual $actual $expected)) $res - (if-error $res - (unify (Error $_ $msg) $res (Error (assertEqual $actual-atom $expected-atom) $msg) $res) - $res))))) (@doc assertAlphaEqual (@desc "Compares (sets of) results of evaluation of two expressions using alpha equality") @@ -1002,14 +994,6 @@ (@param "First expression") (@param "Second expression"))) (@return "Unit atom if both expressions after evaluation are alpha equal, error - otherwise")) -(: assertAlphaEqual (-> Atom Atom (->))) -(= (assertAlphaEqual $actual-atom $expected-atom) - (let $actual (collapse $actual-atom) - (let $expected (collapse $expected-atom) - (chain (eval (assertResultsAlphaEqual $actual $expected)) $res - (if-error $res - (unify (Error $_ $msg) $res (Error (assertAlphaEqual $actual-atom $expected-atom) $msg) $res) - $res))))) (@doc assertEqualToResult (@desc "Same as assertEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") @@ -1017,13 +1001,6 @@ (@param "First expression (it will be evaluated)") (@param "Second expression (it won't be evaluated)"))) (@return "Unit atom if both expressions after evaluation of the first argument are equal, error - otherwise")) -(: assertEqualToResult (-> Atom Atom (->))) -(= (assertEqualToResult $actual-atom $expected) - (let $actual (collapse $actual-atom) - (chain (eval (assertResultsEqual $actual $expected)) $res - (if-error $res - (unify (Error $_ $msg) $res (Error (assertEqualToResult $actual-atom $expected) $msg) $res) - $res)))) (@doc assertAlphaEqualToResult (@desc "Same as assertAlphaEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") @@ -1031,13 +1008,6 @@ (@param "First expression (it will be evaluated)") (@param "Second expression (it won't be evaluated)"))) (@return "Unit atom if both expressions after evaluation of the first argument are alpha equal, error - otherwise")) -(: assertAlphaEqualToResult (-> Atom Atom (->))) -(= (assertAlphaEqualToResult $actual-atom $expected) - (let $actual (collapse $actual-atom) - (chain (eval (assertResultsAlphaEqual $actual $expected)) $res - (if-error $res - (unify (Error $_ $msg) $res (Error (assertAlphaEqualToResult $actual-atom $expected) $msg) $res) - $res)))) (@doc superpose (@desc "Turns a tuple (first argument) into a nondeterministic result") From 19c783121f43f3f2be56e3cfe983b4900f4fe5bd Mon Sep 17 00:00:00 2001 From: Innokenty Date: Wed, 18 Dec 2024 13:28:33 +0300 Subject: [PATCH 8/8] fixes after Vitaly's review --- lib/src/metta/runner/stdlib/debug.rs | 38 +++++++++++++--------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/src/metta/runner/stdlib/debug.rs b/lib/src/metta/runner/stdlib/debug.rs index 2f025e9e4..326b04abe 100644 --- a/lib/src/metta/runner/stdlib/debug.rs +++ b/lib/src/metta/runner/stdlib/debug.rs @@ -137,7 +137,7 @@ impl AssertEqualOp { impl Grounded for AssertEqualOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, UNIT_TYPE]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -174,7 +174,7 @@ impl AssertAlphaEqualOp { impl Grounded for AssertAlphaEqualOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, UNIT_TYPE]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -198,20 +198,12 @@ impl CustomExecute for AssertAlphaEqualOp { #[derive(Clone, Debug)] pub struct AlphaEqOp { - space: DynSpace, } - grounded_op!(AlphaEqOp, "=alpha"); -impl AlphaEqOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - impl Grounded for AlphaEqOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_BOOL]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -226,9 +218,7 @@ impl CustomExecute for AlphaEqOp { let actual_atom = args.get(0).ok_or_else(arg_error)?; let expected_atom = args.get(1).ok_or_else(arg_error)?; - let actual = interpret_no_error(self.space.clone(), actual_atom)?; - let expected = interpret_no_error(self.space.clone(), expected_atom)?; - Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual.get(0).unwrap(), expected.get(0).unwrap())))]) + Ok(vec![Atom::gnd(Bool(atoms_are_equivalent(actual_atom, expected_atom)))]) } } @@ -247,7 +237,7 @@ impl AssertEqualToResultOp { impl Grounded for AssertEqualToResultOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, UNIT_TYPE]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -258,7 +248,7 @@ impl Grounded for AssertEqualToResultOp { impl CustomExecute for AssertEqualToResultOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { log::debug!("AssertEqualToResultOp::execute: {:?}", args); - let arg_error = || ExecError::from("assertEqualToResult expects two atoms as arguments: actual and expected"); + let arg_error = || ExecError::from("assertEqualToResult expects atom and expression as arguments: actual and expected"); let actual_atom = args.get(0).ok_or_else(arg_error)?; let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?) .map_err(|_| arg_error())? @@ -285,7 +275,7 @@ impl AssertAlphaEqualToResultOp { impl Grounded for AssertAlphaEqualToResultOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, UNIT_TYPE]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -296,7 +286,7 @@ impl Grounded for AssertAlphaEqualToResultOp { impl CustomExecute for AssertAlphaEqualToResultOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { log::debug!("AssertAlphaEqualToResultOp::execute: {:?}", args); - let arg_error = || ExecError::from("assertAlphaEqualToResultOp expects two atoms as arguments: actual and expected"); + let arg_error = || ExecError::from("assertAlphaEqualToResultOp expects atom and expression as arguments: actual and expected"); let actual_atom = args.get(0).ok_or_else(arg_error)?; let expected = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?) .map_err(|_| arg_error())? @@ -329,7 +319,7 @@ pub fn register_runner_tokens(tref: &mut Tokenizer, space: &DynSpace) { let assert_equal_op = Atom::gnd(AssertEqualOp::new(space.clone())); tref.register_token(regex(r"assertEqual"), move |_| { assert_equal_op.clone() }); - let alpha_eq_op = Atom::gnd(AlphaEqOp::new(space.clone())); + let alpha_eq_op = Atom::gnd(AlphaEqOp{}); tref.register_token(regex(r"=alpha"), move |_| { alpha_eq_op.clone() }); } @@ -392,8 +382,8 @@ mod tests { #[test] fn metta_alpha_eq_op() { - assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $y))")), Ok(vec![vec![expr!({Bool(true)})]])); - assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(=alpha (foo) (R $x $x))")), Ok(vec![vec![expr!({Bool(false)})]])); + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(let $foo (eval (foo)) (=alpha $foo (R $x $y)))")), Ok(vec![vec![expr!({Bool(true)})]])); + assert_eq!(run_program(&format!("(= (foo) (R $x $y)) !(let $foo (eval (foo)) (=alpha $foo (R $x $x)))")), Ok(vec![vec![expr!({Bool(false)})]])); } #[test] @@ -436,6 +426,12 @@ mod tests { assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult ((foo) (foo)) (($x $y)))")), Ok(vec![ vec![UNIT_ATOM], ])); + + let res = metta.run(SExprParser::new("!(assertAlphaEqualToResult ((foo) (foo)) (($x $x)))")).unwrap(); + let res_first_atom = res.get(0).unwrap().get(0); + assert_eq!(res_first_atom.unwrap().iter().next().unwrap(), &sym!("Error")); + assert_eq!(res.get(0).unwrap().len(), 1); + assert_eq!(metta.run(SExprParser::new("!(assertAlphaEqualToResult (bar) (A))")), Ok(vec![ vec![expr!("Error" ({assert.clone()} ("bar") ("A")) "\nExpected: [A]\nGot: [C]\nMissed result: A")], ]));