Skip to content

Commit

Permalink
Merge branch 'main' into open-ssl-dynamic-link
Browse files Browse the repository at this point in the history
  • Loading branch information
luketpeterson committed Jun 6, 2024
2 parents da74ea2 + 27bc9df commit 728c100
Show file tree
Hide file tree
Showing 17 changed files with 193 additions and 357 deletions.
24 changes: 17 additions & 7 deletions c/src/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,14 +618,10 @@ impl Grounded for CGrounded {
}
}

fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
fn as_match(&self) -> Option<&dyn CustomMatch> {
match self.api().match_ {
Some(func) => {
let other: atom_ref_t = other.into();
let set = func(self.get_ptr(), &other);
Box::new(set.into_inner().into_iter())
},
None => match_by_equality(self, other)
None => None,
Some(_) => Some(self),
}
}

Expand All @@ -640,6 +636,20 @@ impl Grounded for CGrounded {
}
}

impl CustomMatch for CGrounded {
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match self.api().match_ {
Some(func) => {
let other: atom_ref_t = other.into();
let set = func(self.get_ptr(), &other);
Box::new(set.into_inner().into_iter())
},
None => match_by_equality(self, other)
}
}

}

impl PartialEq for CGrounded {
fn eq(&self, other: &CGrounded) -> bool {
(self.api().eq)(self.get_ptr(), other.get_ptr())
Expand Down
8 changes: 7 additions & 1 deletion lib/examples/custom_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ impl Grounded for TestDict {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
}

impl CustomMatch for TestDict {
fn match_(&self, other: &Atom) -> MatchResultIter {
if let Some(other) = other.as_gnd::<TestDict>() {
other.0.iter().map(|(ko, vo)| {
self.0.iter().map(|(k, v)| {
Expand Down
18 changes: 18 additions & 0 deletions lib/src/atom/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,12 @@ mod test {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
}

impl CustomMatch for Rand {
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match other {
Atom::Expression(expr) if expr.children().len() == 1 =>
Expand Down Expand Up @@ -1485,6 +1491,12 @@ mod test {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
}

impl CustomMatch for ReturnPairInX {
fn match_(&self, _other: &Atom) -> matcher::MatchResultIter {
let result = vec![ bind!{ x: expr!("B") }, bind!{ x: expr!("C") } ];
Box::new(result.into_iter())
Expand Down Expand Up @@ -1723,6 +1735,12 @@ mod test {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
}

impl CustomMatch for Assigner {
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match other.iter().collect::<Vec<&Atom>>().as_slice() {
[ Atom::Variable(var), values @ .. ] => {
Expand Down
119 changes: 88 additions & 31 deletions lib/src/atom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ pub fn make_variables_unique(mut atom: Atom) -> Atom {
// values:
// - type_() to return MeTTa type of the atom;
// - execute() to represent functions as atoms;
// - match_() to implement custom matching behaviour.
// - as_match() to return optional custom matching behaviour API.

// match_by_equality() method allows reusing default match_() implementation in
// 3rd party code when it is not required to be customized.
// By default Grounded::as_match() returns None which makes GroundedAtom's
// implementation match atom by equality.

/// Grounded function execution error.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -351,7 +351,21 @@ pub trait GroundedAtom : Any + Debug + Display {
self.as_grounded().execute(args)
}
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
self.as_grounded().match_(other)
match self.as_grounded().as_match() {
Some(match_) => match_.match_(other),
None => {
let equal = match other {
Atom::Grounded(other) => self.eq_gnd(other.as_ref()),
_ => false,
};
let result = if equal {
matcher::BindingsSet::single()
} else {
matcher::BindingsSet::empty()
};
Box::new(result.into_iter())
},
}
}
// A mutable reference is used here instead of a type parameter because
// the type parameter makes impossible using GroundedAtom reference in the
Expand All @@ -373,17 +387,16 @@ impl dyn GroundedAtom {
}
}

/// Trait allows implementing grounded atom with custom behaviour.
/// [rust_type_atom], [match_by_equality] and [execute_not_executable]
/// functions can be used to implement default behavior if requried.
/// 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.
/// [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.
///
/// # Examples
///
/// ```
/// use hyperon::*;
/// use hyperon::matcher::{Bindings, MatchResultIter, match_atoms};
/// use hyperon::matcher::{Bindings, match_atoms};
/// use std::fmt::{Display, Formatter};
/// use std::iter::once;
///
Expand All @@ -398,10 +411,6 @@ impl dyn GroundedAtom {
/// fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
/// execute_not_executable(self)
/// }
///
/// fn match_(&self, other: &Atom) -> MatchResultIter {
/// match_by_equality(self, other)
/// }
/// }
///
/// impl Display for MyGrounded {
Expand All @@ -428,15 +437,17 @@ pub trait Grounded : Display {
/// it is called.
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>;

/// Implements custom matching logic of the grounded atom.
/// Gets `other` atom as input, returns the iterator of the
/// [matcher::Bindings] for the variables of the `other` atom.
/// See [matcher] for detailed explanation.
fn match_(&self, other: &Atom) -> matcher::MatchResultIter;
/// Returns reference to the custom matching API implementation. If `None`
/// is returned then atom is matched by equality.
/// See [CustomMatch] for details.
fn as_match(&self) -> Option<&dyn CustomMatch> {
None
}

/// Implements serialization logic of the grounded atom. The logic is
/// implemented in terms of the Rust native types.
Expand All @@ -446,14 +457,70 @@ pub trait Grounded : Display {
}
}

/// 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 execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
/// execute_not_executable(self)
/// }
///
/// fn as_match(&self) -> Option<&dyn CustomMatch> {
/// Some(self)
/// }
/// }
///
/// impl CustomMatch for MyGrounded {
/// fn match_(&self, other: &Atom) -> MatchResultIter {
/// // Doesn't match with anything even with itself
/// Box::new(BindingsSet::empty().into_iter())
/// }
/// }
///
/// impl Display for MyGrounded {
/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
/// write!(f, "MyGrounded")
/// }
/// }
///
/// let atom = Atom::gnd(MyGrounded{});
/// let same = Atom::gnd(MyGrounded{});
///
/// assert_eq!(atom, same);
/// assert_eq!(match_atoms(&atom, &same).collect::<Vec<Bindings>>(), vec![]);
/// ```
///
pub trait CustomMatch {
/// Implements custom matching logic of the grounded atom.
/// Gets `other` atom as input, returns the iterator of the
/// [matcher::Bindings] for the variables of the `other` atom.
/// See [matcher] for detailed explanation.
fn match_(&self, other: &Atom) -> matcher::MatchResultIter;
}

/// Returns the name of the Rust type wrapped into [Atom::Symbol]. This is a
/// default implementation of `type_()` for the grounded types wrapped
/// automatically.
pub fn rust_type_atom<T>() -> Atom {
Atom::sym(std::any::type_name::<T>())
}

/// Returns either single emtpy [matcher::Bindings] instance if `self` and
/// Returns either single empty [matcher::Bindings] instance if `self` and
/// `other` are equal or empty iterator if not. This is a default
/// implementation of `match_()` for the grounded types wrapped automatically.
pub fn match_by_equality<T: 'static + PartialEq + Debug>(this: &T, other: &Atom) -> matcher::MatchResultIter {
Expand All @@ -464,7 +531,7 @@ pub fn match_by_equality<T: 'static + PartialEq + Debug>(this: &T, other: &Atom)
}
}

/// Returns either single emtpy [matcher::Bindings] instance if the string representing `self` and
/// Returns either single empty [matcher::Bindings] instance if the string representing `self` and
/// `other` render are identical strings, or an empty iterator if not.
pub fn match_by_string_equality(this: &str, other: &Atom) -> matcher::MatchResultIter {
let other_string = other.to_string();
Expand Down Expand Up @@ -522,10 +589,6 @@ impl<T: AutoGroundedType> Grounded for AutoGroundedAtom<T> {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}

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

impl<T: AutoGroundedType> GroundedAtom for AutoGroundedAtom<T> {
Expand Down Expand Up @@ -1000,9 +1063,6 @@ mod test {
fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
execute_not_executable(self)
}
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match_by_equality(self, other)
}
}

impl Display for TestInteger {
Expand All @@ -1021,9 +1081,6 @@ mod test {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::<i32>().unwrap())])
}
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
match_by_equality(self, other)
}
}

impl Display for TestMulX {
Expand Down
4 changes: 0 additions & 4 deletions lib/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ impl Grounded for &'static Operation {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
(self.execute)(self, args)
}

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

impl PartialEq for Operation {
Expand Down
36 changes: 26 additions & 10 deletions lib/src/common/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::{Debug, Display};
use crate::atom::*;
use crate::matcher::MatchResultIter;

pub trait LockBorrow<T: ?Sized> {
fn borrow(&self) -> Box<dyn Deref<Target=T> + '_>;
Expand Down Expand Up @@ -164,15 +163,26 @@ impl<T: Grounded> Grounded for Shared<T> {
rust_type_atom::<Self>()
}

fn match_(&self, other: &Atom) -> MatchResultIter {
self.0.borrow().match_(other)
fn as_match(&self) -> Option<&dyn CustomMatch> {
match self.0.borrow().as_match() {
None => None,
Some(_) => Some(self),
}
}

fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
self.0.borrow().execute(args)
}
}

impl<T: Grounded> CustomMatch for Shared<T> {
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
self.0.borrow().as_match()
.expect("Custom match procedure is not expected to be changed in runtime")
.match_(other)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -203,27 +213,33 @@ mod tests {
fn type_(&self) -> Atom {
Atom::sym("IgnoredType")
}
fn match_(&self, other: &Atom) -> MatchResultIter {
fn as_match(&self) -> Option<&dyn CustomMatch> {
Some(self)
}
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let mut result = vec![Atom::sym("executed")];
result.extend(args.into_iter().cloned());
Ok(result)
}
}

impl CustomMatch for SharedGrounded {
fn match_(&self, other: &Atom) -> matcher::MatchResultIter {
let vec = if *other == Atom::sym("matchable") {
vec![bind!{ x: Atom::sym("match") }]
} else {
Vec::new()
};
Box::new(vec.into_iter())
}
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let mut result = vec![Atom::sym("executed")];
result.extend(args.into_iter().cloned());
Ok(result)
}
}

#[test]
fn grounded_for_shared() {
let shared = Shared::new(SharedGrounded{});

assert_eq!(shared.type_(), Atom::sym("hyperon::common::shared::Shared<hyperon::common::shared::tests::SharedGrounded>"));
assert_eq!(shared.match_(&Atom::sym("matchable")).collect::<Vec<Bindings>>(),
assert_eq!(shared.as_match().unwrap().match_(&Atom::sym("matchable")).collect::<Vec<Bindings>>(),
vec![bind!{ x: Atom::sym("match") }]);
assert_eq!(shared.execute(&mut vec![Atom::sym("arg")]), Ok(vec![Atom::sym("executed"), Atom::sym("arg")]));
}
Expand Down
Loading

0 comments on commit 728c100

Please sign in to comment.