From 99d548978efd0c9a25d54c802f84f008eb026e79 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Mon, 21 Oct 2024 17:34:46 +0300 Subject: [PATCH 1/2] Fix unique, subtraction, intersection, union operations Remove internal interpreter call and fix type to interpret arguments before passing to the operation. --- lib/src/metta/runner/stdlib.rs | 267 ++++++++++--------------- lib/src/metta/runner/stdlib_minimal.rs | 16 +- 2 files changed, 119 insertions(+), 164 deletions(-) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 5e7af787e..db1dd2a58 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -6,6 +6,7 @@ use crate::metta::text::SExprParser; use crate::metta::runner::{Metta, RunContext, ModuleLoader, ResourceKey}; use crate::metta::runner::string::Str; use crate::metta::types::{get_atom_types, get_meta_type}; +#[cfg(feature = "old_interpreter")] use crate::metta::interpreter::interpret; use crate::common::shared::Shared; use crate::common::CachingMapper; @@ -50,6 +51,7 @@ pub(crate) fn regex(regex: &str) -> Regex { // TODO: remove hiding errors completely after making it possible passing // them to the user +#[cfg(feature = "old_interpreter")] fn interpret_no_error(space: DynSpace, expr: &Atom) -> Result, String> { let result = interpret(space, expr); log::debug!("interpret_no_error: interpretation expr: {}, result {:?}", expr, result); @@ -1035,21 +1037,13 @@ pub(crate) mod pkg_mgmt_ops { } #[derive(Clone, Debug)] -pub struct UniqueOp { - pub(crate) space: DynSpace, -} +pub struct UniqueOp {} grounded_op!(UniqueOp, "unique"); -impl UniqueOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - impl Grounded for UniqueOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1059,38 +1053,28 @@ impl Grounded for UniqueOp { impl CustomExecute for UniqueOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { - let arg_error = || ExecError::from("unique expects single executable atom as an argument"); - let atom = args.get(0).ok_or_else(arg_error)?; + let arg_error = || ExecError::from("unique expects single expression atom as an argument"); + let expr = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?; - // TODO: Calling interpreter inside the operation is not too good - // Could it be done via StepResult? - let mut result = interpret_no_error(self.space.clone(), atom)?; + let mut atoms = expr.children().clone(); let mut set = GroundingSpace::new(); - result.retain(|x| { + atoms.retain(|x| { let not_contained = set.query(x).is_empty(); if not_contained { set.add(x.clone()) }; not_contained }); - Ok(result) + Ok(atoms) } } #[derive(Clone, Debug)] -pub struct UnionOp { - pub(crate) space: DynSpace, -} +pub struct UnionOp {} grounded_op!(UnionOp, "union"); -impl UnionOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - impl Grounded for UnionOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1101,35 +1085,23 @@ impl Grounded for UnionOp { impl CustomExecute for UnionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("union expects and executable LHS and RHS atom"); - let lhs = args.get(0).ok_or_else(arg_error)?; - let rhs = args.get(1).ok_or_else(arg_error)?; + let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); + let rhs = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().clone(); - // TODO: Calling interpreter inside the operation is not too good - // Could it be done via StepResult? - let mut lhs_result = interpret_no_error(self.space.clone(), lhs)?; - let rhs_result = interpret_no_error(self.space.clone(), rhs)?; - lhs_result.extend(rhs_result); + lhs.extend(rhs); - Ok(lhs_result) + Ok(lhs) } } #[derive(Clone, Debug)] -pub struct IntersectionOp { - pub(crate) space: DynSpace, -} +pub struct IntersectionOp {} grounded_op!(IntersectionOp, "intersection"); -impl IntersectionOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - impl Grounded for IntersectionOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1140,15 +1112,11 @@ impl Grounded for IntersectionOp { impl CustomExecute for IntersectionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("intersection expects and executable LHS and RHS atom"); - let lhs = args.get(0).ok_or_else(arg_error)?; - let rhs = args.get(1).ok_or_else(arg_error)?; + let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); + let rhs = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().clone(); - // TODO: Calling interpreter inside the operation is not too good - // Could it be done via StepResult? - let mut lhs_result = interpret_no_error(self.space.clone(), lhs)?; - let rhs_result = interpret_no_error(self.space.clone(), rhs)?; let mut rhs_index: MultiTrie> = MultiTrie::new(); - for (index, rhs_item) in rhs_result.iter().enumerate() { + for (index, rhs_item) in rhs.iter().enumerate() { let k = atom_to_trie_key(&rhs_item); // FIXME this should // a) use a mutable value endpoint which the MultiTrie does not support atm @@ -1166,13 +1134,13 @@ impl CustomExecute for IntersectionOp { } } - lhs_result.retain(|candidate| { + lhs.retain(|candidate| { let k = atom_to_trie_key(candidate); let r = rhs_index.get(&k).next(); match r.cloned() { None => { false } Some(bucket) => { - match bucket.iter().position(|item| &rhs_result[*item] == candidate) { + match bucket.iter().position(|item| &rhs[*item] == candidate) { None => { false } Some(i) => { rhs_index.remove(&k, &bucket); @@ -1188,26 +1156,18 @@ impl CustomExecute for IntersectionOp { } }); - Ok(lhs_result) + Ok(lhs) } } #[derive(Clone, Debug)] -pub struct SubtractionOp { - pub(crate) space: DynSpace, -} +pub struct SubtractionOp {} grounded_op!(SubtractionOp, "subtraction"); -impl SubtractionOp { - pub fn new(space: DynSpace) -> Self { - Self{ space } - } -} - impl Grounded for SubtractionOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1218,15 +1178,11 @@ impl Grounded for SubtractionOp { impl CustomExecute for SubtractionOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("subtraction expects and executable LHS and RHS atom"); - let lhs = args.get(0).ok_or_else(arg_error)?; - let rhs = args.get(1).ok_or_else(arg_error)?; + let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); + let rhs = TryInto::<&ExpressionAtom>::try_into(args.get(1).ok_or_else(arg_error)?)?.children().clone(); - // TODO: Calling interpreter inside the operation is not too good - // Could it be done via StepResult? - let mut lhs_result = interpret_no_error(self.space.clone(), lhs)?; - let rhs_result = interpret_no_error(self.space.clone(), rhs)?; let mut rhs_index: MultiTrie> = MultiTrie::new(); - for (index, rhs_item) in rhs_result.iter().enumerate() { + for (index, rhs_item) in rhs.iter().enumerate() { let k = atom_to_trie_key(&rhs_item); // FIXME this should // a) use a mutable value endpoint which the MultiTrie does not support atm @@ -1244,13 +1200,13 @@ impl CustomExecute for SubtractionOp { } } - lhs_result.retain(|candidate| { + lhs.retain(|candidate| { let k = atom_to_trie_key(candidate); let r = rhs_index.get(&k).next(); match r.cloned() { None => { true } Some(bucket) => { - match bucket.iter().position(|item| &rhs_result[*item] == candidate) { + match bucket.iter().position(|item| &rhs[*item] == candidate) { None => { true } Some(i) => { rhs_index.remove(&k, &bucket); @@ -1266,7 +1222,7 @@ impl CustomExecute for SubtractionOp { } }); - Ok(lhs_result) + Ok(lhs) } } @@ -1807,6 +1763,14 @@ mod non_minimal_only_stdlib { tref.register_token(regex(r"print-mods!"), move |_| { print_mods_op.clone() }); let sealed_op = Atom::gnd(SealedOp{}); tref.register_token(regex(r"sealed"), move |_| { sealed_op.clone() }); + let unique_op = Atom::gnd(UniqueOp{}); + tref.register_token(regex(r"unique"), move |_| { unique_op.clone() }); + let subtraction_op = Atom::gnd(SubtractionOp{}); + tref.register_token(regex(r"subtraction"), move |_| { subtraction_op.clone() }); + let intersection_op = Atom::gnd(IntersectionOp{}); + tref.register_token(regex(r"intersection"), move |_| { intersection_op.clone() }); + let union_op = Atom::gnd(UnionOp{}); + tref.register_token(regex(r"union"), move |_| { union_op.clone() }); #[cfg(feature = "pkg_mgmt")] pkg_mgmt_ops::register_pkg_mgmt_tokens(tref, metta); @@ -1829,14 +1793,6 @@ mod non_minimal_only_stdlib { tref.register_token(regex(r"collapse"), move |_| { collapse_op.clone() }); let superpose_op = Atom::gnd(SuperposeOp::new(space.clone())); tref.register_token(regex(r"superpose"), move |_| { superpose_op.clone() }); - let unique_op = Atom::gnd(UniqueOp::new(space.clone())); - tref.register_token(regex(r"unique"), move |_| { unique_op.clone() }); - let union_op = Atom::gnd(UnionOp::new(space.clone())); - tref.register_token(regex(r"union"), move |_| { union_op.clone() }); - let intersection_op = Atom::gnd(IntersectionOp::new(space.clone())); - tref.register_token(regex(r"intersection"), move |_| { intersection_op.clone() }); - let subtraction_op = Atom::gnd(SubtractionOp::new(space.clone())); - tref.register_token(regex(r"subtraction"), move |_| { subtraction_op.clone() }); let get_type_op = Atom::gnd(GetTypeOp::new(space.clone())); tref.register_token(regex(r"get-type"), move |_| { get_type_op.clone() }); let get_type_space_op = Atom::gnd(GetTypeSpaceOp{}); @@ -2186,36 +2142,35 @@ mod tests { #[test] fn unique_op() { - let space = DynSpace::new(metta_space(" - (= (foo) (A (B C))) - (= (foo) (A (B C))) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) Z) - ")); - let unique_op = UniqueOp::new(space); - let actual = unique_op.execute(&mut vec![expr!(("foo"))]).unwrap(); + let unique_op = UniqueOp{}; + let actual = unique_op.execute(&mut vec![expr!( + ("A" ("B" "C")) + ("A" ("B" "C")) + ("f" "g") + ("f" "g") + ("f" "g") + "Z" + )]).unwrap(); assert_eq_no_order!(actual, vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("Z")]); } #[test] fn union_op() { - let space = DynSpace::new(metta_space(" - (= (foo) (A (B C))) - (= (foo) (A (B C))) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) Z) - (= (bar) (A (B C))) - (= (bar) p) - (= (bar) p) - (= (bar) (Q a)) - ")); - let union_op = UnionOp::new(space); - let actual = union_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]).unwrap(); + let union_op = UnionOp{}; + let actual = union_op.execute(&mut vec![expr!( + ("A" ("B" "C")) + ("A" ("B" "C")) + ("f" "g") + ("f" "g") + ("f" "g") + "Z" + ), expr!( + ("A" ("B" "C")) + "p" + "p" + ("Q" "a") + )]).unwrap(); assert_eq_no_order!(actual, vec![expr!("A" ("B" "C")), expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("f" "g"), expr!("Z"), expr!("A" ("B" "C")), expr!("p"), expr!("p"), expr!("Q" "a")]); @@ -2223,62 +2178,62 @@ mod tests { #[test] fn intersection_op() { - let space = DynSpace::new(metta_space(" - (= (foo) Z) - (= (foo) (A (B C))) - (= (foo) (A (B C))) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (P b)) - (= (bar) (f g)) - (= (bar) (f g)) - (= (bar) (A (B C))) - (= (bar) p) - (= (bar) p) - (= (bar) (Q a)) - (= (bar) Z) - - (= (nsl) 5) - (= (nsl) 4) - (= (nsl) 3) - (= (nsl) 2) - (= (nsr) 5) - (= (nsr) 3) - ")); - let intersection_op = IntersectionOp::new(space); - let actual = intersection_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]).unwrap(); + let intersection_op = IntersectionOp{}; + let actual = intersection_op.execute(&mut vec![expr!( + "Z" + ("A" ("B" "C")) + ("A" ("B" "C")) + ("f" "g") + ("f" "g") + ("f" "g") + ("P" "b") + ), expr!( + ("f" "g") + ("f" "g") + ("A" ("B" "C")) + "p" + "p" + ("Q" "a") + "Z" + )]).unwrap(); assert_eq_no_order!(actual, vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("Z")]); - assert_eq_no_order!(intersection_op.execute(&mut vec![expr!(("nsl")), expr!(("nsr"))]).unwrap(), - vec![expr!("5"), expr!("3")]); + assert_eq_no_order!(intersection_op.execute(&mut vec![expr!( + { Number::Integer(5) } + { Number::Integer(4) } + { Number::Integer(3) } + { Number::Integer(2) } + ), expr!( + { Number::Integer(5) } + { Number::Integer(3) } + )]).unwrap(), vec![expr!({Number::Integer(5)}), expr!({Number::Integer(3)})]); } #[test] fn subtraction_op() { - let space = DynSpace::new(metta_space(" - (= (foo) Z) - (= (foo) S) - (= (foo) S) - (= (foo) (A (B C))) - (= (foo) (A (B C))) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (f g)) - (= (foo) (P b)) - (= (bar) (f g)) - (= (bar) (A (B C))) - (= (bar) p) - (= (bar) p) - (= (bar) (Q a)) - (= (bar) Z) - (= (bar) S) - (= (bar) S) - (= (bar) S) - ")); - let subtraction_op = SubtractionOp::new(space); - let actual = subtraction_op.execute(&mut vec![expr!(("foo")), expr!(("bar"))]).unwrap(); + let subtraction_op = SubtractionOp{}; + let actual = subtraction_op.execute(&mut vec![expr!( + "Z" + "S" + "S" + ("A" ("B" "C")) + ("A" ("B" "C")) + ("f" "g") + ("f" "g") + ("f" "g") + ("P" "b") + ), expr!( + ("f" "g") + ("A" ("B" "C")) + "p" + "P" + ("Q" "a") + "Z" + "S" + "S" + "S" + )]).unwrap(); assert_eq_no_order!(actual, vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("P" "b")]); } diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index 066fe7503..756982242 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -436,6 +436,14 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared tref.register_token(regex(r"superpose"), move |_| { superpose_op.clone() }); let collapse_op = Atom::gnd(CollapseOp::new(space.clone())); tref.register_token(regex(r"collapse"), move |_| { collapse_op.clone() }); - let unique_op = Atom::gnd(stdlib::UniqueOp::new(space.clone())); - tref.register_token(regex(r"unique"), move |_| { unique_op.clone() }); - let union_op = Atom::gnd(stdlib::UnionOp::new(space.clone())); - tref.register_token(regex(r"union"), move |_| { union_op.clone() }); - let intersection_op = Atom::gnd(stdlib::IntersectionOp::new(space.clone())); - tref.register_token(regex(r"intersection"), move |_| { intersection_op.clone() }); - let subtraction_op = Atom::gnd(stdlib::SubtractionOp::new(space.clone())); - tref.register_token(regex(r"subtraction"), move |_| { subtraction_op.clone() }); let case_op = Atom::gnd(CaseOp::new(space.clone())); tref.register_token(regex(r"case"), move |_| { case_op.clone() }); let capture_op = Atom::gnd(CaptureOp::new(space.clone())); From 54d68aaad1e3309de82f0a2d2e36e2c1affec6e9 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Mon, 21 Oct 2024 18:18:51 +0300 Subject: [PATCH 2/2] Add non-deterministic union, intersection, unique and subtraction Rename old implementations to ...-atom, add MeTTa implementations of union, intersection, unique and subtraction with non-deterministic input and output. --- lib/src/metta/runner/stdlib.metta | 73 ++++++++++++++----- lib/src/metta/runner/stdlib.rs | 86 +++++++++++------------ lib/src/metta/runner/stdlib_minimal.metta | 71 +++++++++++++++---- lib/src/metta/runner/stdlib_minimal.rs | 16 ++--- 4 files changed, 164 insertions(+), 82 deletions(-) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index 5a01ede4b..34b90e9b4 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -67,6 +67,47 @@ (: empty (-> %Undefined%)) (= (empty) (let a b never-happens)) +(@doc unique + (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") + (@params ( + (@param "Non-deterministic set of values"))) + (@return "Unique values from input set")) +(: unique (-> Atom Atom)) +(= (unique $arg) (let $c (collapse $arg) (let $u (unique-atom $c) (superpose $u)))) + +(@doc union + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Union of sets")) +(: union (-> Atom Atom Atom)) +(= (union $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (union-atom $c1 $c2) (superpose $u))))) + +(@doc intersection + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Intersection of sets")) +(: intersection (-> Atom Atom Atom)) +(= (intersection $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (intersection-atom $c1 $c2) (superpose $u))))) + +(@doc subtraction + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Subtraction of sets")) +(: subtraction (-> Atom Atom Atom)) +(= (subtraction $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (subtraction-atom $c1 $c2) (superpose $u))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Documentation formatting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -657,35 +698,35 @@ (@params ()) (@return "Random boolean value")) -(@doc unique - (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") +(@doc unique-atom + (@desc "Function takes tuple and returns only unique entities. E.g. (unique-atom (a b c d d)) -> (a b c d)") (@params ( - (@param "Non-deterministic set of values"))) + (@param "List of values"))) (@return "Unique values from input set")) -(@doc union - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") +(@doc union-atom + (@desc "Function takes two tuples and returns their union. E.g. (union-atom (a b b c) (b c c d)) -> (a b b c b c c d)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Union of sets")) -(@doc intersection - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") +(@doc intersection-atom + (@desc "Function takes two tuples and returns their intersection. E.g. (intersection-atom (a b c c) (b c c c d)) -> (b c c)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Intersection of sets")) -(@doc subtraction - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") +(@doc subtraction-atom + (@desc "Function takes two tuples and returns their subtraction. E.g. !(subtraction-atom (a b b c) (b c c d)) -> (a b)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Subtraction of sets")) (@doc git-module! (@desc "Provides access to module in a remote git repo, from within MeTTa code. Similar to `register-module!`, this op will bypass the catalog search") (@params ( (@param "URL to github repo"))) - (@return "Unit atom")) \ No newline at end of file + (@return "Unit atom")) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index db1dd2a58..47e26de37 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1037,13 +1037,13 @@ pub(crate) mod pkg_mgmt_ops { } #[derive(Clone, Debug)] -pub struct UniqueOp {} +pub struct UniqueAtomOp {} -grounded_op!(UniqueOp, "unique"); +grounded_op!(UniqueAtomOp, "unique-atom"); -impl Grounded for UniqueOp { +impl Grounded for UniqueAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1051,7 +1051,7 @@ impl Grounded for UniqueOp { } } -impl CustomExecute for UniqueOp { +impl CustomExecute for UniqueAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("unique expects single expression atom as an argument"); let expr = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?; @@ -1063,18 +1063,18 @@ impl CustomExecute for UniqueOp { if not_contained { set.add(x.clone()) }; not_contained }); - Ok(atoms) + Ok(vec![Atom::expr(atoms)]) } } #[derive(Clone, Debug)] -pub struct UnionOp {} +pub struct UnionAtomOp {} -grounded_op!(UnionOp, "union"); +grounded_op!(UnionAtomOp, "union-atom"); -impl Grounded for UnionOp { +impl Grounded for UnionAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1082,7 +1082,7 @@ impl Grounded for UnionOp { } } -impl CustomExecute for UnionOp { +impl CustomExecute for UnionAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("union expects and executable LHS and RHS atom"); let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); @@ -1090,18 +1090,18 @@ impl CustomExecute for UnionOp { lhs.extend(rhs); - Ok(lhs) + Ok(vec![Atom::expr(lhs)]) } } #[derive(Clone, Debug)] -pub struct IntersectionOp {} +pub struct IntersectionAtomOp {} -grounded_op!(IntersectionOp, "intersection"); +grounded_op!(IntersectionAtomOp, "intersection-atom"); -impl Grounded for IntersectionOp { +impl Grounded for IntersectionAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1109,7 +1109,7 @@ impl Grounded for IntersectionOp { } } -impl CustomExecute for IntersectionOp { +impl CustomExecute for IntersectionAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("intersection expects and executable LHS and RHS atom"); let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); @@ -1156,18 +1156,18 @@ impl CustomExecute for IntersectionOp { } }); - Ok(lhs) + Ok(vec![Atom::expr(lhs)]) } } #[derive(Clone, Debug)] -pub struct SubtractionOp {} +pub struct SubtractionAtomOp {} -grounded_op!(SubtractionOp, "subtraction"); +grounded_op!(SubtractionAtomOp, "subtraction-atom"); -impl Grounded for SubtractionOp { +impl Grounded for SubtractionAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_UNDEFINED, ATOM_TYPE_UNDEFINED, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION, ATOM_TYPE_EXPRESSION]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1175,7 +1175,7 @@ impl Grounded for SubtractionOp { } } -impl CustomExecute for SubtractionOp { +impl CustomExecute for SubtractionAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("subtraction expects and executable LHS and RHS atom"); let mut lhs = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children().clone(); @@ -1222,7 +1222,7 @@ impl CustomExecute for SubtractionOp { } }); - Ok(lhs) + Ok(vec![Atom::expr(lhs)]) } } @@ -1763,14 +1763,14 @@ mod non_minimal_only_stdlib { tref.register_token(regex(r"print-mods!"), move |_| { print_mods_op.clone() }); let sealed_op = Atom::gnd(SealedOp{}); tref.register_token(regex(r"sealed"), move |_| { sealed_op.clone() }); - let unique_op = Atom::gnd(UniqueOp{}); - tref.register_token(regex(r"unique"), move |_| { unique_op.clone() }); - let subtraction_op = Atom::gnd(SubtractionOp{}); - tref.register_token(regex(r"subtraction"), move |_| { subtraction_op.clone() }); - let intersection_op = Atom::gnd(IntersectionOp{}); - tref.register_token(regex(r"intersection"), move |_| { intersection_op.clone() }); - let union_op = Atom::gnd(UnionOp{}); - tref.register_token(regex(r"union"), move |_| { union_op.clone() }); + let unique_op = Atom::gnd(UniqueAtomOp{}); + tref.register_token(regex(r"unique-atom"), move |_| { unique_op.clone() }); + let subtraction_op = Atom::gnd(SubtractionAtomOp{}); + tref.register_token(regex(r"subtraction-atom"), move |_| { subtraction_op.clone() }); + let intersection_op = Atom::gnd(IntersectionAtomOp{}); + tref.register_token(regex(r"intersection-atom"), move |_| { intersection_op.clone() }); + let union_op = Atom::gnd(UnionAtomOp{}); + tref.register_token(regex(r"union-atom"), move |_| { union_op.clone() }); #[cfg(feature = "pkg_mgmt")] pkg_mgmt_ops::register_pkg_mgmt_tokens(tref, metta); @@ -2142,7 +2142,7 @@ mod tests { #[test] fn unique_op() { - let unique_op = UniqueOp{}; + let unique_op = UniqueAtomOp{}; let actual = unique_op.execute(&mut vec![expr!( ("A" ("B" "C")) ("A" ("B" "C")) @@ -2152,12 +2152,12 @@ mod tests { "Z" )]).unwrap(); assert_eq_no_order!(actual, - vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("Z")]); + vec![expr!(("A" ("B" "C")) ("f" "g") "Z")]); } #[test] fn union_op() { - let union_op = UnionOp{}; + let union_op = UnionAtomOp{}; let actual = union_op.execute(&mut vec![expr!( ("A" ("B" "C")) ("A" ("B" "C")) @@ -2172,13 +2172,14 @@ mod tests { ("Q" "a") )]).unwrap(); assert_eq_no_order!(actual, - vec![expr!("A" ("B" "C")), expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("f" "g"), expr!("Z"), - expr!("A" ("B" "C")), expr!("p"), expr!("p"), expr!("Q" "a")]); + vec![expr!(("A" ("B" "C")) ("A" ("B" "C")) + ("f" "g") ("f" "g") ("f" "g") "Z" + ("A" ("B" "C")) "p" "p" ("Q" "a"))]); } #[test] fn intersection_op() { - let intersection_op = IntersectionOp{}; + let intersection_op = IntersectionAtomOp{}; let actual = intersection_op.execute(&mut vec![expr!( "Z" ("A" ("B" "C")) @@ -2196,8 +2197,7 @@ mod tests { ("Q" "a") "Z" )]).unwrap(); - assert_eq_no_order!(actual, - vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("Z")]); + assert_eq_no_order!(actual, vec![expr!("Z" ("A" ("B" "C")) ("f" "g") ("f" "g"))]); assert_eq_no_order!(intersection_op.execute(&mut vec![expr!( { Number::Integer(5) } @@ -2207,12 +2207,12 @@ mod tests { ), expr!( { Number::Integer(5) } { Number::Integer(3) } - )]).unwrap(), vec![expr!({Number::Integer(5)}), expr!({Number::Integer(3)})]); + )]).unwrap(), vec![expr!({Number::Integer(5)} {Number::Integer(3)})]); } #[test] fn subtraction_op() { - let subtraction_op = SubtractionOp{}; + let subtraction_op = SubtractionAtomOp{}; let actual = subtraction_op.execute(&mut vec![expr!( "Z" "S" @@ -2235,7 +2235,7 @@ mod tests { "S" )]).unwrap(); assert_eq_no_order!(actual, - vec![expr!("A" ("B" "C")), expr!("f" "g"), expr!("f" "g"), expr!("P" "b")]); + vec![expr!(("A" ("B" "C")) ("f" "g") ("f" "g") ("P" "b"))]); } #[test] diff --git a/lib/src/metta/runner/stdlib_minimal.metta b/lib/src/metta/runner/stdlib_minimal.metta index c6a7917a5..5f053be70 100644 --- a/lib/src/metta/runner/stdlib_minimal.metta +++ b/lib/src/metta/runner/stdlib_minimal.metta @@ -460,6 +460,47 @@ (@return "Nothing")) (= (empty) Empty) +(@doc unique + (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") + (@params ( + (@param "Non-deterministic set of values"))) + (@return "Unique values from input set")) +(: unique (-> Atom Atom)) +(= (unique $arg) (let $c (collapse $arg) (let $u (unique-atom $c) (superpose $u)))) + +(@doc union + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Union of sets")) +(: union (-> Atom Atom Atom)) +(= (union $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (union-atom $c1 $c2) (superpose $u))))) + +(@doc intersection + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Intersection of sets")) +(: intersection (-> Atom Atom Atom)) +(= (intersection $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (intersection-atom $c1 $c2) (superpose $u))))) + +(@doc subtraction + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Subtraction of sets")) +(: subtraction (-> Atom Atom Atom)) +(= (subtraction $arg1 $arg2) + (let $c1 (collapse $arg1) (let $c2 (collapse $arg2) + (let $u (subtraction-atom $c1 $c2) (superpose $u))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Documentation formatting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -971,31 +1012,31 @@ (@params ()) (@return "Returns uniformly distributed random boolean value")) -(@doc unique - (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") +(@doc unique-atom + (@desc "Function takes tuple and returns only unique entities. E.g. (unique-atom (a b c d d)) -> (a b c d)") (@params ( - (@param "Non-deterministic set of values"))) + (@param "List of values"))) (@return "Unique values from input set")) -(@doc union - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") +(@doc union-atom + (@desc "Function takes two tuples and returns their union. E.g. (union-atom (a b b c) (b c c d)) -> (a b b c b c c d)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Union of sets")) -(@doc intersection - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") +(@doc intersection-atom + (@desc "Function takes two tuples and returns their intersection. E.g. (intersection-atom (a b c c) (b c c c d)) -> (b c c)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Intersection of sets")) -(@doc subtraction - (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") +(@doc subtraction-atom + (@desc "Function takes two tuples and returns their subtraction. E.g. !(subtraction-atom (a b b c) (b c c d)) -> (a b)") (@params ( - (@param "Non-deterministic set of values") - (@param "Another non-deterministic set of values"))) + (@param "List of values") + (@param "List of values"))) (@return "Subtraction of sets")) (@doc git-module! diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index 756982242..927a33bb3 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -436,14 +436,14 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared