diff --git a/c/src/atom.rs b/c/src/atom.rs index c81e20b96..d2f9391e8 100644 --- a/c/src/atom.rs +++ b/c/src/atom.rs @@ -599,22 +599,10 @@ impl Grounded for CGrounded { unsafe{ &(*self.get_ptr()).typ }.borrow().clone() } - fn execute(&self, args: &[Atom]) -> Result, ExecError> { + fn as_execute(&self) -> Option<&dyn CustomExecute> { match self.api().execute { - Some(func) => { - let mut ret = atom_vec_t::new(); - let c_args: atom_vec_t = args.into(); - let error = func(self.get_ptr(), &c_args, &mut ret); - let ret = if error.is_no_err() { - Ok(ret.into()) - } else { - let error = error.into_inner(); - Err(error) - }; - log::trace!("CGrounded::execute: atom: {:?}, args: {:?}, ret: {:?}", self, args, ret); - ret - }, - None => execute_not_executable(self) + None => None, + Some(_) => Some(self), } } @@ -636,6 +624,27 @@ impl Grounded for CGrounded { } } +impl CustomExecute for CGrounded { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + match self.api().execute { + Some(func) => { + let mut ret = atom_vec_t::new(); + let c_args: atom_vec_t = args.into(); + let error = func(self.get_ptr(), &c_args, &mut ret); + let ret = if error.is_no_err() { + Ok(ret.into()) + } else { + let error = error.into_inner(); + Err(error) + }; + log::trace!("CGrounded::execute: atom: {:?}, args: {:?}, ret: {:?}", self, args, ret); + ret + }, + None => panic!("CustomExecute implementation should not be changed in runtime"), + } + } +} + impl CustomMatch for CGrounded { fn match_(&self, other: &Atom) -> matcher::MatchResultIter { match self.api().match_ { diff --git a/lib/examples/custom_match.rs b/lib/examples/custom_match.rs index d12e46128..d3cc34516 100644 --- a/lib/examples/custom_match.rs +++ b/lib/examples/custom_match.rs @@ -28,9 +28,6 @@ impl Grounded for TestDict { fn type_(&self) -> Atom { Atom::sym("Dict") } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } diff --git a/lib/src/atom/matcher.rs b/lib/src/atom/matcher.rs index d85cdb529..6fd92267f 100644 --- a/lib/src/atom/matcher.rs +++ b/lib/src/atom/matcher.rs @@ -1441,9 +1441,6 @@ mod test { fn type_(&self) -> Atom { Atom::sym("Rand") } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } @@ -1491,9 +1488,6 @@ mod test { fn type_(&self) -> Atom { Atom::sym("ReturnPairInX") } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } @@ -1735,9 +1729,6 @@ mod test { fn type_(&self) -> Atom { Atom::sym("Assigner") } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } diff --git a/lib/src/atom/mod.rs b/lib/src/atom/mod.rs index 6b719b215..198e869e6 100644 --- a/lib/src/atom/mod.rs +++ b/lib/src/atom/mod.rs @@ -45,7 +45,6 @@ //! behaviour if needed: //! - [rust_type_atom] - return Rust type name calculated by compiler; //! - [match_by_equality] - match two atoms when `PartialEq::eq` returns `true`; -//! - [execute_not_executable] - return error "atom is not executable". //! // Macros to simplify expression writing @@ -347,9 +346,6 @@ pub trait GroundedAtom : Any + Debug + Display { fn type_(&self) -> Atom { self.as_grounded().type_() } - fn execute(&self, args: &[Atom]) -> Result, ExecError> { - self.as_grounded().execute(args) - } fn match_(&self, other: &Atom) -> matcher::MatchResultIter { match self.as_grounded().as_match() { Some(match_) => match_.match_(other), @@ -387,10 +383,9 @@ impl dyn GroundedAtom { } } -/// Trait allows implementing grounded atom with custom behavior. -/// [rust_type_atom] and [execute_not_executable] functions can be used to -/// implement default behavior if required. If no custom behavior is needed -/// then simpler way is using [Atom::value] function for automatic grounding. +/// Trait allows implementing grounded atom with custom behavior. If no custom +/// behavior is needed then simpler way is using [Atom::value] function for +/// automatic grounding. /// /// # Examples /// @@ -407,10 +402,6 @@ impl dyn GroundedAtom { /// fn type_(&self) -> Atom { /// rust_type_atom::() /// } -/// -/// fn execute(&self, args: &[Atom]) -> Result, ExecError> { -/// execute_not_executable(self) -/// } /// } /// /// impl Display for MyGrounded { @@ -427,20 +418,23 @@ impl dyn GroundedAtom { /// /// assert_eq!(atom.to_string(), "MyGrounded"); /// assert_ne!(atom, Atom::sym("MyGrounded")); -/// assert_eq!(gnd.execute(&mut vec![]), Err(ExecError::NoReduce)); +/// assert!(gnd.as_grounded().as_execute().is_none()); /// assert_eq!(match_atoms(&atom, &other).collect::>(), vec![Bindings::new()]); /// assert_eq!(atom, other); /// ``` /// pub trait Grounded : Display { /// Returns type of the grounded atom. Should return same type each time - /// it is called. + /// it is called. [rust_type_atom] function can be used to implement + /// default behavior if required. fn type_(&self) -> Atom; - // TODO: move `Grounded::execute` into a separate trait similarly to `CustomMatch` - /// Executes grounded function on passed `args` and returns list of - /// results as `Vec` or [ExecError]. - fn execute(&self, args: &[Atom]) -> Result, ExecError>; + /// Returns reference to the custom execution API implementation. If `None` + /// is returned then atom is not executable. + /// See [CustomExecute] for details. + fn as_execute(&self) -> Option<&dyn CustomExecute> { + None + } /// Returns reference to the custom matching API implementation. If `None` /// is returned then atom is matched by equality. @@ -457,14 +451,14 @@ pub trait Grounded : Display { } } -/// Trait for implementing custom matching logic. In order to make it work -/// one should also implement [Grounded::as_match] method. +/// Trait for implementing custom execution logic. Using this trait one can +/// represent a grounded function as an atom. In order to make it work +/// one should also implement [Grounded::as_execute] method. /// /// # Examples /// /// ``` /// use hyperon::*; -/// use hyperon::matcher::{BindingsSet, Bindings, MatchResultIter, match_atoms}; /// use std::fmt::{Display, Formatter}; /// use std::iter::once; /// @@ -476,8 +470,52 @@ pub trait Grounded : Display { /// rust_type_atom::() /// } /// +/// fn as_execute(&self) -> Option<&dyn CustomExecute> { +/// Some(self) +/// } +/// } +/// +/// impl CustomExecute for MyGrounded { /// fn execute(&self, args: &[Atom]) -> Result, ExecError> { -/// execute_not_executable(self) +/// Ok(vec![sym!("result")]) +/// } +/// } +/// +/// impl Display for MyGrounded { +/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +/// write!(f, "MyGrounded") +/// } +/// } +/// +/// let atom = Atom::gnd(MyGrounded{}); +/// let gnd = if let Atom::Grounded(ref gnd) = atom { gnd } else { panic!("Non grounded atom"); }; +/// +/// assert_eq!(gnd.as_grounded().as_execute().unwrap().execute(&mut vec![]), Ok(vec![sym!("result")])); +/// ``` +/// +pub trait CustomExecute { + /// Executes grounded function on passed `args` and returns list of + /// results as `Vec` or [ExecError]. + fn execute(&self, args: &[Atom]) -> Result, ExecError>; +} + +/// Trait for implementing custom matching logic. In order to make it work +/// one should also implement [Grounded::as_match] method. +/// +/// # Examples +/// +/// ``` +/// use hyperon::*; +/// use hyperon::matcher::{BindingsSet, Bindings, MatchResultIter, match_atoms}; +/// use std::fmt::{Display, Formatter}; +/// use std::iter::once; +/// +/// #[derive(Debug, PartialEq, Clone)] +/// struct MyGrounded {} +/// +/// impl Grounded for MyGrounded { +/// fn type_(&self) -> Atom { +/// rust_type_atom::() /// } /// /// fn as_match(&self) -> Option<&dyn CustomMatch> { @@ -562,14 +600,6 @@ pub fn match_by_bidirectional_equality(this: &T, other: &Atom) -> matcher::Ma } } -// TODO: pass args to execute_not_executable(), rename to execute_non_executable() -/// Returns [ExecError::NoReduce] which means this atom should not be reduced -/// further. This is a default implementation of `execute()` for the -/// grounded types wrapped automatically. -pub fn execute_not_executable(_this: &T) -> Result, ExecError> { - Err(ExecError::NoReduce) -} - /// Alias for the list of traits required for the standard Rust types to be /// automatically wrapped into [GroundedAtom]. It is implemented automatically /// when type implements `'static + PartialEq + Clone + Debug`. No need @@ -585,10 +615,6 @@ impl Grounded for AutoGroundedAtom { fn type_(&self) -> Atom { rust_type_atom::() } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } impl GroundedAtom for AutoGroundedAtom { @@ -1060,9 +1086,6 @@ mod test { fn type_(&self) -> Atom { Atom::sym("Integer") } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } impl Display for TestInteger { @@ -1078,6 +1101,12 @@ mod test { fn type_(&self) -> Atom { expr!("->" "i32" "i32") } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for TestMulX { fn execute(&self, args: &[Atom]) -> Result, ExecError> { Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::().unwrap())]) } @@ -1202,7 +1231,9 @@ mod test { fn test_custom_execution() { let mul3 = Atom::gnd(TestMulX(3)); if let Atom::Grounded(gnd) = mul3 { - let res = gnd.execute(&mut vec![Atom::value(14)]); + let res = gnd.as_grounded() + .as_execute().unwrap() + .execute(&mut vec![Atom::value(14)]); assert_eq!(res, Ok(vec![Atom::value(42)])); } else { assert!(false, "GroundedAtom is expected"); diff --git a/lib/src/common/mod.rs b/lib/src/common/mod.rs index c79f25904..b4e83cab5 100644 --- a/lib/src/common/mod.rs +++ b/lib/src/common/mod.rs @@ -41,6 +41,12 @@ impl Grounded for &'static Operation { parser.parse(&Tokenizer::new()).unwrap().unwrap() } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for &'static Operation { fn execute(&self, args: &[Atom]) -> Result, ExecError> { (self.execute)(self, args) } diff --git a/lib/src/common/shared.rs b/lib/src/common/shared.rs index 0c3b0ee94..25db15432 100644 --- a/lib/src/common/shared.rs +++ b/lib/src/common/shared.rs @@ -170,8 +170,19 @@ impl Grounded for Shared { } } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + match self.0.borrow().as_execute() { + None => None, + Some(_) => Some(self), + } + } +} + +impl CustomExecute for Shared { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - self.0.borrow().execute(args) + self.0.borrow().as_execute() + .expect("Custom execution procedure is not expected to be changed in runtime") + .execute(args) } } @@ -216,6 +227,12 @@ mod tests { fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for SharedGrounded { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let mut result = vec![Atom::sym("executed")]; result.extend(args.into_iter().cloned()); diff --git a/lib/src/metta/interpreter.rs b/lib/src/metta/interpreter.rs index a76e1c81c..794a1c2a9 100644 --- a/lib/src/metta/interpreter.rs +++ b/lib/src/metta/interpreter.rs @@ -586,23 +586,28 @@ fn execute_op<'a, T: SpaceRef<'a>>(context: InterpreterContextRef<'a, T>, input: let op = expr.children().get(0); if let Some(Atom::Grounded(op)) = op { let args = expr.children(); - match op.execute(&args[1..]) { - Ok(mut vec) => { - let results: Vec = vec.drain(0..) - .map(|atom| InterpretedAtom(atom, bindings.clone())) - .collect(); - if results.is_empty() { - StepResult::ret(results) - } else { - make_alternives_plan(input.0, results, move |result| { - interpret_as_type_plan(context.clone(), - result, ATOM_TYPE_UNDEFINED) - }) + match op.as_grounded().as_execute() { + None => StepResult::err((input.0, NOT_REDUCIBLE_SYMBOL)), + Some(executable) => { + match executable.execute(&args[1..]) { + Ok(mut vec) => { + let results: Vec = vec.drain(0..) + .map(|atom| InterpretedAtom(atom, bindings.clone())) + .collect(); + if results.is_empty() { + StepResult::ret(results) + } else { + make_alternives_plan(input.0, results, move |result| { + interpret_as_type_plan(context.clone(), + result, ATOM_TYPE_UNDEFINED) + }) + } + }, + Err(ExecError::Runtime(msg)) => StepResult::ret(vec![InterpretedAtom( + Atom::expr([ERROR_SYMBOL, input.0, Atom::sym(msg)]), input.1)]), + Err(ExecError::NoReduce) => StepResult::err((input.0, NOT_REDUCIBLE_SYMBOL)), } }, - Err(ExecError::Runtime(msg)) => StepResult::ret(vec![InterpretedAtom( - Atom::expr([ERROR_SYMBOL, input.0, Atom::sym(msg)]), input.1)]), - Err(ExecError::NoReduce) => StepResult::err((input.0, NOT_REDUCIBLE_SYMBOL)), } } else { panic!("Trying to execute non grounded atom: {}", expr) @@ -903,6 +908,12 @@ mod tests { fn type_(&self) -> Atom { expr!("->" "&str" "Error") } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ThrowError { fn execute(&self, args: &[Atom]) -> Result, ExecError> { Err((*args[0].as_gnd::<&str>().unwrap()).into()) } @@ -930,6 +941,12 @@ mod tests { fn type_(&self) -> Atom { expr!("->" "&str" "u32") } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for NonReducible { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { Err(ExecError::NoReduce) } @@ -972,6 +989,12 @@ mod tests { fn type_(&self) -> Atom { ATOM_TYPE_UNDEFINED } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for MulXUndefinedType { fn execute(&self, args: &[Atom]) -> Result, ExecError> { Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::().unwrap())]) } diff --git a/lib/src/metta/interpreter_minimal.rs b/lib/src/metta/interpreter_minimal.rs index ada0a8d53..2125ee950 100644 --- a/lib/src/metta/interpreter_minimal.rs +++ b/lib/src/metta/interpreter_minimal.rs @@ -419,33 +419,38 @@ fn eval<'a, T: Space>(context: &InterpreterContext, stack: Stack, bindings: B log::debug!("eval: to_eval: {}", to_eval); match atom_as_slice(&to_eval) { Some([Atom::Grounded(op), args @ ..]) => { - let exec_res = op.execute(args); - log::debug!("eval: execution results: {:?}", exec_res); - match exec_res { - Ok(results) => { - if results.is_empty() { - // There is no valid reason to return empty result from - // the grounded function. If alternative should be removed - // from the plan then EMPTY_SYMBOL is a proper result. - // If grounded atom returns no value then UNIT_ATOM() - // should be returned. NotReducible or Exec::NoReduce - // can be returned to let a caller know that function - // is not defined on a passed input data. Thus we can - // interpreter empty result by any way we like. - finished_result(EMPTY_SYMBOL, bindings, prev) - } else { - let call_stack = call_to_stack(to_eval, vars, prev.clone()); - results.into_iter() - .map(|res| eval_result(prev.clone(), res, &call_stack, bindings.clone())) - .collect() + match op.as_grounded().as_execute() { + None => finished_result(return_not_reducible(), bindings, prev), + Some(executable) => { + let exec_res = executable.execute(args); + log::debug!("eval: execution results: {:?}", exec_res); + match exec_res { + Ok(results) => { + if results.is_empty() { + // There is no valid reason to return empty result from + // the grounded function. If alternative should be removed + // from the plan then EMPTY_SYMBOL is a proper result. + // If grounded atom returns no value then UNIT_ATOM() + // should be returned. NotReducible or Exec::NoReduce + // can be returned to let a caller know that function + // is not defined on a passed input data. Thus we can + // interpreter empty result by any way we like. + finished_result(EMPTY_SYMBOL, bindings, prev) + } else { + let call_stack = call_to_stack(to_eval, vars, prev.clone()); + results.into_iter() + .map(|res| eval_result(prev.clone(), res, &call_stack, bindings.clone())) + .collect() + } + }, + Err(ExecError::Runtime(err)) => + finished_result(error_atom(to_eval, err), bindings, prev), + Err(ExecError::NoReduce) => + // TODO: we could remove ExecError::NoReduce and explicitly + // return NOT_REDUCIBLE_SYMBOL from the grounded function instead. + finished_result(return_not_reducible(), bindings, prev), } }, - Err(ExecError::Runtime(err)) => - finished_result(error_atom(to_eval, err), bindings, prev), - Err(ExecError::NoReduce) => - // TODO: we could remove ExecError::NoReduce and explicitly - // return NOT_REDUCIBLE_SYMBOL from the grounded function instead. - finished_result(return_not_reducible(), bindings, prev), } }, _ if is_embedded_op(&to_eval) => @@ -1190,6 +1195,12 @@ mod tests { fn type_(&self) -> Atom { expr!("->" "&str" "Error") } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ThrowError { fn execute(&self, args: &[Atom]) -> Result, ExecError> { Err((*args[0].as_gnd::<&str>().unwrap()).into()) } @@ -1208,6 +1219,12 @@ mod tests { fn type_(&self) -> Atom { expr!("->" "u32" "u32") } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for NonReducible { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { Err(ExecError::NoReduce) } @@ -1226,6 +1243,12 @@ mod tests { fn type_(&self) -> Atom { ATOM_TYPE_UNDEFINED } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for MulXUndefinedType { fn execute(&self, args: &[Atom]) -> Result, ExecError> { Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::().unwrap())]) } @@ -1244,6 +1267,12 @@ mod tests { fn type_(&self) -> Atom { ATOM_TYPE_UNDEFINED } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ReturnNothing { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { Ok(vec![]) } diff --git a/lib/src/metta/runner/arithmetics.rs b/lib/src/metta/runner/arithmetics.rs index 4689b11d9..f76e45de3 100644 --- a/lib/src/metta/runner/arithmetics.rs +++ b/lib/src/metta/runner/arithmetics.rs @@ -116,10 +116,6 @@ impl Grounded for Number { ATOM_TYPE_NUMBER } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } - fn serialize(&self, serializer: &mut dyn serial::Serializer) -> serial::Result { match self { &Self::Integer(n) => serializer.serialize_i64(n), @@ -161,10 +157,6 @@ impl Grounded for Bool { ATOM_TYPE_BOOL } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } - fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } @@ -196,6 +188,12 @@ macro_rules! def_binary_number_op { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, $r]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for $name { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from(concat!(stringify!($op), " expects two number arguments")); let a = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?; @@ -240,6 +238,12 @@ macro_rules! def_binary_bool_op { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for $name { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from(concat!(stringify!($disp), " expects two boolean arguments")); let Bool(a) = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_bool().ok_or_else(arg_error)?; @@ -271,6 +275,12 @@ impl Grounded for FlipOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for FlipOp { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { Ok(vec![Atom::gnd(Bool(rand::random()))]) } @@ -291,6 +301,12 @@ impl Grounded for NotOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL, ATOM_TYPE_BOOL]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for NotOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("not expects one boolean arguments"); let &Bool(a) = args.get(0).ok_or_else(arg_error)?.as_gnd::().ok_or_else(arg_error)?; diff --git a/lib/src/metta/runner/builtin_mods/catalog_mods.rs b/lib/src/metta/runner/builtin_mods/catalog_mods.rs index 3fab2d2cc..0a1f92d6c 100644 --- a/lib/src/metta/runner/builtin_mods/catalog_mods.rs +++ b/lib/src/metta/runner/builtin_mods/catalog_mods.rs @@ -1,4 +1,4 @@ -use crate::atom::{Atom, Grounded, ExecError}; +use crate::atom::{Atom, Grounded, ExecError, CustomExecute}; use crate::space::grounding::GroundingSpace; use crate::metta::{ARROW_SYMBOL, ATOM_TYPE_SYMBOL, UNIT_TYPE}; use crate::metta::runner::{Metta, ModuleLoader, RunContext, DynSpace}; @@ -91,6 +91,12 @@ impl Grounded for CatalogListOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CatalogListOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "catalog-list! expects a catalog name, or \"all\" to list all available"; let cat_name_arg_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; @@ -150,6 +156,12 @@ impl Grounded for CatalogUpdateOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CatalogUpdateOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "catalog-update! expects a catalog name, or \"all\" to update all"; let cat_name_arg_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; @@ -204,6 +216,12 @@ impl Grounded for CatalogClearOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CatalogClearOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "catalog-clear! expects a catalog name, or \"all\" to clear all"; let cat_name_arg_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; diff --git a/lib/src/metta/runner/mod.rs b/lib/src/metta/runner/mod.rs index 314f25861..7c139f00c 100644 --- a/lib/src/metta/runner/mod.rs +++ b/lib/src/metta/runner/mod.rs @@ -1247,23 +1247,23 @@ mod tests { assert_eq!(result, Ok(vec![vec![expr!("Error" ("foo" "b") "BadType")]])); } - #[derive(Clone, PartialEq, Debug)] + #[derive(Clone, Debug)] struct ErrorOp{} - impl std::fmt::Display for ErrorOp { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "error") - } - } + grounded_op!(ErrorOp, "error"); impl Grounded for ErrorOp { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ErrorOp { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - // TODO: why next two lines led to not equal results? - Ok(vec![expr!("Error" ("error") "TestError")]) - //Err("TestError".into()) + Err("TestError".into()) } } @@ -1280,7 +1280,7 @@ mod tests { |_| Atom::gnd(ErrorOp{})); let result = metta.run(SExprParser::new(program)); - assert_eq!(result, Ok(vec![vec![expr!("Error" ("error") "TestError")]])); + assert_eq!(result, Ok(vec![vec![expr!("Error" ({ErrorOp{}}) "TestError")]])); } #[test] @@ -1312,6 +1312,12 @@ mod tests { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ReturnAtomOp { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { Ok(vec![self.0.clone()]) } diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 4a86edecb..dbc9cf023 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -81,6 +81,12 @@ impl Grounded for ImportOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for ImportOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { //QUESTION: "Import" can mean several (3) different things. In Python parlance, it can mean //1. "import module" opt. ("as bar") @@ -172,6 +178,12 @@ impl Grounded for IncludeOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for IncludeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("include expects a module name argument"); let mod_name_atom = args.get(0).ok_or_else(arg_error)?; @@ -229,6 +241,12 @@ impl Grounded for ModSpaceOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, rust_type_atom::()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for ModSpaceOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "mod-space! expects a module name argument"; let mod_name_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; @@ -274,6 +292,12 @@ impl Grounded for PrintModsOp { Atom::expr([ARROW_SYMBOL, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for PrintModsOp { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { self.metta.display_loaded_modules(); unit_result() @@ -298,6 +322,12 @@ impl Grounded for BindOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_SYMBOL, ATOM_TYPE_UNDEFINED, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for BindOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("bind! expects two arguments: token and atom"); let token = <&SymbolAtom>::try_from(args.get(0).ok_or_else(arg_error)?).map_err(|_| "bind! expects symbol atom as a token")?.name(); @@ -319,6 +349,12 @@ impl Grounded for NewSpaceOp { Atom::expr([ARROW_SYMBOL, rust_type_atom::()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for NewSpaceOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { if args.len() == 0 { let space = Atom::gnd(DynSpace::new(GroundingSpace::new())); @@ -340,6 +376,12 @@ impl Grounded for AddAtomOp { ATOM_TYPE_ATOM, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for AddAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("add-atom expects two arguments: space and atom"); let space = args.get(0).ok_or_else(arg_error)?; @@ -361,6 +403,12 @@ impl Grounded for RemoveAtomOp { ATOM_TYPE_ATOM, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for RemoveAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("remove-atom expects two arguments: space and atom"); let space = args.get(0).ok_or_else(arg_error)?; @@ -383,6 +431,12 @@ impl Grounded for GetAtomsOp { ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetAtomsOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("get-atoms expects one argument: space"); let space = args.get(0).ok_or_else(arg_error)?; @@ -411,6 +465,12 @@ impl Grounded for PragmaOp { ATOM_TYPE_UNDEFINED } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for PragmaOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("pragma! expects key and value as arguments"); let key = <&SymbolAtom>::try_from(args.get(0).ok_or_else(arg_error)?).map_err(|_| "pragma! expects symbol atom as a key")?.name(); @@ -438,6 +498,12 @@ impl Grounded for GetTypeOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetTypeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("get-type expects single atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -456,6 +522,12 @@ impl Grounded for GetTypeSpaceOp { Atom::expr([ARROW_SYMBOL, rust_type_atom::(), ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetTypeSpaceOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("get-type-space expects two arguments: space and atom"); let space = args.get(0).ok_or_else(arg_error)?; @@ -477,6 +549,12 @@ impl Grounded for GetMetaTypeOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetMetaTypeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("get-metatype expects single atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -496,6 +574,12 @@ impl Grounded for PrintlnOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for PrintlnOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("println! expects single atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -516,6 +600,12 @@ impl Grounded for FormatArgsOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for FormatArgsOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("format-args expects format string as a first argument and expression as a second argument"); let format = atom_to_string(args.get(0).ok_or_else(arg_error)?); @@ -583,6 +673,12 @@ impl Grounded for TraceOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, Atom::var("a"), Atom::var("a")]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for TraceOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("trace! expects two atoms as arguments"); let val = args.get(1).ok_or_else(arg_error)?; @@ -602,6 +698,12 @@ impl Grounded for NopOp { ATOM_TYPE_UNDEFINED } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for NopOp { fn execute(&self, _args: &[Atom]) -> Result, ExecError> { unit_result() } @@ -637,10 +739,6 @@ impl Grounded for StateAtom { }; Atom::expr([expr!("StateMonad"), typ]) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } #[derive(Clone, Debug)] @@ -653,6 +751,12 @@ impl Grounded for NewStateOp { Atom::expr([ARROW_SYMBOL, expr!(tnso), expr!("StateMonad" tnso)]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for NewStateOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "new-state expects single atom as an argument"; let atom = args.get(0).ok_or(arg_error)?; @@ -670,6 +774,12 @@ impl Grounded for GetStateOp { Atom::expr([ARROW_SYMBOL, expr!("StateMonad" tgso), expr!(tgso)]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetStateOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "get-state expects single state atom as an argument"; let state = args.get(0).ok_or(arg_error)?; @@ -688,6 +798,12 @@ impl Grounded for ChangeStateOp { Atom::expr([ARROW_SYMBOL, expr!("StateMonad" tcso), expr!(tcso), expr!("StateMonad" tcso)]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for ChangeStateOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "change-state! expects a state atom and its new value as arguments"; let atom = args.get(0).ok_or(arg_error)?; @@ -708,6 +824,12 @@ impl Grounded for SealedOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for SealedOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("sealed expects two arguments: var_list and expression"); @@ -742,6 +864,12 @@ impl Grounded for EqualOp { Atom::expr([ARROW_SYMBOL, expr!(t), expr!(t), ATOM_TYPE_BOOL]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for EqualOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from(concat!(stringify!($op), " expects two arguments")); let a = args.get(0).ok_or_else(arg_error)?; @@ -761,6 +889,12 @@ impl Grounded for MatchOp { Atom::expr([ARROW_SYMBOL, rust_type_atom::(), ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for MatchOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("match expects three arguments: space, pattern and template"); let space = args.get(0).ok_or_else(arg_error)?; @@ -796,6 +930,12 @@ pub(crate) mod pkg_mgmt_ops { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for RegisterModuleOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "register-module! expects a file system path; use quotes if needed"; let path_arg_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; @@ -845,6 +985,12 @@ pub(crate) mod pkg_mgmt_ops { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for GitModuleOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = "git-module! expects a URL; use quotes if needed"; let url_arg_atom = args.get(0).ok_or_else(|| ExecError::from(arg_error))?; @@ -906,6 +1052,12 @@ impl Grounded for UniqueOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for UniqueOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("unique expects single executable atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -941,6 +1093,12 @@ impl Grounded for UnionOp { 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 UnionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("union expects and executable LHS and RHS atom"); let lhs = args.get(0).ok_or_else(arg_error)?; @@ -974,6 +1132,12 @@ impl Grounded for IntersectionOp { 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 IntersectionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("intersection expects and executable LHS and RHS atom"); let lhs = args.get(0).ok_or_else(arg_error)?; @@ -1046,6 +1210,12 @@ impl Grounded for SubtractionOp { 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 SubtractionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("subtraction expects and executable LHS and RHS atom"); let lhs = args.get(0).ok_or_else(arg_error)?; @@ -1125,6 +1295,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for CarAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("car-atom expects one argument: expression"); let expr = args.get(0).ok_or_else(arg_error)?; @@ -1144,6 +1320,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for CdrAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("cdr-atom expects one argument: expression"); let expr = args.get(0).ok_or_else(arg_error)?; @@ -1167,6 +1349,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for ConsAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("cons-atom expects two arguments: atom and expression"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -1196,6 +1384,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for CaptureOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("capture expects one argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -1293,6 +1487,12 @@ mod non_minimal_only_stdlib { 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 CaseOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { CaseOp::execute(self, args) } @@ -1325,6 +1525,12 @@ mod non_minimal_only_stdlib { 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> { 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)?; @@ -1355,6 +1561,12 @@ mod non_minimal_only_stdlib { 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> { 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)?; @@ -1386,6 +1598,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for CollapseOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("collapse expects single executable atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -1416,6 +1634,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for SuperposeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("superpose expects single expression as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -1443,6 +1667,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for LetOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("let expects three arguments: pattern, atom and template"); let mut template = args.get(2).ok_or_else(arg_error)?.clone(); @@ -1496,6 +1726,12 @@ mod non_minimal_only_stdlib { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } + } + + impl CustomExecute for LetVarOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("let* list of couples and template as arguments"); let expr = atom_as_expr(args.get(0).ok_or_else(arg_error)?).ok_or(arg_error())?; @@ -2321,10 +2557,6 @@ mod tests { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, sym!("Arg1Type"), sym!("Arg2Type"), sym!("ReturnType")]) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } #[test] diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index 6d748e8fc..58ac73a31 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -28,6 +28,12 @@ impl Grounded for PrintAlternativesOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, UNIT_TYPE()]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for PrintAlternativesOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("print-alternatives! expects format string as a first argument and expression as a second argument"); let atom = atom_to_string(args.get(0).ok_or_else(arg_error)?); @@ -72,6 +78,12 @@ impl Grounded for GetTypeOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for GetTypeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("get-type expects single atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -99,6 +111,12 @@ impl Grounded for IfEqualOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for IfEqualOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("if-equal expects as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -157,6 +175,12 @@ impl Grounded for AssertEqualOp { 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"); @@ -188,6 +212,12 @@ impl Grounded for AssertEqualToResultOp { 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"); @@ -220,6 +250,12 @@ impl Grounded for SuperposeOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_UNDEFINED]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for SuperposeOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("superpose expects single expression as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -258,6 +294,12 @@ impl Grounded for CollapseOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CollapseOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("collapse expects single executable atom as an argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -288,6 +330,12 @@ impl Grounded for CaptureOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CaptureOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("capture expects one argument"); let atom = args.get(0).ok_or_else(arg_error)?; @@ -313,6 +361,12 @@ impl Grounded for CaseOp { Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) } + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for CaseOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("case expects two arguments: atom and expression of cases"); let cases = args.get(1).ok_or_else(arg_error)?; @@ -1032,10 +1086,6 @@ mod tests { fn type_(&self) -> Atom { Atom::expr([ARROW_SYMBOL, sym!("Arg1Type"), sym!("Arg2Type"), sym!("RetType")]) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } #[ignore = "Test is slow"] diff --git a/lib/src/metta/runner/string.rs b/lib/src/metta/runner/string.rs index abc9efef3..b1cb13b6e 100644 --- a/lib/src/metta/runner/string.rs +++ b/lib/src/metta/runner/string.rs @@ -30,10 +30,6 @@ impl Grounded for Str { ATOM_TYPE_STRING } - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } - fn serialize(&self, serializer: &mut dyn serial::Serializer) -> serial::Result { serializer.serialize_str(self.as_str()) } diff --git a/lib/src/metta/types.rs b/lib/src/metta/types.rs index e2cb39ef3..69bd333de 100644 --- a/lib/src/metta/types.rs +++ b/lib/src/metta/types.rs @@ -315,10 +315,6 @@ impl Grounded for UndefinedTypeMatch { fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } impl CustomMatch for UndefinedTypeMatch { @@ -798,10 +794,6 @@ mod tests { fn type_(&self) -> Atom { self.0.clone() } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } #[test] diff --git a/lib/src/space/grounding.rs b/lib/src/space/grounding.rs index 3462cf454..dd4235ea8 100644 --- a/lib/src/space/grounding.rs +++ b/lib/src/space/grounding.rs @@ -373,10 +373,6 @@ impl Grounded for GroundingSpace { fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } impl CustomMatch for GroundingSpace { diff --git a/lib/src/space/mod.rs b/lib/src/space/mod.rs index 9b05c9c64..af23e9157 100644 --- a/lib/src/space/mod.rs +++ b/lib/src/space/mod.rs @@ -363,10 +363,6 @@ impl crate::atom::Grounded for DynSpace { fn as_match(&self) -> Option<&dyn CustomMatch> { Some(self) } - - fn execute(&self, _args: &[Atom]) -> Result, ExecError> { - execute_not_executable(self) - } } impl CustomMatch for DynSpace {