Skip to content

Commit

Permalink
Merge pull request #800 from vsbogd/const-expr-atom
Browse files Browse the repository at this point in the history
Allow instantiating constant expression atoms in Rust code
  • Loading branch information
vsbogd authored Nov 14, 2024
2 parents 3136f80 + d9aebbc commit 7035497
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 84 deletions.
2 changes: 1 addition & 1 deletion c/src/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ pub unsafe extern "C" fn atom_get_space(atom: *const atom_ref_t) -> space_t {
}

/// Private convenience function to call an `c_atom_vec_callback_t` callback with each atom in a vec
pub(crate) fn return_atoms(atoms: &Vec<Atom>, callback: c_atom_vec_callback_t, context: *mut c_void) {
pub(crate) fn return_atoms(atoms: &[Atom], callback: c_atom_vec_callback_t, context: *mut c_void) {
callback(&(&atoms[..]).into(), context);
}

Expand Down
4 changes: 2 additions & 2 deletions c/src/metta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ pub extern "C" fn atom_error_message(atom: *const atom_ref_t, buf: *mut c_char,
/// @return The `atom_t` representing the atom
/// @note The returned `atom_t` must be freed with `atom_free()`
///
#[no_mangle] pub extern "C" fn ATOM_TYPE_UNIT() -> atom_t { hyperon::metta::UNIT_TYPE().into() }
#[no_mangle] pub extern "C" fn ATOM_TYPE_UNIT() -> atom_t { hyperon::metta::UNIT_TYPE.into() }

/// @brief Creates a Symbol atom for the special MeTTa symbol used to indicate empty results
/// returned by function.
Expand All @@ -636,7 +636,7 @@ pub extern "C" fn atom_error_message(atom: *const atom_ref_t, buf: *mut c_char,
/// @note The returned `atom_t` must be freed with `atom_free()`
///
#[no_mangle] pub extern "C" fn UNIT_ATOM() -> atom_t {
hyperon::metta::UNIT_ATOM().into()
hyperon::metta::UNIT_ATOM.into()
}

/// @brief Creates a Symbol atom for the special MeTTa symbol used to indicate
Expand Down
58 changes: 32 additions & 26 deletions lib/src/atom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,16 @@ macro_rules! expr {
use $crate::*;
(&&$crate::Wrap($x)).to_atom()
}};
(($($x:tt)*)) => { $crate::Atom::expr(vec![ $( expr!($x) , )* ]) };
($($x:tt)*) => { $crate::Atom::expr(vec![ $( expr!($x) , )* ]) };
(($($x:tt)*)) => { $crate::Atom::expr([ $( expr!($x) , )* ]) };
($($x:tt)*) => { $crate::Atom::expr([ $( expr!($x) , )* ]) };
}

#[macro_export]
macro_rules! constexpr {
() => { $crate::Atom::Expression($crate::ExpressionAtom::new($crate::common::collections::CowArray::Literal(&[]))) };
($x:literal) => { $crate::Atom::Symbol($crate::SymbolAtom::new($crate::common::collections::ImmutableString::Literal($x))) };
(($($x:tt)*)) => { $crate::Atom::Expression($crate::ExpressionAtom::new($crate::common::collections::CowArray::Literal(const { &[ $( constexpr!($x) , )* ] }))) };
($($x:tt)*) => { $crate::Atom::Expression($crate::ExpressionAtom::new($crate::common::collections::CowArray::Literal(const { &[ $( constexpr!($x) , )* ] }))) };
}

/// Constructs new symbol atom. Can be used to construct `const` instances.
Expand Down Expand Up @@ -117,7 +125,7 @@ use std::any::Any;
use std::fmt::{Display, Debug};
use std::convert::TryFrom;

use crate::common::collections::ImmutableString;
use crate::common::collections::{ImmutableString, CowArray};

// Symbol atom

Expand All @@ -130,7 +138,6 @@ pub struct SymbolAtom {
impl SymbolAtom {
/// Constructs new symbol from `name`. Not intended to be used directly,
/// use [sym!] or [Atom::sym] instead.
#[doc(hidden)]
pub const fn new(name: ImmutableString) -> Self {
Self{ name }
}
Expand All @@ -152,14 +159,13 @@ impl Display for SymbolAtom {
/// An expression atom structure.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpressionAtom {
children: Vec<Atom>,
children: CowArray<Atom>,
}

impl ExpressionAtom {
/// Constructs new expression from vector of sub-atoms. Not intended to be
/// used directly, use [Atom::expr] instead.
#[doc(hidden)]
pub(crate) fn new(children: Vec<Atom>) -> Self {
/// used directly, use [expr!], [constexpr!] or [Atom::expr] instead.
pub const fn new(children: CowArray<Atom>) -> Self {
Self{ children }
}

Expand All @@ -169,18 +175,18 @@ impl ExpressionAtom {
}

/// Returns a reference to a vector of sub-atoms.
pub fn children(&self) -> &Vec<Atom> {
&self.children
pub fn children(&self) -> &[Atom] {
self.children.as_slice()
}

/// Returns a mutable reference to a vector of sub-atoms.
pub fn children_mut(&mut self) -> &mut Vec<Atom> {
&mut self.children
pub fn children_mut(&mut self) -> &mut [Atom] {
self.children.as_slice_mut()
}

/// Converts into a vector of sub-atoms.
pub fn into_children(self) -> Vec<Atom> {
self.children
self.children.into()
}
}

Expand Down Expand Up @@ -837,7 +843,7 @@ impl Atom {
/// assert_eq!(expr, same_expr);
/// assert_ne!(expr, other_expr);
/// ```
pub fn expr<T: Into<Vec<Atom>>>(children: T) -> Self {
pub fn expr<T: Into<CowArray<Atom>>>(children: T) -> Self {
Self::Expression(ExpressionAtom::new(children.into()))
}

Expand Down Expand Up @@ -1013,7 +1019,7 @@ impl<'a> TryFrom<&'a Atom> for &'a [Atom] {
type Error = &'static str;
fn try_from(atom: &Atom) -> Result<&[Atom], &'static str> {
match atom {
Atom::Expression(expr) => Ok(expr.children().as_slice()),
Atom::Expression(expr) => Ok(expr.children()),
_ => Err("Atom is not an ExpressionAtom")
}
}
Expand All @@ -1023,7 +1029,7 @@ impl<'a> TryFrom<&'a mut Atom> for &'a mut [Atom] {
type Error = &'static str;
fn try_from(atom: &mut Atom) -> Result<&mut [Atom], &'static str> {
match atom {
Atom::Expression(expr) => Ok(expr.children_mut().as_mut_slice()),
Atom::Expression(expr) => Ok(expr.children_mut()),
_ => Err("Atom is not an ExpressionAtom")
}
}
Expand Down Expand Up @@ -1093,8 +1099,8 @@ mod test {
}

#[inline]
fn expression(children: Vec<Atom>) -> Atom {
Atom::Expression(ExpressionAtom{ children })
fn expression<const N: usize>(children: [Atom; N]) -> Atom {
Atom::Expression(ExpressionAtom::new(CowArray::Allocated(Box::new(children))))
}

#[inline]
Expand Down Expand Up @@ -1175,15 +1181,15 @@ mod test {
#[test]
fn test_expr_expression() {
assert_eq!(expr!("=" ("fact" n) ("*" n ("-" n "1"))),
expression(vec![symbol("="), expression(vec![symbol("fact"), variable("n")]),
expression(vec![symbol("*"), variable("n"),
expression(vec![symbol("-"), variable("n"), symbol("1") ]) ]) ]));
expression([symbol("="), expression([symbol("fact"), variable("n")]),
expression([symbol("*"), variable("n"),
expression([symbol("-"), variable("n"), symbol("1") ]) ]) ]));
assert_eq!(expr!("=" n {[1, 2, 3]}),
expression(vec![symbol("="), variable("n"), value([1, 2, 3])]));
expression([symbol("="), variable("n"), value([1, 2, 3])]));
assert_eq!(expr!("=" {6} ("fact" n)),
expression(vec![symbol("="), value(6), expression(vec![symbol("fact"), variable("n")])]));
expression([symbol("="), value(6), expression([symbol("fact"), variable("n")])]));
assert_eq!(expr!({TestMulX(3)} {TestInteger(6)}),
expression(vec![grounded(TestMulX(3)), grounded(TestInteger(6))]));
expression([grounded(TestMulX(3)), grounded(TestInteger(6))]));
}

#[test]
Expand Down Expand Up @@ -1239,8 +1245,8 @@ mod test {
assert_eq!(Atom::gnd(TestMulX(3)).clone(), grounded(TestMulX(3)));
assert_eq!(Atom::expr([Atom::sym("="), Atom::value(6),
Atom::expr([Atom::sym("fact"), Atom::var("n")])]).clone(),
expression(vec![symbol("="), value(6),
expression(vec![symbol("fact"), variable("n")])]));
expression([symbol("="), value(6),
expression([symbol("fact"), variable("n")])]));
}

#[test]
Expand Down
91 changes: 91 additions & 0 deletions lib/src/common/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,97 @@ impl From<String> for ImmutableString {
}
}

#[derive(Debug, Clone)]
pub enum CowArray<T: 'static> {
Allocated(Box<[T]>),
Literal(&'static [T]),
}

impl<T: 'static> CowArray<T> {

pub fn new() -> Self {
Self::Literal(&[])
}

pub fn as_slice(&self) -> &[T] {
match self {
Self::Allocated(array) => &*array,
Self::Literal(array) => array,
}
}

pub fn as_slice_mut(&mut self) -> &mut [T] where T: Clone {
match self {
Self::Allocated(array) => &mut *array,
Self::Literal(array) => {
*self = Self::Allocated((*array).into());
self.as_slice_mut()
}
}
}

pub fn len(&self) -> usize {
self.as_slice().len()
}

pub fn iter(&self) -> impl Iterator<Item=&T> {
self.as_slice().iter()
}
}

impl<T: PartialEq> PartialEq for CowArray<T> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}

impl<T: Eq> Eq for CowArray<T> {}

impl<T: Display> Display for CowArray<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")
.and_then(|_| self.as_slice().iter().take(1).fold(Ok(()),
|res, atom| res.and_then(|_| write!(f, "{}", atom))))
.and_then(|_| self.as_slice().iter().skip(1).fold(Ok(()),
|res, atom| res.and_then(|_| write!(f, " {}", atom))))
.and_then(|_| write!(f, "]"))
}
}

impl<T: 'static> From<&'static [T]> for CowArray<T> {
fn from(a: &'static [T]) -> Self {
CowArray::Literal(a)
}
}

impl<T, const N: usize> From<[T; N]> for CowArray<T> {
fn from(a: [T; N]) -> Self {
CowArray::Allocated(Box::new(a))
}
}

impl<T> From<Vec<T>> for CowArray<T> {
fn from(v: Vec<T>) -> Self {
CowArray::Allocated(v.into_boxed_slice())
}
}

impl<T: Clone> Into<Vec<T>> for CowArray<T> {
fn into(self) -> Vec<T> {
match self {
Self::Allocated(array) => array.into(),
Self::Literal(array) => array.into(),
}
}
}

impl<'a, T> Into<&'a [T]> for &'a CowArray<T> {
fn into(self) -> &'a [T] {
self.as_slice()
}
}


#[cfg(test)]
mod test {
use super::*;
Expand Down
34 changes: 20 additions & 14 deletions lib/src/metta/interpreter_minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::space::*;
use crate::metta::*;
use crate::metta::types::*;
use crate::metta::runner::stdlib_minimal::IfEqualOp;
use crate::common::collections::CowArray;

use std::fmt::{Debug, Display, Formatter};
use std::convert::TryFrom;
Expand Down Expand Up @@ -652,16 +653,18 @@ fn function_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Op
}

fn collapse_bind(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
let Stack{ prev, atom: mut collapse, ret: _, finished: _, vars } = stack;
let Stack{ prev, atom: collapse, ret: _, finished: _, vars } = stack;

let mut nested = Atom::expr([]);
match &mut collapse {
let collapse = match collapse {
Atom::Expression(expr) => {
std::mem::swap(&mut nested, &mut expr.children_mut()[1]);
expr.children_mut().push(Atom::value(bindings.clone()))
let mut children = expr.into_children();
std::mem::swap(&mut nested, &mut children[1]);
children.push(Atom::value(bindings.clone()));
Atom::expr(children)
},
_ => panic!("Unexpected state"),
}
};

let prev = Stack::from_prev_with_vars(prev, collapse, vars, collapse_bind_ret);
let prev = Rc::new(RefCell::new(prev));
Expand All @@ -672,16 +675,19 @@ fn collapse_bind(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {

fn collapse_bind_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> {
let nested = atom;
{
if nested != EMPTY_SYMBOL {
let stack_ref = &mut *stack.borrow_mut();
let Stack{ prev: _, atom: collapse, ret: _, finished: _, vars: _ } = stack_ref;
let finished = match atom_as_slice_mut(collapse) {
Some([_op, Atom::Expression(finished), _bindings]) => finished,
match atom_as_slice_mut(collapse) {
Some([_op, Atom::Expression(finished_placeholder), _bindings]) => {
let mut finished = ExpressionAtom::new(CowArray::new());
std::mem::swap(&mut finished, finished_placeholder);
let mut finished = finished.into_children();
finished.push(atom_bindings_into_atom(nested, bindings));
std::mem::swap(&mut ExpressionAtom::new(finished.into()), finished_placeholder);
},
_ => panic!("Unexpected state"),
};
if nested != EMPTY_SYMBOL {
finished.children_mut().push(atom_bindings_into_atom(nested, bindings));
}
}

// all alternatives are evaluated
Expand Down Expand Up @@ -1105,9 +1111,9 @@ fn interpret_function(args: Atom, bindings: Bindings) -> MettaResult {
let mut call = atom.clone().into_children();
let head = call.remove(0);
let args = call;
let mut arg_types = op_type.clone();
arg_types.children_mut().remove(0);
let arg_types = Atom::Expression(arg_types);
let mut arg_types: Vec<Atom> = op_type.children().into();
arg_types.remove(0);
let arg_types = Atom::expr(arg_types);
let rop = Atom::Variable(VariableAtom::new("rop").make_unique());
let rargs = Atom::Variable(VariableAtom::new("rargs").make_unique());
let result = Atom::Variable(VariableAtom::new("result").make_unique());
Expand Down
22 changes: 12 additions & 10 deletions lib/src/metta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,8 @@ pub const SUPERPOSE_BIND_SYMBOL : Atom = sym!("superpose-bind");
pub const METTA_SYMBOL : Atom = sym!("metta");
pub const CALL_NATIVE_SYMBOL : Atom = sym!("call-native");

//TODO: convert these from functions to static strcutures, when Atoms are Send+Sync
#[allow(non_snake_case)]
pub fn UNIT_ATOM() -> Atom {
Atom::expr([])
}

#[allow(non_snake_case)]
pub fn UNIT_TYPE() -> Atom {
Atom::expr([ARROW_SYMBOL])
}
pub const UNIT_ATOM: Atom = constexpr!();
pub const UNIT_TYPE: Atom = constexpr!(("->"));

/// Initializes an error expression atom
pub fn error_atom(err_atom: Option<Atom>, err_code: Option<Atom>, message: String) -> Atom {
Expand Down Expand Up @@ -95,3 +87,13 @@ pub fn atom_error_message(atom: &Atom) -> &str {
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn unit_type() {
assert_eq!(UNIT_ATOM, Atom::expr([]));
assert_eq!(UNIT_TYPE, Atom::expr([ARROW_SYMBOL]));
}
}
Loading

0 comments on commit 7035497

Please sign in to comment.