From dd00140c584c10aca64b07b65f29f19704384ec5 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Tue, 5 Nov 2024 07:48:08 +0300 Subject: [PATCH 1/4] size-atom added --- lib/src/metta/runner/stdlib.rs | 34 ++++++++++++++++++++++++++ lib/src/metta/runner/stdlib_minimal.rs | 8 ++++++ 2 files changed, 42 insertions(+) diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 0aee17415..4184ce4f4 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1160,6 +1160,30 @@ impl CustomExecute for IntersectionAtomOp { } } +#[derive(Clone, Debug)] +pub struct SizeAtomOp {} + +grounded_op!(SizeAtomOp, "size-atom"); + +impl Grounded for SizeAtomOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for SizeAtomOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + let arg_error = || ExecError::from("size-atom expects one argument: expression"); + let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); + let size = children.len(); + Ok(vec![Atom::gnd(Number::Integer(size as i64))]) + } +} + #[derive(Clone, Debug)] pub struct IndexAtomOp {} @@ -1764,6 +1788,8 @@ mod non_minimal_only_stdlib { tref.register_token(regex(r"cdr-atom"), move |_| { cdr_atom_op.clone() }); let cons_atom_op = Atom::gnd(ConsAtomOp{}); tref.register_token(regex(r"cons-atom"), move |_| { cons_atom_op.clone() }); + let size_atom_op = Atom::gnd(SizeAtomOp{}); + tref.register_token(regex(r"size-atom"), move |_| { size_atom_op.clone() }); let index_atom_op = Atom::gnd(IndexAtomOp{}); tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() }); let println_op = Atom::gnd(PrintlnOp{}); @@ -2053,6 +2079,14 @@ mod tests { assert_eq!(res, vec![expr!(("A" "F") ("B" "C") "D")]); } + #[test] + fn size_atom_op() { + let res = SizeAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)})]).expect("No result returned"); + assert_eq!(res, vec![expr!({Number::Integer(5)})]); + let res = SizeAtomOp{}.execute(&mut vec![expr!()]).expect("No result returned"); + assert_eq!(res, vec![expr!({Number::Integer(0)})]); + } + #[test] fn index_atom_op() { let res = IndexAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)}), expr!({Number::Integer(2)})]).expect("No result returned"); diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index ff81ba839..4d48a7e03 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -432,6 +432,8 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared Date: Tue, 5 Nov 2024 07:51:42 +0300 Subject: [PATCH 2/4] documentation for size atom added --- lib/src/metta/runner/stdlib.metta | 6 ++++++ lib/src/metta/runner/stdlib_minimal.metta | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index 2139af922..ad5ca9944 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -415,6 +415,12 @@ (@param "Tail of an expression"))) (@return "New expression consists of two input arguments")) +(@doc size-atom + (@desc "Returns size of an expression (first argument)") + (@params ( + (@param "Expression"))) + (@return "Size of an expression")) + (@doc index-atom (@desc "Returns atom from an expression (first argument) using index (second argument) or error if index is out of bounds") (@params ( diff --git a/lib/src/metta/runner/stdlib_minimal.metta b/lib/src/metta/runner/stdlib_minimal.metta index e4d43d89a..3cc56c9ad 100644 --- a/lib/src/metta/runner/stdlib_minimal.metta +++ b/lib/src/metta/runner/stdlib_minimal.metta @@ -72,6 +72,12 @@ (@return "Deconsed expression")) (: decons-atom (-> Expression Expression)) +(@doc size-atom + (@desc "Returns size of an expression (first argument)") + (@params ( + (@param "Expression"))) + (@return "Size of an expression")) + (@doc index-atom (@desc "Returns atom from an expression (first argument) using index (second argument) or error if index is out of bounds") (@params ( From 9eb027b3c5105a93ea86697ed203ab6d12cab742 Mon Sep 17 00:00:00 2001 From: Innokenty Date: Thu, 7 Nov 2024 11:01:40 +0300 Subject: [PATCH 3/4] min/max -atom functions added --- lib/src/metta/runner/stdlib.metta | 12 ++++ lib/src/metta/runner/stdlib.rs | 88 +++++++++++++++++++++++ lib/src/metta/runner/stdlib_minimal.metta | 12 ++++ lib/src/metta/runner/stdlib_minimal.rs | 18 +++++ 4 files changed, 130 insertions(+) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index ad5ca9944..d05f0fcd0 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -415,6 +415,18 @@ (@param "Tail of an expression"))) (@return "New expression consists of two input arguments")) +(@doc min-atom + (@desc "Returns atom with min value in the expression (first argument). Only numbers allowed") + (@params ( + (@param "Expression"))) + (@return "Min value in the expression. Error if expression contains non-numeric value or is empty")) + +(@doc max-atom + (@desc "Returns atom with max value in the expression (first argument). Only numbers allowed") + (@params ( + (@param "Expression"))) + (@return "Max value in the expression. Error if expression contains non-numeric value or is empty")) + (@doc size-atom (@desc "Returns size of an expression (first argument)") (@params ( diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 4184ce4f4..2a4e1f401 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1160,6 +1160,70 @@ impl CustomExecute for IntersectionAtomOp { } } +#[derive(Clone, Debug)] +pub struct MaxAtomOp {} + +grounded_op!(MaxAtomOp, "max-atom"); + +impl Grounded for MaxAtomOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for MaxAtomOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + let arg_error = || ExecError::from("max-atom expects one argument: expression"); + let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); + for x in children.iter() { + match AsPrimitive::from_atom(x).as_number() { + None => Err(ExecError::from("Only numbers allowed in expression")), + _ => Ok({}), + }? + }; + match children.into_iter().map(|x| Into::::into(AsPrimitive::from_atom(x).as_number().unwrap())).reduce(f64::max) { + Some(max) => Ok(vec![Atom::gnd(Number::Float(max))]), + None => Err(ExecError::from("Empty expression")), + } + } +} + +#[derive(Clone, Debug)] +pub struct MinAtomOp {} + +grounded_op!(MinAtomOp, "min-atom"); + +impl Grounded for MinAtomOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + } + + fn as_execute(&self) -> Option<&dyn CustomExecute> { + Some(self) + } +} + +impl CustomExecute for MinAtomOp { + fn execute(&self, args: &[Atom]) -> Result, ExecError> { + let arg_error = || ExecError::from("min-atom expects one argument: expression"); + let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); + for x in children.iter() { + match AsPrimitive::from_atom(x).as_number() { + None => Err(ExecError::from("Only numbers allowed in expression")), + _ => Ok({}), + }? + }; + match children.into_iter().map(|x| Into::::into(AsPrimitive::from_atom(x).as_number().unwrap())).reduce(f64::min) { + Some(min) => Ok(vec![Atom::gnd(Number::Float(min))]), + None => Err(ExecError::from("Empty expression")), + } + } +} + #[derive(Clone, Debug)] pub struct SizeAtomOp {} @@ -1788,6 +1852,10 @@ mod non_minimal_only_stdlib { tref.register_token(regex(r"cdr-atom"), move |_| { cdr_atom_op.clone() }); let cons_atom_op = Atom::gnd(ConsAtomOp{}); tref.register_token(regex(r"cons-atom"), move |_| { cons_atom_op.clone() }); + let max_atom_op = Atom::gnd(MaxAtomOp{}); + tref.register_token(regex(r"max-atom"), move |_| { max_atom_op.clone() }); + let min_atom_op = Atom::gnd(MinAtomOp{}); + tref.register_token(regex(r"min-atom"), move |_| { min_atom_op.clone() }); let size_atom_op = Atom::gnd(SizeAtomOp{}); tref.register_token(regex(r"size-atom"), move |_| { size_atom_op.clone() }); let index_atom_op = Atom::gnd(IndexAtomOp{}); @@ -2087,6 +2155,26 @@ mod tests { assert_eq!(res, vec![expr!({Number::Integer(0)})]); } + #[test] + fn min_atom_op() { + let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned"); + assert_eq!(res, vec![expr!({Number::Integer(4)})]); + let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]); + assert_eq!(res, Err(ExecError::from("Only numbers allowed in expression"))); + let res = MinAtomOp{}.execute(&mut vec![expr!()]); + assert_eq!(res, Err(ExecError::from("Empty expression"))); + } + + #[test] + fn max_atom_op() { + let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned"); + assert_eq!(res, vec![expr!({Number::Float(5.5)})]); + let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]); + assert_eq!(res, Err(ExecError::from("Only numbers allowed in expression"))); + let res = MaxAtomOp{}.execute(&mut vec![expr!()]); + assert_eq!(res, Err(ExecError::from("Empty expression"))); + } + #[test] fn index_atom_op() { let res = IndexAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)}), expr!({Number::Integer(2)})]).expect("No result returned"); diff --git a/lib/src/metta/runner/stdlib_minimal.metta b/lib/src/metta/runner/stdlib_minimal.metta index 3cc56c9ad..10ce57e01 100644 --- a/lib/src/metta/runner/stdlib_minimal.metta +++ b/lib/src/metta/runner/stdlib_minimal.metta @@ -72,6 +72,18 @@ (@return "Deconsed expression")) (: decons-atom (-> Expression Expression)) +(@doc min-atom + (@desc "Returns atom with min value in the expression (first argument). Only numbers allowed") + (@params ( + (@param "Expression"))) + (@return "Min value in the expression. Error if expression contains non-numeric value or is empty")) + +(@doc max-atom + (@desc "Returns atom with max value in the expression (first argument). Only numbers allowed") + (@params ( + (@param "Expression"))) + (@return "Max value in the expression. Error if expression contains non-numeric value or is empty")) + (@doc size-atom (@desc "Returns size of an expression (first argument)") (@params ( diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index 4d48a7e03..e9fcb1f3f 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -432,6 +432,10 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared Date: Fri, 8 Nov 2024 10:29:24 +0300 Subject: [PATCH 4/4] Vitaly's fixes --- lib/src/metta/runner/stdlib.metta | 4 +- lib/src/metta/runner/stdlib.rs | 50 ++++++++++++----------- lib/src/metta/runner/stdlib_minimal.metta | 4 +- lib/src/metta/runner/stdlib_minimal.rs | 4 +- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/src/metta/runner/stdlib.metta b/lib/src/metta/runner/stdlib.metta index d05f0fcd0..7a0537bb6 100644 --- a/lib/src/metta/runner/stdlib.metta +++ b/lib/src/metta/runner/stdlib.metta @@ -418,13 +418,13 @@ (@doc min-atom (@desc "Returns atom with min value in the expression (first argument). Only numbers allowed") (@params ( - (@param "Expression"))) + (@param "Expression which contains atoms of Number type"))) (@return "Min value in the expression. Error if expression contains non-numeric value or is empty")) (@doc max-atom (@desc "Returns atom with max value in the expression (first argument). Only numbers allowed") (@params ( - (@param "Expression"))) + (@param "Expression which contains atoms of Number type"))) (@return "Max value in the expression. Error if expression contains non-numeric value or is empty")) (@doc size-atom diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index 2a4e1f401..5ec1481e9 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1167,7 +1167,7 @@ grounded_op!(MaxAtomOp, "max-atom"); impl Grounded for MaxAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1179,16 +1179,17 @@ impl CustomExecute for MaxAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("max-atom expects one argument: expression"); let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); - for x in children.iter() { - match AsPrimitive::from_atom(x).as_number() { - None => Err(ExecError::from("Only numbers allowed in expression")), - _ => Ok({}), - }? - }; - match children.into_iter().map(|x| Into::::into(AsPrimitive::from_atom(x).as_number().unwrap())).reduce(f64::max) { - Some(max) => Ok(vec![Atom::gnd(Number::Float(max))]), - None => Err(ExecError::from("Empty expression")), - } + if children.is_empty() { + Err(ExecError::from("Empty expression")) + } else { + children.into_iter().fold(Ok(f64::NEG_INFINITY), |res, x| { + match (res, AsPrimitive::from_atom(x).as_number()) { + (res @ Err(_), _) => res, + (_, None) => Err(ExecError::from("Only numbers are allowed in expression")), + (Ok(max), Some(x)) => Ok(f64::max(max, x.into())), + } + }) + }.map(|max| vec![Atom::gnd(Number::Float(max))]) } } @@ -1199,7 +1200,7 @@ grounded_op!(MinAtomOp, "min-atom"); impl Grounded for MinAtomOp { fn type_(&self) -> Atom { - Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_ATOM]) + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER]) } fn as_execute(&self) -> Option<&dyn CustomExecute> { @@ -1211,16 +1212,17 @@ impl CustomExecute for MinAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("min-atom expects one argument: expression"); let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); - for x in children.iter() { - match AsPrimitive::from_atom(x).as_number() { - None => Err(ExecError::from("Only numbers allowed in expression")), - _ => Ok({}), - }? - }; - match children.into_iter().map(|x| Into::::into(AsPrimitive::from_atom(x).as_number().unwrap())).reduce(f64::min) { - Some(min) => Ok(vec![Atom::gnd(Number::Float(min))]), - None => Err(ExecError::from("Empty expression")), - } + if children.is_empty() { + Err(ExecError::from("Empty expression")) + } else { + children.into_iter().fold(Ok(f64::INFINITY), |res, x| { + match (res, AsPrimitive::from_atom(x).as_number()) { + (res @ Err(_), _) => res, + (_, None) => Err(ExecError::from("Only numbers are allowed in expression")), + (Ok(min), Some(x)) => Ok(f64::min(min, x.into())), + } + }) + }.map(|min| vec![Atom::gnd(Number::Float(min))]) } } @@ -2160,7 +2162,7 @@ mod tests { let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned"); assert_eq!(res, vec![expr!({Number::Integer(4)})]); let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]); - assert_eq!(res, Err(ExecError::from("Only numbers allowed in expression"))); + assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression"))); let res = MinAtomOp{}.execute(&mut vec![expr!()]); assert_eq!(res, Err(ExecError::from("Empty expression"))); } @@ -2170,7 +2172,7 @@ mod tests { let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned"); assert_eq!(res, vec![expr!({Number::Float(5.5)})]); let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]); - assert_eq!(res, Err(ExecError::from("Only numbers allowed in expression"))); + assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression"))); let res = MaxAtomOp{}.execute(&mut vec![expr!()]); assert_eq!(res, Err(ExecError::from("Empty expression"))); } diff --git a/lib/src/metta/runner/stdlib_minimal.metta b/lib/src/metta/runner/stdlib_minimal.metta index 10ce57e01..08793ba91 100644 --- a/lib/src/metta/runner/stdlib_minimal.metta +++ b/lib/src/metta/runner/stdlib_minimal.metta @@ -75,13 +75,13 @@ (@doc min-atom (@desc "Returns atom with min value in the expression (first argument). Only numbers allowed") (@params ( - (@param "Expression"))) + (@param "Expression which contains atoms of Number type"))) (@return "Min value in the expression. Error if expression contains non-numeric value or is empty")) (@doc max-atom (@desc "Returns atom with max value in the expression (first argument). Only numbers allowed") (@params ( - (@param "Expression"))) + (@param "Expression which contains atoms of Number type"))) (@return "Max value in the expression. Error if expression contains non-numeric value or is empty")) (@doc size-atom diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index e9fcb1f3f..a3141ad9f 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -633,14 +633,14 @@ mod tests { fn metta_min_atom() { assert_eq!(run_program(&format!("!(min-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Integer(4)})]])); assert_eq!(run_program(&format!("!(min-atom ())")), Ok(vec![vec![expr!("Error" ({ stdlib::MinAtomOp{} } ()) "Empty expression")]])); - assert_eq!(run_program(&format!("!(min-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MinAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers allowed in expression")]])); + assert_eq!(run_program(&format!("!(min-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MinAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression")]])); } #[test] fn metta_max_atom() { assert_eq!(run_program(&format!("!(max-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Float(5.5)})]])); assert_eq!(run_program(&format!("!(max-atom ())")), Ok(vec![vec![expr!("Error" ({ stdlib::MaxAtomOp{} } ()) "Empty expression")]])); - assert_eq!(run_program(&format!("!(max-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MaxAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers allowed in expression")]])); + assert_eq!(run_program(&format!("!(max-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MaxAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression")]])); } #[test]