Skip to content

Commit

Permalink
Merge pull request trueagi-io#718 from vsbogd/move-out-executable
Browse files Browse the repository at this point in the history
Move out executable
  • Loading branch information
vsbogd authored Jun 15, 2024
2 parents 9019fac + 3727042 commit aff2b9f
Show file tree
Hide file tree
Showing 17 changed files with 562 additions and 157 deletions.
39 changes: 24 additions & 15 deletions c/src/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,22 +599,10 @@ impl Grounded for CGrounded {
unsafe{ &(*self.get_ptr()).typ }.borrow().clone()
}

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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),
}
}

Expand All @@ -636,6 +624,27 @@ impl Grounded for CGrounded {
}
}

impl CustomExecute for CGrounded {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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_ {
Expand Down
3 changes: 0 additions & 3 deletions lib/examples/custom_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ impl Grounded for TestDict {
fn type_(&self) -> Atom {
Atom::sym("Dict")
}
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
Expand Down
9 changes: 0 additions & 9 deletions lib/src/atom/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,9 +1441,6 @@ mod test {
fn type_(&self) -> Atom {
Atom::sym("Rand")
}
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
Expand Down Expand Up @@ -1491,9 +1488,6 @@ mod test {
fn type_(&self) -> Atom {
Atom::sym("ReturnPairInX")
}
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
Expand Down Expand Up @@ -1735,9 +1729,6 @@ mod test {
fn type_(&self) -> Atom {
Atom::sym("Assigner")
}
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
Expand Down
107 changes: 69 additions & 38 deletions lib/src/atom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -347,9 +346,6 @@ pub trait GroundedAtom : Any + Debug + Display {
fn type_(&self) -> Atom {
self.as_grounded().type_()
}
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
self.as_grounded().execute(args)
}
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match self.as_grounded().as_match() {
Some(match_) => match_.match_(other),
Expand Down Expand Up @@ -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
///
Expand All @@ -407,10 +402,6 @@ impl dyn GroundedAtom {
/// fn type_(&self) -> Atom {
/// rust_type_atom::<MyGrounded>()
/// }
///
/// fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
/// execute_not_executable(self)
/// }
/// }
///
/// impl Display for MyGrounded {
Expand All @@ -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>>(), 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<Atom>` or [ExecError].
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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.
Expand All @@ -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;
///
Expand All @@ -476,8 +470,52 @@ pub trait Grounded : Display {
/// rust_type_atom::<MyGrounded>()
/// }
///
/// fn as_execute(&self) -> Option<&dyn CustomExecute> {
/// Some(self)
/// }
/// }
///
/// impl CustomExecute for MyGrounded {
/// fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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<Atom>` or [ExecError].
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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::<MyGrounded>()
/// }
///
/// fn as_match(&self) -> Option<&dyn CustomMatch> {
Expand Down Expand Up @@ -562,14 +600,6 @@ pub fn match_by_bidirectional_equality<T>(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<T: Debug>(_this: &T) -> Result<Vec<Atom>, 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
Expand All @@ -585,10 +615,6 @@ impl<T: AutoGroundedType> Grounded for AutoGroundedAtom<T> {
fn type_(&self) -> Atom {
rust_type_atom::<T>()
}

fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
}

impl<T: AutoGroundedType> GroundedAtom for AutoGroundedAtom<T> {
Expand Down Expand Up @@ -1060,9 +1086,6 @@ mod test {
fn type_(&self) -> Atom {
Atom::sym("Integer")
}
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
}

impl Display for TestInteger {
Expand All @@ -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<Vec<Atom>, ExecError> {
Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::<i32>().unwrap())])
}
Expand Down Expand Up @@ -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");
Expand Down
6 changes: 6 additions & 0 deletions lib/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<Atom>, ExecError> {
(self.execute)(self, args)
}
Expand Down
19 changes: 18 additions & 1 deletion lib/src/common/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,19 @@ impl<T: Grounded> Grounded for Shared<T> {
}
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
match self.0.borrow().as_execute() {
None => None,
Some(_) => Some(self),
}
}
}

impl <T: Grounded> CustomExecute for Shared<T> {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, 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)
}
}

Expand Down Expand Up @@ -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<Vec<Atom>, ExecError> {
let mut result = vec![Atom::sym("executed")];
result.extend(args.into_iter().cloned());
Expand Down
Loading

0 comments on commit aff2b9f

Please sign in to comment.