Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow instantiating constant expression atoms in Rust code #800

Merged
merged 3 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading