From 56916cd8b2a9c1d86fc90ae31c35f5da242d96c1 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Tue, 30 Jan 2024 17:39:07 +0300 Subject: [PATCH 01/16] Automatically convert None to unit in Python grounded functions When unwrap=True, automatically convert result of Python grounded functions which return None to unit (). --- python/hyperon/atoms.py | 5 ++++- python/tests/test_atom.py | 10 ++++++++++ python/tests/test_grounded_type.py | 7 ++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index ed592284d..5c6afce73 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -322,7 +322,10 @@ def execute(self, *args, res_typ=AtomType.UNDEFINED): # raise RuntimeError("Grounded operation " + self.name + " with unwrap=True expects only grounded arguments") raise NoReduceError() args = [arg.get_object().content for arg in args] - return [G(ValueObject(self.op(*args)), res_typ)] + result = self.op(*args) + if result is None: + return [E()] + return [G(ValueObject(result), res_typ)] else: result = self.op(*args) if not isinstance(result, list): diff --git a/python/tests/test_atom.py b/python/tests/test_atom.py index fcf92e588..71f86ed20 100644 --- a/python/tests/test_atom.py +++ b/python/tests/test_atom.py @@ -105,6 +105,16 @@ def x2_op(atom): self.assertEqual(interpret(space, expr), [E(S('Error'), expr, S('Grounded operation which is defined using unwrap=False should return atom instead of Python type'))]) + def test_grounded_no_return(self): + space = GroundingSpaceRef() + def print_op(input): + print(input) + + printExpr = E(OperationAtom('print', print_op, type_names=["Atom", "->"], unwrap=False), S("test")) + self.assertTrue(atom_is_error(interpret(space, printExpr)[0])) + printExpr = E(OperationAtom('print', print_op, type_names=["Atom", "->"], unwrap=True), ValueAtom("test")) + self.assertEqual(interpret(space, printExpr), [E()]) + def test_plan(self): space = GroundingSpaceRef() interpreter = Interpreter(space, E(x2Atom, ValueAtom(1))) diff --git a/python/tests/test_grounded_type.py b/python/tests/test_grounded_type.py index abe155631..314f5b35c 100644 --- a/python/tests/test_grounded_type.py +++ b/python/tests/test_grounded_type.py @@ -22,11 +22,8 @@ def test_apply_type(self): metta.register_atom("untyped", ValueAtom(None)) metta.register_atom("untop", OperationAtom("untop", lambda: None)) self.assertEqual( - metta.run("!(untop)")[0][0].get_grounded_type(), - metta.parse_single("untyped").get_grounded_type()) - self.assertNotEqual( - metta.run("!(untop)")[0][0].get_grounded_type(), - metta.run("!(+ 1 1)")[0][0].get_grounded_type()) + metta.run("!(untop)")[0][0], + metta.parse_single("()")) self.assertNotEqual( metta.run("!(> 1 1)")[0][0].get_grounded_type(), metta.run("!(+ 1 1)")[0][0].get_grounded_type()) From 3a806baf009031527b222d1d2c0c25a73c9f2e24 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Tue, 30 Jan 2024 17:57:07 +0300 Subject: [PATCH 02/16] Add Atoms.UNIT and AtomType.UNIT constants into Python API Bind these constants to the corresponding Rust atoms. --- c/src/metta.rs | 21 +++++++++++++++++++-- python/hyperon/atoms.py | 4 +++- python/hyperonpy.cpp | 8 +++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index 29f0ed9bf..d2835e223 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -557,16 +557,33 @@ pub extern "C" fn atom_error_message(atom: *const atom_ref_t, buf: *mut c_char, /// #[no_mangle] pub extern "C" fn ATOM_TYPE_GROUNDED_SPACE() -> atom_t { rust_type_atom::().into() } +/// @brief Creates an atom used to indicate that an atom's type is a unit type. +/// @ingroup metta_language_group +/// @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() } + /// @brief Creates a Symbol atom for the special MeTTa symbol used to indicate empty results /// returned by function. /// @ingroup metta_language_group -/// @return The `atom_t` representing the Void atom +/// @return The `atom_t` representing the Empty atom /// @note The returned `atom_t` must be freed with `atom_free()` /// -#[no_mangle] pub extern "C" fn EMPTY_SYMBOL() -> atom_t { +#[no_mangle] pub extern "C" fn EMPTY_ATOM() -> atom_t { hyperon::metta::EMPTY_SYMBOL.into() } +/// @brief Creates an atom used to return from functions which are not +/// supposed to return results (print for example). +/// @ingroup metta_language_group +/// @return The `atom_t` representing the Unit atom +/// @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() +} + /// @brief Checks whether Atom `atom` has Type `typ` in context of `space` /// @ingroup metta_language_group /// @param[in] space A pointer to the `space_t` representing the space context in which to perform the check diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 5c6afce73..3c04f069a 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -116,10 +116,12 @@ class AtomType: EXPRESSION = Atom._from_catom(hp.CAtomType.EXPRESSION) GROUNDED = Atom._from_catom(hp.CAtomType.GROUNDED) GROUNDED_SPACE = Atom._from_catom(hp.CAtomType.GROUNDED_SPACE) + UNIT = Atom._from_catom(hp.CAtomType.UNIT) class Atoms: EMPTY = Atom._from_catom(hp.CAtoms.EMPTY) + UNIT = Atom._from_catom(hp.CAtoms.UNIT) class GroundedAtom(Atom): """ @@ -324,7 +326,7 @@ def execute(self, *args, res_typ=AtomType.UNDEFINED): args = [arg.get_object().content for arg in args] result = self.op(*args) if result is None: - return [E()] + return [Atoms.UNIT] return [G(ValueObject(result), res_typ)] else: result = self.op(*args) diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index 94dc0e0e6..5507435e3 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -773,7 +773,8 @@ PYBIND11_MODULE(hyperonpy, m) { ADD_TYPE(VARIABLE, "Variable") ADD_TYPE(EXPRESSION, "Expression") ADD_TYPE(GROUNDED, "Grounded") - ADD_TYPE(GROUNDED_SPACE, "Space"); + ADD_TYPE(GROUNDED_SPACE, "Space") + ADD_TYPE(UNIT, "Unit"); m.def("check_type", [](CSpace space, CAtom& atom, CAtom& type) { return check_type(space.ptr(), atom.ptr(), type.ptr()); }, "Check if atom is an instance of the passed type"); @@ -786,10 +787,11 @@ PYBIND11_MODULE(hyperonpy, m) { return atoms; }, "Get types of the given atom"); -#define ADD_SYMBOL(t, d) .def_property_readonly_static(#t, [](py::object) { return CAtom(t ## _SYMBOL()); }, d " atom type") +#define ADD_ATOM(t, d) .def_property_readonly_static(#t, [](py::object) { return CAtom(t ## _ATOM()); }, d " atom type") py::class_(m, "CAtoms") - ADD_SYMBOL(EMPTY, "Empty"); + ADD_ATOM(EMPTY, "Empty") + ADD_ATOM(UNIT, "Unit"); py::class_(m, "CMetta"); m.def("metta_new", [](CSpace space, EnvBuilder env_builder) { From 2b02984b60a70edbdfa1c212b2973fba97fa598e Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Wed, 31 Jan 2024 12:22:36 +0300 Subject: [PATCH 03/16] Fix crash on retain all variables from single binding --- lib/src/atom/matcher.rs | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/lib/src/atom/matcher.rs b/lib/src/atom/matcher.rs index 0bde8deb6..596195d31 100644 --- a/lib/src/atom/matcher.rs +++ b/lib/src/atom/matcher.rs @@ -181,7 +181,7 @@ impl Bindings { self.bindings.remove(binding_id).atom } else { if binding.var == *var { - self.rename_binding(binding_id, var); + let _ = self.rename_binding(binding_id); } None } @@ -190,14 +190,15 @@ impl Bindings { } } - fn rename_binding(&mut self, binding_id: usize, old_var: &VariableAtom) { + fn rename_binding(&mut self, binding_id: usize) -> Result<(), ()> { let binding = &mut self.bindings[binding_id]; let var = self.binding_by_var.iter() - .filter(|(v, i)| **i == binding.id && *v != old_var) + .filter(|(v, i)| **i == binding.id && **v != binding.var) .map(|(v, _i)| v) .next() - .expect("Unexpected state"); + .ok_or(())?; binding.var = var.clone(); + Ok(()) } fn move_binding_to_binding(&mut self, from_binding_id: usize, to_binding_id: usize) { @@ -1712,6 +1713,35 @@ mod test { Ok(()) } + #[test] + fn bindings_retain_all() -> Result<(), &'static str> { + let mut bindings = Bindings::new() + .add_var_equality(&VariableAtom::new("a"), &VariableAtom::new("b"))?; + bindings.retain(|_| false); + assert!(bindings.is_empty()); + Ok(()) + } + + #[test] + fn bindings_rename_binding() -> Result<(), &'static str> { + let a = &VariableAtom::new("a"); + let b = &VariableAtom::new("b"); + let mut bindings = Bindings::new().add_var_equality(a, b)?; + let binding_id = bindings.get_binding(a).unwrap().id; + assert_eq!(bindings.resolve(a), Some(Atom::Variable(a.clone()))); + assert_eq!(bindings.resolve(b), Some(Atom::Variable(a.clone()))); + + assert_eq!(bindings.rename_binding(binding_id), Ok(())); + assert_eq!(bindings.resolve(a), Some(Atom::Variable(b.clone()))); + assert_eq!(bindings.resolve(b), Some(Atom::Variable(b.clone()))); + + let mut bindings = Bindings::new().with_var_no_value(a); + let binding_id = bindings.get_binding(a).unwrap().id; + assert_eq!(bindings.rename_binding(binding_id), Err(())); + assert_eq!(bindings.resolve(a), Some(Atom::Variable(a.clone()))); + Ok(()) + } + #[ignore = "Unstable because HashMap doesn't guarantee the order of entries"] #[test] fn bindings_into_entries() -> Result<(), &'static str> { From b3db1d9b93cf054b2faabc22c8e34778db3a5c50 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 31 Jan 2024 18:09:55 +0300 Subject: [PATCH 04/16] move das_gate to exts --- python/hyperon/exts/das_gate/__init__.py | 1 + python/{sandbox => hyperon/exts}/das_gate/dasgate.py | 0 python/{sandbox/das_gate => integration}/test_das.metta | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 python/hyperon/exts/das_gate/__init__.py rename python/{sandbox => hyperon/exts}/das_gate/dasgate.py (100%) rename python/{sandbox/das_gate => integration}/test_das.metta (96%) diff --git a/python/hyperon/exts/das_gate/__init__.py b/python/hyperon/exts/das_gate/__init__.py new file mode 100644 index 000000000..5f4c01e18 --- /dev/null +++ b/python/hyperon/exts/das_gate/__init__.py @@ -0,0 +1 @@ +from .dasgate import das_atoms, my_glob_atoms diff --git a/python/sandbox/das_gate/dasgate.py b/python/hyperon/exts/das_gate/dasgate.py similarity index 100% rename from python/sandbox/das_gate/dasgate.py rename to python/hyperon/exts/das_gate/dasgate.py diff --git a/python/sandbox/das_gate/test_das.metta b/python/integration/test_das.metta similarity index 96% rename from python/sandbox/das_gate/test_das.metta rename to python/integration/test_das.metta index d02f53aab..e2a0e3b10 100644 --- a/python/sandbox/das_gate/test_das.metta +++ b/python/integration/test_das.metta @@ -1,4 +1,4 @@ -!(extend-py! dasgate) +!(extend-py! das_gate) !(bind! &das (new-das)) ;!(bind! &das (new-remote-das (Error "Put your IP here" ()) 8081)) From e08c2a88db25c9d9ffd248746f77e2c40f8a9693 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Wed, 31 Jan 2024 22:22:02 -0800 Subject: [PATCH 05/16] Updating Cargo.toml to use rustyline v.13, now that our changes have been released in the upstream branch --- repl/Cargo.toml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/repl/Cargo.toml b/repl/Cargo.toml index afaf592a6..31cba87f9 100644 --- a/repl/Cargo.toml +++ b/repl/Cargo.toml @@ -6,10 +6,7 @@ description = "A shell to execute MeTTa" [dependencies] anyhow = { version = "1.0.75", features = ["std"] } -# rustyline = { version = "12.0.0", features = ["derive"] } -# rustyline = {git = "https://github.com/luketpeterson/rustyline", version = "12.0.0", features = ["derive"] } -# TODO: Yay, our fix landed in main. Still needs to publish however. One step closer -rustyline = {git = "https://github.com/kkawakam/rustyline", version = "12.0.0", features = ["derive"] } +rustyline = { version = "13.0.0", features = ["derive"] } clap = { version = "4.4.0", features = ["derive"] } signal-hook = "0.3.17" pyo3 = { version = "0.19.2", features = ["auto-initialize"], optional = true } @@ -24,4 +21,4 @@ path = "src/main.rs" default = ["python"] no_python = ["hyperon"] python = ["pyo3", "pep440_rs"] -minimal = ["hyperon/minimal", "no_python"] +minimal = ["hyperon/minimal", "no_python"] #WARNING: The interpreter belonging to the hyperon python module will be used if hyperon is run through python From b560b8b4076d374266086e484636256a9b4b90b0 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 12 Jan 2024 20:03:03 +0300 Subject: [PATCH 06/16] Rename function to separate errors and success results --- lib/src/metta/runner/stdlib.metta | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index 89509ed9e..7f944966c 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -131,8 +131,8 @@ (chain (eval (foldl-atom $tail $head-folded $a $b $op)) $res (return $res)) ))) (return $init) )))) -(: divide-errors (-> Expression Expression Expression)) -(= (divide-errors $succ-err $res) +(: separate-errors (-> Expression Expression Expression)) +(= (separate-errors $succ-err $res) (function (unify $succ-err ($suc $err) (unify $res ($a $_b) (eval (if-error $a @@ -145,8 +145,8 @@ (function (chain (collapse-bind $atom) $collapsed (chain (eval (foldl-atom $collapsed (() ()) $succ-err $res - (eval (divide-errors $succ-err $res)))) $divided - (unify $divided ($success $error) + (eval (separate-errors $succ-err $res)))) $separated + (unify $separated ($success $error) (chain (eval (if-equal $success () $error $success)) $filtered (chain (superpose-bind $filtered) $ret (return $ret)) ) (return (Error (check-alternatives $atom) "list of results was not filtered correctly")) ))))) From 03d5453c70591d04cb2483b4c6667147e13389bd Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 12 Jan 2024 20:04:03 +0300 Subject: [PATCH 07/16] Fix error message on type check --- lib/src/metta/runner/stdlib.metta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index 7f944966c..42cec6e83 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -195,7 +195,7 @@ (eval (match-types $actual-ret-type $ret-type (return ()) (return (Error $atom BadType)) )) - (return (Error (interpret-args $atom $args $arg-types $ret-type $space) "interpret-args expects a non-empty value for $arg-types argument")) )) + (return (Error $atom "Too many arguments")) )) (eval (if-decons-expr $args $head $tail (eval (if-decons-expr $arg-types $head-type $tail-types (chain (eval (interpret $head $head-type $space)) $reduced-head From 413542b198221cbdb8928a21fc67f085f6f16c65 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 12 Jan 2024 20:05:35 +0300 Subject: [PATCH 08/16] Rename Stack constructors --- lib/src/metta/interpreter2.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/metta/interpreter2.rs b/lib/src/metta/interpreter2.rs index 293df23bb..6ab9bd059 100644 --- a/lib/src/metta/interpreter2.rs +++ b/lib/src/metta/interpreter2.rs @@ -57,14 +57,14 @@ fn no_handler(_stack: Rc>, _atom: Atom, _bindings: Bindings) -> O } impl Stack { - fn from_prev_vars(prev: Option>>, atom: Atom, ret: ReturnHandler) -> Self { + fn from_prev_add_vars(prev: Option>>, atom: Atom, ret: ReturnHandler) -> Self { // TODO: vars are introduced in specific locations of the atom thus // in theory it is possible to optimize vars search for eval, unify and chain let vars = Self::vars(&prev, &atom); Self{ prev, atom, ret, finished: false, vars } } - fn from_prev_no_vars(prev: Option>>, atom: Atom, ret: ReturnHandler) -> Self { + fn from_prev_keep_vars(prev: Option>>, atom: Atom, ret: ReturnHandler) -> Self { let vars = Self::vars_copy(&prev); Self{ prev, atom, ret, finished: false, vars } } @@ -434,7 +434,7 @@ fn eval<'a, T: SpaceRef<'a>>(context: &InterpreterContext<'a, T>, stack: Stack, results.into_iter() .map(|atom| { let stack = if is_function_op(&atom) { - let call = Stack::from_prev_no_vars(prev.clone(), query_atom.clone(), call_ret); + let call = Stack::from_prev_keep_vars(prev.clone(), query_atom.clone(), call_ret); atom_to_stack(atom, Some(Rc::new(RefCell::new(call)))) } else { Stack::finished(prev.clone(), atom) @@ -471,7 +471,7 @@ fn query<'a, T: SpaceRef<'a>>(space: T, prev: Option>>, atom: .flat_map(|mut b| { let res = apply_bindings_to_atom(&atom_x, &b); let stack = if is_function_op(&res) { - let call = Stack::from_prev_no_vars(prev.clone(), atom.clone(), call_ret); + let call = Stack::from_prev_keep_vars(prev.clone(), atom.clone(), call_ret); atom_to_stack(res, Some(Rc::new(RefCell::new(call)))) } else { Stack::finished(prev.clone(), res) @@ -509,10 +509,10 @@ fn atom_to_stack(atom: Atom, prev: Option>>) -> Stack { }, Some([op, ..]) if *op == EVAL_SYMBOL || *op == UNIFY_SYMBOL => { - Stack::from_prev_vars(prev, atom, no_handler) + Stack::from_prev_add_vars(prev, atom, no_handler) }, _ => { - Stack::from_prev_no_vars(prev, atom, no_handler) + Stack::from_prev_keep_vars(prev, atom, no_handler) }, }; result @@ -528,7 +528,7 @@ fn chain_to_stack(mut atom: Atom, prev: Option>>) -> Stack { }, }; std::mem::swap(nested_arg, &mut nested); - let cur = Stack::from_prev_vars(prev, atom, chain_ret); + let cur = Stack::from_prev_add_vars(prev, atom, chain_ret); atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) } @@ -567,7 +567,7 @@ fn function_to_stack(mut atom: Atom, prev: Option>>) -> Stack }, }; std::mem::swap(nested_arg, &mut nested); - let cur = Stack::from_prev_no_vars(prev, atom, function_ret); + let cur = Stack::from_prev_keep_vars(prev, atom, function_ret); atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) } @@ -602,7 +602,7 @@ fn collapse_bind_to_stack(mut atom: Atom, prev: Option>>) -> S }, }; std::mem::swap(nested_arg, &mut nested); - let cur = Stack::from_prev_no_vars(prev, atom, collapse_bind_ret); + let cur = Stack::from_prev_keep_vars(prev, atom, collapse_bind_ret); atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) } From 6b404572fde7fd2032cbc3cf9dcd18cac1ae8ab5 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 25 Jan 2024 11:14:12 +0300 Subject: [PATCH 09/16] Fix variable cleanup after query Take into account new variables which can be inserted to atom after bindings are applied. --- lib/src/metta/interpreter2.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/metta/interpreter2.rs b/lib/src/metta/interpreter2.rs index 6ab9bd059..3c6e97373 100644 --- a/lib/src/metta/interpreter2.rs +++ b/lib/src/metta/interpreter2.rs @@ -454,34 +454,34 @@ fn eval<'a, T: SpaceRef<'a>>(context: &InterpreterContext<'a, T>, stack: Stack, }, _ if is_embedded_op(&query_atom) => vec![InterpretedAtom(atom_to_stack(query_atom, prev), bindings)], - _ => query(&context.space, prev, query_atom, bindings, &vars), + _ => query(&context.space, prev, query_atom, bindings, vars), } } -fn query<'a, T: SpaceRef<'a>>(space: T, prev: Option>>, atom: Atom, bindings: Bindings, vars: &Variables) -> Vec { - let var_x = VariableAtom::new("X").make_unique(); +fn query<'a, T: SpaceRef<'a>>(space: T, prev: Option>>, atom: Atom, bindings: Bindings, _vars: Variables) -> Vec { + let var_x = &VariableAtom::new("X").make_unique(); let query = Atom::expr([EQUAL_SYMBOL, atom.clone(), Atom::Variable(var_x.clone())]); let results = space.query(&query); - let atom_x = Atom::Variable(var_x); + let atom_x = Atom::Variable(var_x.clone()); let results: Vec = { log::debug!("interpreter2::query: query: {}", query); log::debug!("interpreter2::query: results.len(): {}, bindings.len(): {}, results: {} bindings: {}", results.len(), bindings.len(), results, bindings); results.into_iter() - .flat_map(|mut b| { + .flat_map(|b| { let res = apply_bindings_to_atom(&atom_x, &b); let stack = if is_function_op(&res) { - let call = Stack::from_prev_keep_vars(prev.clone(), atom.clone(), call_ret); + let call = Stack::from_prev_add_vars(prev.clone(), atom.clone(), call_ret); atom_to_stack(res, Some(Rc::new(RefCell::new(call)))) } else { - Stack::finished(prev.clone(), res) + Stack::finished_add_vars(prev.clone(), res) }; - b.retain(|v| vars.contains(v)); log::debug!("interpreter2::query: b: {}", b); - b.merge_v2(&bindings).into_iter().filter_map(move |b| { + b.merge_v2(&bindings).into_iter().filter_map(move |mut b| { if b.has_loops() { None } else { + b.retain(|v| stack.vars.contains(v)); Some(InterpretedAtom(stack.clone(), b)) } }) From 269829815728cef4aa1ba05582d63dc8fd343951 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 25 Jan 2024 11:42:35 +0300 Subject: [PATCH 10/16] Merge variable bindings from superpose with current ones This fixes the situation when new bindings are added into current working set between collapse and superpose calls. --- lib/src/metta/interpreter2.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/src/metta/interpreter2.rs b/lib/src/metta/interpreter2.rs index 3c6e97373..c5ffe3954 100644 --- a/lib/src/metta/interpreter2.rs +++ b/lib/src/metta/interpreter2.rs @@ -38,6 +38,7 @@ macro_rules! match_atom { type ReturnHandler = fn(Rc>, Atom, Bindings) -> Option; #[derive(Debug, Clone)] +#[cfg_attr(test, derive(PartialEq))] struct Stack { // Internal mutability is required to implement collapse-bind. All alternatives // reference the same collapse-bind Stack instance. When some alternative @@ -126,6 +127,7 @@ impl Display for Stack { } #[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] struct InterpretedAtom(Stack, Bindings); impl Display for InterpretedAtom { @@ -304,6 +306,7 @@ fn is_function_op(atom: &Atom) -> bool { } #[derive(Debug, Clone)] +#[cfg_attr(test, derive(PartialEq))] struct Variables(im::HashSet); impl Variables { @@ -732,8 +735,12 @@ fn superpose_bind(stack: Stack, bindings: Bindings) -> Vec { }; collapsed.into_children().into_iter() .map(atom_into_atom_bindings) - .map(|(atom, bindings)| { - InterpretedAtom(Stack::finished(prev.clone(), atom), bindings) + .flat_map(|(atom, b)| { + let prev = &prev; + b.merge_v2(&bindings).into_iter() + .map(move |b| { + InterpretedAtom(Stack::finished(prev.clone(), atom.clone()), b) + }) }) .collect() } @@ -975,6 +982,19 @@ mod tests { } + #[test] + fn test_superpose_bind() { + let vars = Variables::new(); + let atom = Atom::expr([Atom::sym("superpose-bind"), + Atom::expr([atom_bindings_into_atom(expr!("some-atom"), bind!{ a: expr!("A") })])]); + let stack = Stack{ prev: None, atom, ret: no_handler, finished: false, vars: vars.clone() }; + let result = superpose_bind(stack, bind!{ b: expr!("B") }); + assert_eq!(result, vec![InterpretedAtom( + Stack{ prev: None, atom: expr!("some-atom"), ret: no_handler, finished: true, vars }, + bind!{ a: expr!("A"), b: expr!("B") } + )]); + } + #[test] fn metta_turing_machine() { let space = space(" From ed6bcbd19cd4287f487cd7ba4bd31ca8c6e4b535 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 25 Jan 2024 19:41:29 +0300 Subject: [PATCH 11/16] Fix collapse to keep variable context which was created before calling it When collapse is called and then results are filtered, in case when all results are filtered out after collapse we still need to keep bindings of the variables which were found before calling collapse. When collapse results are passed to the superpose we need to merge these results with bindings inside collapse results. --- lib/src/metta/interpreter2.rs | 107 +++++++++++++++++----------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/lib/src/metta/interpreter2.rs b/lib/src/metta/interpreter2.rs index c5ffe3954..142b9c27e 100644 --- a/lib/src/metta/interpreter2.rs +++ b/lib/src/metta/interpreter2.rs @@ -35,7 +35,7 @@ macro_rules! match_atom { } }; } -type ReturnHandler = fn(Rc>, Atom, Bindings) -> Option; +type ReturnHandler = fn(Rc>, Atom, Bindings) -> Option<(Stack, Bindings)>; #[derive(Debug, Clone)] #[cfg_attr(test, derive(PartialEq))] @@ -53,7 +53,7 @@ struct Stack { vars: Variables, } -fn no_handler(_stack: Rc>, _atom: Atom, _bindings: Bindings) -> Option { +fn no_handler(_stack: Rc>, _atom: Atom, _bindings: Bindings) -> Option<(Stack, Bindings)> { panic!("Unexpected state"); } @@ -355,10 +355,8 @@ fn interpret_nested_atom<'a, T: SpaceRef<'a>>(context: &InterpreterContext<'a, T None => panic!("Unexpected state"), }; let ret = prev.borrow().ret; - // TODO: two copies of bindings here; we could try to include bindings - // into Stack and eliminate copying - ret(prev, atom, bindings.clone()) - .map_or(vec![], |stack| vec![InterpretedAtom(stack, bindings)]) + ret(prev, atom, bindings) + .map_or(vec![], |(stack, bindings)| vec![InterpretedAtom(stack, bindings)]) } else { let expr = atom_as_slice(&stack.atom); let result = match expr { @@ -507,9 +505,6 @@ fn atom_to_stack(atom: Atom, prev: Option>>) -> Stack { Some([op, ..]) if *op == FUNCTION_SYMBOL => { function_to_stack(atom, prev) }, - Some([op, ..]) if *op == COLLAPSE_BIND_SYMBOL => { - collapse_bind_to_stack(atom, prev) - }, Some([op, ..]) if *op == EVAL_SYMBOL || *op == UNIFY_SYMBOL => { Stack::from_prev_add_vars(prev, atom, no_handler) @@ -535,7 +530,7 @@ fn chain_to_stack(mut atom: Atom, prev: Option>>) -> Stack { atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) } -fn chain_ret(stack: Rc>, atom: Atom, _bindings: Bindings) -> Option { +fn chain_ret(stack: Rc>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> { let mut stack = (*stack.borrow()).clone(); let nested = atom; let Stack{ prev: _, atom: chain, ret: _, finished: _, vars: _} = &mut stack; @@ -544,7 +539,7 @@ fn chain_ret(stack: Rc>, atom: Atom, _bindings: Bindings) -> Opti _ => panic!("Unexpected state"), }; *arg = nested; - Some(stack) + Some((stack, bindings)) } fn chain(stack: Stack, bindings: Bindings) -> Vec { @@ -574,70 +569,74 @@ fn function_to_stack(mut atom: Atom, prev: Option>>) -> Stack atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) } -fn call_ret(stack: Rc>, atom: Atom, _bindings: Bindings) -> Option { +fn call_ret(stack: Rc>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> { let mut stack = (*stack.borrow()).clone(); stack.atom = atom; stack.finished = true; - Some(stack) + Some((stack, bindings)) } -fn function_ret(stack: Rc>, atom: Atom, _bindings: Bindings) -> Option { +fn function_ret(stack: Rc>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> { match_atom!{ atom ~ [op, result] if *op == RETURN_SYMBOL => { let mut stack = (*stack.borrow()).clone(); stack.atom = result; stack.finished = true; - Some(stack) + Some((stack, bindings)) }, _ => { - Some(atom_to_stack(atom, Some(stack))) + Some((atom_to_stack(atom, Some(stack)), bindings)) } } } -fn collapse_bind_to_stack(mut atom: Atom, prev: Option>>) -> Stack { +fn collapse_bind(stack: Stack, bindings: Bindings) -> Vec { + let Stack{ prev, atom: mut collapse, ret: _, finished: _, vars: _ } = stack; + let mut nested = Atom::expr([]); - let nested_arg = match atom_as_slice_mut(&mut atom) { - Some([_op, nested @ Atom::Expression(_)]) => nested, - _ => { - let error: String = format!("expected: ({} (: Expression)), found: {}", COLLAPSE_BIND_SYMBOL, atom); - return Stack::finished(prev, error_atom(atom, error)); + match &mut collapse { + Atom::Expression(expr) => { + std::mem::swap(&mut nested, &mut expr.children_mut()[1]); + expr.children_mut().push(Atom::value(bindings.clone())) }, - }; - std::mem::swap(nested_arg, &mut nested); - let cur = Stack::from_prev_keep_vars(prev, atom, collapse_bind_ret); - atom_to_stack(nested, Some(Rc::new(RefCell::new(cur)))) + _ => panic!("Unexpected state"), + } + + let prev = Stack::from_prev_keep_vars(prev, collapse, collapse_bind_ret); + let cur = atom_to_stack(nested, Some(Rc::new(RefCell::new(prev)))); + vec![InterpretedAtom(cur, bindings)] } -fn collapse_bind_ret(stack: Rc>, atom: Atom, bindings: Bindings) -> Option { +fn collapse_bind_ret(stack: Rc>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> { let nested = atom; { - let stack = &mut *stack.borrow_mut(); - let Stack{ prev: _, atom: collapse, ret: _, finished: _, vars: _ } = stack; + 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)]) => finished, + Some([_op, Atom::Expression(finished), _bindings]) => finished, _ => panic!("Unexpected state"), }; finished.children_mut().push(atom_bindings_into_atom(nested, bindings)); } - Rc::into_inner(stack).map(RefCell::into_inner) + + match Rc::into_inner(stack).map(RefCell::into_inner) { + Some(stack) => { + let Stack{ prev, atom: collapse, ret: _, finished: _, vars: _ } = stack; + let (result, bindings) = match atom_into_array(collapse) { + Some([_op, result, bindings]) => (result, atom_into_bindings(bindings)), + None => panic!("Unexpected state"), + }; + // FIXME: need a way to return binding from .ret + Some((Stack::finished(prev, result), bindings)) + }, + None => None, + } } fn atom_bindings_into_atom(atom: Atom, bindings: Bindings) -> Atom { Atom::expr([atom, Atom::value(bindings)]) } -fn collapse_bind(stack: Stack, bindings: Bindings) -> Vec { - let Stack{ prev, atom: collapse, ret: _, finished: _, vars: _ } = stack; - let result = match_atom!{ - collapse ~ [_op, finished @ Atom::Expression(_)] => finished, - _ => { - panic!("Unexpected state") - } - }; - vec![InterpretedAtom(atom_to_stack(result, prev), bindings)] -} - fn unify(stack: Stack, bindings: Bindings) -> Vec { let Stack{ prev, atom: unify, ret: _, finished: _, vars } = stack; let (atom, pattern, then, else_) = match atom_as_slice(&unify) { @@ -707,23 +706,25 @@ fn cons_atom(stack: Stack, bindings: Bindings) -> Vec { fn atom_into_atom_bindings(pair: Atom) -> (Atom, Bindings) { match_atom!{ - pair ~ [atom, bindings] => { - match bindings.as_gnd::() { - Some(bindings) => { - // TODO: cloning is ineffective, but it is not possible - // to convert grounded atom into internal value at the - // moment - (atom, bindings.clone()) - }, - _ => panic!("Unexpected state: second item cannot be converted to Bindings"), - } - }, + pair ~ [atom, bindings] => (atom, atom_into_bindings(bindings)), _ => { panic!("(Atom Bindings) pair is expected, {} was received", pair) } } } +fn atom_into_bindings(bindings: Atom) -> Bindings { + match bindings.as_gnd::() { + Some(bindings) => { + // TODO: cloning is ineffective, but it is not possible + // to convert grounded atom into internal value at the + // moment + bindings.clone() + }, + _ => panic!("Unexpected state: second item cannot be converted to Bindings"), + } +} + fn superpose_bind(stack: Stack, bindings: Bindings) -> Vec { let Stack{ prev, atom: superpose, ret: _, finished: _, vars: _ } = stack; let collapsed = match_atom!{ From f645c42ca0a285b8ca51c19cd846c2428ba4a309 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Mon, 29 Jan 2024 19:05:22 +0300 Subject: [PATCH 12/16] Fix processing variables inside superpose and collapse Fix superpose to include variable bindings which are present in returned atom. Fix collapse to keep all variables which were collected inside returned atoms. --- lib/src/atom/matcher.rs | 5 +++ lib/src/metta/interpreter2.rs | 69 ++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/lib/src/atom/matcher.rs b/lib/src/atom/matcher.rs index 596195d31..c47db1877 100644 --- a/lib/src/atom/matcher.rs +++ b/lib/src/atom/matcher.rs @@ -695,6 +695,11 @@ impl Bindings { BindingsIter { bindings: self, delegate: self.binding_by_var.iter() } } + /// An iterator visiting all variables in arbitrary order. + pub fn vars(&self) -> impl Iterator { + self.binding_by_var.keys() + } + fn into_vec_of_pairs(mut self) -> Vec<(VariableAtom, Atom)> { let mut result = Vec::new(); diff --git a/lib/src/metta/interpreter2.rs b/lib/src/metta/interpreter2.rs index 142b9c27e..7e56d5758 100644 --- a/lib/src/metta/interpreter2.rs +++ b/lib/src/metta/interpreter2.rs @@ -75,6 +75,15 @@ impl Stack { Self{ prev, atom, ret: no_handler, finished: true, vars } } + fn finished_add_vars(prev: Option>>, atom: Atom) -> Self { + let vars = Self::vars(&prev, &atom); + Self{ prev, atom, ret: no_handler, finished: true, vars } + } + + fn finished_with_vars(prev: Option>>, atom: Atom, vars: Variables) -> Self { + Self{ prev, atom, ret: no_handler, finished: true, vars } + } + fn len(&self) -> usize { self.fold(0, |len, _stack| len + 1) } @@ -122,7 +131,8 @@ impl Display for Stack { prev.borrow().fold((res, last_level - 1), |(res, level), top| { (res.and_then(|_| print_level(buffer, level, false, top)), level - 1) }).0 - }).and_then(|_| write!(f, "{}", buffer)) + }) + .and_then(|_| write!(f, "{}", buffer)) } } @@ -338,6 +348,17 @@ impl VariableSet for Variables { } } +impl Display for Variables { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "[") + .and_then(|_| self.iter().take(1).fold(Ok(()), + |res, atom| res.and_then(|_| write!(f, "{}", atom)))) + .and_then(|_| self.iter().skip(1).fold(Ok(()), + |res, atom| res.and_then(|_| write!(f, " {}", atom)))) + .and_then(|_| write!(f, "]")) + } +} + fn interpret_root_atom<'a, T: SpaceRef<'a>>(context: &InterpreterContext<'a, T>, interpreted_atom: InterpretedAtom) -> Vec { let InterpretedAtom(stack, bindings) = interpreted_atom; interpret_nested_atom(context, stack, bindings) @@ -621,13 +642,16 @@ fn collapse_bind_ret(stack: Rc>, atom: Atom, bindings: Bindings) match Rc::into_inner(stack).map(RefCell::into_inner) { Some(stack) => { - let Stack{ prev, atom: collapse, ret: _, finished: _, vars: _ } = stack; + let Stack{ prev, atom: collapse, ret: _, finished: _, mut vars } = stack; let (result, bindings) = match atom_into_array(collapse) { Some([_op, result, bindings]) => (result, atom_into_bindings(bindings)), None => panic!("Unexpected state"), }; - // FIXME: need a way to return binding from .ret - Some((Stack::finished(prev, result), bindings)) + for r in <&ExpressionAtom>::try_from(&result).unwrap().children() { + let (_, bindings) = atom_get_atom_bindings(r); + vars = vars.union(bindings.vars().cloned().collect()); + } + Some((Stack::finished_with_vars(prev, result, vars), bindings)) }, None => None, } @@ -637,6 +661,22 @@ fn atom_bindings_into_atom(atom: Atom, bindings: Bindings) -> Atom { Atom::expr([atom, Atom::value(bindings)]) } +fn atom_get_atom_bindings(pair: &Atom) -> (&Atom, &Bindings) { + match atom_as_slice(pair) { + Some([atom, bindings]) => (atom, atom_get_bindings(bindings)), + _ => { + panic!("(Atom Bindings) pair is expected, {} was received", pair) + } + } +} + +fn atom_get_bindings(bindings: &Atom) -> &Bindings { + match bindings.as_gnd::() { + Some(bindings) => bindings, + _ => panic!("Unexpected state: second item cannot be converted to Bindings"), + } +} + fn unify(stack: Stack, bindings: Bindings) -> Vec { let Stack{ prev, atom: unify, ret: _, finished: _, vars } = stack; let (atom, pattern, then, else_) = match atom_as_slice(&unify) { @@ -739,8 +779,14 @@ fn superpose_bind(stack: Stack, bindings: Bindings) -> Vec { .flat_map(|(atom, b)| { let prev = &prev; b.merge_v2(&bindings).into_iter() - .map(move |b| { - InterpretedAtom(Stack::finished(prev.clone(), atom.clone()), b) + .filter_map(move |b| { + if b.has_loops() { + None + } else { + let stack = Stack::finished_add_vars(prev.clone(), atom.clone()); + let b = b.narrow_vars(&stack.vars); + Some(InterpretedAtom(stack, b)) + } }) }) .collect() @@ -985,13 +1031,16 @@ mod tests { #[test] fn test_superpose_bind() { - let vars = Variables::new(); + let vars: Variables = [ "a", "b", "c" ].into_iter().map(VariableAtom::new).collect(); let atom = Atom::expr([Atom::sym("superpose-bind"), - Atom::expr([atom_bindings_into_atom(expr!("some-atom"), bind!{ a: expr!("A") })])]); + Atom::expr([atom_bindings_into_atom(expr!("foo" a b), bind!{ a: expr!("A"), c: expr!("C") })])]); let stack = Stack{ prev: None, atom, ret: no_handler, finished: false, vars: vars.clone() }; - let result = superpose_bind(stack, bind!{ b: expr!("B") }); + + let result = superpose_bind(stack, bind!{ b: expr!("B"), d: expr!("D") }); + + let expected_vars: Variables = [ "a", "b" ].into_iter().map(VariableAtom::new).collect(); assert_eq!(result, vec![InterpretedAtom( - Stack{ prev: None, atom: expr!("some-atom"), ret: no_handler, finished: true, vars }, + Stack{ prev: None, atom: expr!("foo" a b), ret: no_handler, finished: true, vars: expected_vars }, bind!{ a: expr!("A"), b: expr!("B") } )]); } From ddde909e9777793130aac5252de784dee7e195f2 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 2 Feb 2024 05:36:31 +0300 Subject: [PATCH 13/16] Add Cargo workspace configuration Introducing virtual workspace Cargo.toml file allows reusing workspace configuration (version, dependencies) in all Rust subprojects. --- Cargo.toml | 17 +++++++++++++++++ c/Cargo.toml | 13 ++++++------- lib/Cargo.toml | 11 +++++------ repl/Cargo.toml | 6 +++--- 4 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 Cargo.toml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..d241f6a73 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[workspace] +members = [ + "lib", + "c", + "repl", +] +resolver = "2" + +[workspace.package] +version = "0.1.6" +edition = "2021" + +[workspace.dependencies] +hyperon = { path = "./lib", version = "0.1.6" } +regex = "1.5.4" +log = "0.4.0" +env_logger = "0.8.4" diff --git a/c/Cargo.toml b/c/Cargo.toml index beba9ddbc..f1325cb59 100644 --- a/c/Cargo.toml +++ b/c/Cargo.toml @@ -1,14 +1,13 @@ [package] name = "hyperonc" -version = "0.1.6" -authors = ["Vitaly Bogdanov "] -edition = "2018" +version.workspace = true +edition.workspace = true [dependencies] -hyperon = { path = "../lib/" } -regex = "1.5.4" -log = "0.4.0" -env_logger = "0.8.4" +hyperon = { workspace = true } +regex = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } [lib] name = "hyperonc" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 01a233810..26358e85e 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -1,14 +1,13 @@ [package] name = "hyperon" -version = "0.1.6" -authors = ["Vitaly Bogdanov "] -edition = "2021" +version.workspace = true +edition.workspace = true [dependencies] mopa = "0.2.2" -regex = "1.5.4" -log = "0.4.0" -env_logger = "0.8.4" +regex = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } directories = "5.0.1" # For Environment to find platform-specific config location smallvec = "1.10.0" im = "15.1.0" diff --git a/repl/Cargo.toml b/repl/Cargo.toml index 31cba87f9..fbf041a87 100644 --- a/repl/Cargo.toml +++ b/repl/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "metta-repl" -version = "0.1.0" -edition = "2021" +version.workspace = true +edition.workspace = true description = "A shell to execute MeTTa" [dependencies] @@ -11,7 +11,7 @@ clap = { version = "4.4.0", features = ["derive"] } signal-hook = "0.3.17" pyo3 = { version = "0.19.2", features = ["auto-initialize"], optional = true } pep440_rs = { version = "0.3.11", optional = true } -hyperon = { path = "../lib/", optional = true } #TODO: We can only link Hyperon directly or through Python, but not both at the same time. The right fix is to allow HyperonPy to be built within Hyperon, See https://github.com/trueagi-io/hyperon-experimental/issues/283 +hyperon = { workspace = true, optional = true } #TODO: We can only link Hyperon directly or through Python, but not both at the same time. The right fix is to allow HyperonPy to be built within Hyperon, See https://github.com/trueagi-io/hyperon-experimental/issues/283 [[bin]] name = "metta" From 08142137c8e5b945d13a4666f97085fb362d7b64 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 2 Feb 2024 05:50:49 +0300 Subject: [PATCH 14/16] Fix compiler error and warning Fix error when building REPL with --features minimal. Fix lifetime eliding warning. --- repl/src/interactive_helper.rs | 2 +- repl/src/metta_shim.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/repl/src/interactive_helper.rs b/repl/src/interactive_helper.rs index 9c63e694d..b99b1b5d3 100644 --- a/repl/src/interactive_helper.rs +++ b/repl/src/interactive_helper.rs @@ -210,7 +210,7 @@ impl ReplHelper { } impl StyleSettings { - const ERR_STR: &str = "Fatal Error: Invalid REPL config"; + const ERR_STR: &'static str = "Fatal Error: Invalid REPL config"; pub fn new(metta_shim: &mut MettaShim) -> Self { Self { bracket_styles: metta_shim.get_config_expr_vec(CFG_BRACKET_STYLES).expect(Self::ERR_STR), diff --git a/repl/src/metta_shim.rs b/repl/src/metta_shim.rs index 5aa80e0e2..0e83ae5d9 100644 --- a/repl/src/metta_shim.rs +++ b/repl/src/metta_shim.rs @@ -72,7 +72,7 @@ pub mod metta_interface_mod { } impl MettaShim { - const PY_CODE: &str = include_str!("py_shim.py"); + const PY_CODE: &'static str = include_str!("py_shim.py"); pub fn new(working_dir: PathBuf, include_paths: Vec) -> Self { @@ -413,7 +413,7 @@ pub mod metta_interface_mod { pub fn exec(&mut self, line: &str) { let parser = SExprParser::new(line); - let mut runner_state = RunnerState::new_with_parser(&self.metta, parser); + let mut runner_state = RunnerState::new_with_parser(&self.metta, Box::new(parser)); exec_state_prepare(); From a530bcf29c75b9e7b0d858a8d70786cbb55c8ab7 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Fri, 2 Feb 2024 06:13:38 +0300 Subject: [PATCH 15/16] Add commands using Rust workspace into README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f51b2591a..9f719bd84 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,10 @@ python3 -m pip install pip==23.1.2 # Build and run -## Hyperon library +## Rust library and REPL -Build and test the library: +Build and test the Rust binaries: ``` -cd ./lib -cargo build cargo test ``` @@ -90,6 +88,12 @@ Run examples: cargo run --example sorted_list ``` +Run REPL: +``` +cargo run --bin metta +``` +You can also find executable at `./target/debug/metta`. + To enable logging during running tests or examples export `RUST_LOG` environment variable: ``` @@ -103,10 +107,9 @@ cargo +nightly bench Generate docs: ``` -cd ./lib cargo doc --no-deps ``` -Docs can be found at `./lib/target/doc/hyperon/index.html`. +Docs can be found at `./target/doc/hyperon/index.html`. ## C and Python API From e1ccd0ba3c784ec2f48df274857264165faad0f2 Mon Sep 17 00:00:00 2001 From: Luke Peterson Date: Thu, 1 Feb 2024 23:43:11 -0800 Subject: [PATCH 16/16] Fixing segfault in C API caused by freeing the tokenizer callbacks before we were finished with them --- c/src/metta.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/c/src/metta.rs b/c/src/metta.rs index d2835e223..4f3a2d1b9 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -126,6 +126,7 @@ pub extern "C" fn tokenizer_register_token(tokenizer: *mut tokenizer_t, let regex = Regex::new(cstr_as_str(regex)).unwrap(); let c_token = CToken{ context, api }; tokenizer.register_token(regex, move |token| { + let c_token = &c_token; //Be explicit we're capturing c_token, and not the pointers it contains let constr = unsafe{ (&*c_token.api).construct_atom }; let atom = constr(str_as_cstr(token).as_ptr(), c_token.context); atom.into_inner()