From 67d3f03614c84c184a0e2abd4f13f01d3ba43072 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 14 Nov 2024 12:56:15 +0300 Subject: [PATCH 1/3] Add method to convert atom into Str --- lib/src/atom/serial.rs | 20 ++++++++++++++++++++ lib/src/metta/runner/string.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/src/atom/serial.rs b/lib/src/atom/serial.rs index 80b959a72..e1a3ce76e 100644 --- a/lib/src/atom/serial.rs +++ b/lib/src/atom/serial.rs @@ -39,6 +39,26 @@ pub enum Error { pub trait ConvertingSerializer: Serializer { fn as_mut(&mut self) -> &mut dyn Serializer; fn into_type(self) -> Option; + + /// Converts atom into Rust value using `Self::default` + fn convert(atom: &super::Atom) -> Option + where + T: 'static + Clone, + Self: Default { + std::convert::TryInto::<&dyn super::GroundedAtom>::try_into(atom) + .ok() + .map(|gnd| { + gnd.as_any_ref() + .downcast_ref::() + .cloned() + .or_else(|| { + let mut serializer = Self::default(); + let _ = gnd.serialize(&mut serializer); + serializer.into_type() + }) + }) + .flatten() + } } /// Serialization result type diff --git a/lib/src/metta/runner/string.rs b/lib/src/metta/runner/string.rs index b1cb13b6e..b187b7fba 100644 --- a/lib/src/metta/runner/string.rs +++ b/lib/src/metta/runner/string.rs @@ -1,22 +1,32 @@ use crate::*; use crate::common::collections::ImmutableString; use crate::serial; +use crate::atom::serial::ConvertingSerializer; +/// String type pub const ATOM_TYPE_STRING : Atom = sym!("String"); +/// Grounded Rust string representation #[derive(Clone, PartialEq, Debug)] pub struct Str(ImmutableString); impl Str { + /// Construct new instance from string literal pub fn from_str(s: &'static str) -> Self { Str(ImmutableString::Literal(s)) } + /// Construct new instance from owned string pub fn from_string(s: String) -> Self { Str(ImmutableString::Allocated(s)) } + /// Return reference to string slice pub fn as_str(&self) -> &str { self.0.as_str() } + /// Try to convert an atom into `Str` instance + pub fn from_atom(atom: &Atom) -> Option { + StrSerializer::convert(atom) + } } impl AsRef for Str { @@ -56,3 +66,24 @@ pub fn strip_quotes(src: &str) -> &str { } src } + +#[derive(Default)] +struct StrSerializer { + value: Option, +} + +impl serial::Serializer for StrSerializer { + fn serialize_str(&mut self, v: &str) -> serial::Result { + self.value = Some(Str::from_string(v.into())); + Ok(()) + } +} + +impl serial::ConvertingSerializer for StrSerializer { + fn as_mut(&mut self) -> &mut dyn serial::Serializer { + self + } + fn into_type(self) -> Option { + self.value + } +} From 0504d23e2eaeefe48527913b34a2e0824e43f5c1 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 14 Nov 2024 13:28:03 +0300 Subject: [PATCH 2/3] Use ConvertingSerializer::convert() to replace AsPrimitive class Remove ConvertingSerializer::as_mut() method. Simplify argument extraction code. --- lib/src/atom/serial.rs | 1 - lib/src/metta/runner/arithmetics.rs | 141 ++++++++++--------------- lib/src/metta/runner/stdlib_minimal.rs | 18 ++-- lib/src/metta/runner/string.rs | 3 - 4 files changed, 64 insertions(+), 99 deletions(-) diff --git a/lib/src/atom/serial.rs b/lib/src/atom/serial.rs index e1a3ce76e..9d80af566 100644 --- a/lib/src/atom/serial.rs +++ b/lib/src/atom/serial.rs @@ -37,7 +37,6 @@ pub enum Error { /// Serializer which converts serialized atom into native Rust type T. pub trait ConvertingSerializer: Serializer { - fn as_mut(&mut self) -> &mut dyn Serializer; fn into_type(self) -> Option; /// Converts atom into Rust value using `Self::default` diff --git a/lib/src/metta/runner/arithmetics.rs b/lib/src/metta/runner/arithmetics.rs index bc8374b44..fbc08afef 100644 --- a/lib/src/metta/runner/arithmetics.rs +++ b/lib/src/metta/runner/arithmetics.rs @@ -1,6 +1,7 @@ use crate::*; use crate::metta::*; use crate::atom::serial; +use crate::atom::serial::ConvertingSerializer; use std::fmt::Display; @@ -70,6 +71,10 @@ impl Number { (a.cast(res_type), b.cast(res_type)) } + pub fn from_atom(atom: &Atom) -> Option { + NumberSerializer::convert(atom) + } + fn get_type(&self) -> NumberType { match self { Number::Integer(_) => NumberType::Integer, @@ -124,6 +129,28 @@ impl Grounded for Number { } } +#[derive(Default)] +struct NumberSerializer { + value: Option, +} + +impl serial::Serializer for NumberSerializer { + fn serialize_i64(&mut self, v: i64) -> serial::Result { + self.value = Some(Number::Integer(v)); + Ok(()) + } + fn serialize_f64(&mut self, v: f64) -> serial::Result { + self.value = Some(Number::Float(v)); + Ok(()) + } +} + +impl serial::ConvertingSerializer for NumberSerializer { + fn into_type(self) -> Option { + self.value + } +} + #[derive(Clone, PartialEq, Debug)] pub struct Bool(pub bool); @@ -135,6 +162,10 @@ impl Bool { _ => panic!("Could not parse Bool value: {}", b), } } + + pub fn from_atom(atom: &Atom) -> Option { + BoolSerializer::convert(atom) + } } impl Into for bool { @@ -172,6 +203,25 @@ impl CustomMatch for Bool { } } +#[derive(Default)] +struct BoolSerializer { + value: Option, +} + +impl serial::Serializer for BoolSerializer { + fn serialize_bool(&mut self, v: bool) -> serial::Result { + self.value = Some(Bool(v)); + Ok(()) + } +} + +impl serial::ConvertingSerializer for BoolSerializer { + fn into_type(self) -> Option { + self.value + } +} + + macro_rules! def_binary_number_op { ($name:ident, $op:tt, $r:ident, $ret_type:ident) => { #[derive(Clone, PartialEq, Debug)] @@ -196,8 +246,8 @@ macro_rules! def_binary_number_op { impl CustomExecute for $name { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from(concat!(stringify!($op), " expects two number arguments")); - let a = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?; - let b = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?; + let a = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?; + let b = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?; let (a, b) = Number::promote(a, b); let res: $ret_type = match (a, b) { @@ -246,8 +296,8 @@ macro_rules! def_binary_bool_op { impl CustomExecute for $name { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from(concat!(stringify!($disp), " expects two boolean arguments")); - let Bool(a) = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_bool().ok_or_else(arg_error)?; - let Bool(b) = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_bool().ok_or_else(arg_error)?; + let Bool(a) = args.get(0).and_then(Bool::from_atom).ok_or_else(arg_error)?; + let Bool(b) = args.get(1).and_then(Bool::from_atom).ok_or_else(arg_error)?; Ok(vec![Atom::gnd(Bool(a $op b))]) } @@ -309,93 +359,12 @@ impl Grounded for NotOp { impl CustomExecute for NotOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("not expects one boolean arguments"); - let &Bool(a) = args.get(0).ok_or_else(arg_error)?.as_gnd::().ok_or_else(arg_error)?; + let &Bool(a) = args.get(0).and_then(Atom::as_gnd).ok_or_else(arg_error)?; Ok(vec![Atom::gnd(Bool(!a))]) } } -#[derive(Default)] -struct BoolSerializer { - value: Option, -} - -impl serial::Serializer for BoolSerializer { - fn serialize_bool(&mut self, v: bool) -> serial::Result { - self.value = Some(Bool(v)); - Ok(()) - } -} - -#[derive(Default)] -struct NumberSerializer { - value: Option, -} - -impl serial::Serializer for NumberSerializer { - fn serialize_i64(&mut self, v: i64) -> serial::Result { - self.value = Some(Number::Integer(v)); - Ok(()) - } - fn serialize_f64(&mut self, v: f64) -> serial::Result { - self.value = Some(Number::Float(v)); - Ok(()) - } -} - -pub struct AsPrimitive<'a> { - atom: &'a super::Atom -} - -impl<'a> AsPrimitive<'a> { - pub fn from_atom(atom: &'a super::Atom) -> Self { - Self{ atom } - } - - fn as_gnd(&self) -> Option<&dyn super::GroundedAtom> { - std::convert::TryInto::<&dyn super::GroundedAtom>::try_into(self.atom).ok() - } - - fn as_type>(&self, mut serializer: S) -> Option { - self.as_gnd() - .map(|gnd| { - gnd.as_any_ref() - .downcast_ref::() - .cloned() - .or_else(|| { - let _ = gnd.serialize(serializer.as_mut()); - serializer.into_type() - }) - }).flatten() - } - - pub fn as_bool(self) -> Option { - self.as_type(BoolSerializer::default()) - } - - pub fn as_number(self) -> Option { - self.as_type(NumberSerializer::default()) - } -} - -impl serial::ConvertingSerializer for BoolSerializer { - fn as_mut(&mut self) -> &mut dyn serial::Serializer { - self - } - fn into_type(self) -> Option { - self.value - } -} - -impl serial::ConvertingSerializer for NumberSerializer { - fn as_mut(&mut self) -> &mut dyn serial::Serializer { - self - } - fn into_type(self) -> Option { - self.value - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/lib/src/metta/runner/stdlib_minimal.rs b/lib/src/metta/runner/stdlib_minimal.rs index 03cdb8672..4f02f471f 100644 --- a/lib/src/metta/runner/stdlib_minimal.rs +++ b/lib/src/metta/runner/stdlib_minimal.rs @@ -1126,7 +1126,7 @@ impl CustomExecute for MaxAtomOp { Err(ExecError::from("Empty expression")) } else { children.into_iter().fold(Ok(f64::NEG_INFINITY), |res, x| { - match (res, AsPrimitive::from_atom(x).as_number()) { + match (res, Number::from_atom(x)) { (res @ Err(_), _) => res, (_, None) => Err(ExecError::from("Only numbers are allowed in expression")), (Ok(max), Some(x)) => Ok(f64::max(max, x.into())), @@ -1159,7 +1159,7 @@ impl CustomExecute for MinAtomOp { Err(ExecError::from("Empty expression")) } else { children.into_iter().fold(Ok(f64::INFINITY), |res, x| { - match (res, AsPrimitive::from_atom(x).as_number()) { + match (res, Number::from_atom(x)) { (res @ Err(_), _) => res, (_, None) => Err(ExecError::from("Only numbers are allowed in expression")), (Ok(min), Some(x)) => Ok(f64::min(min, x.into())), @@ -1212,7 +1212,7 @@ impl CustomExecute for IndexAtomOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("index-atom expects two arguments: expression and atom"); let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children(); - let index = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?; + let index = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?; match children.get(Into::::into(index) as usize) { Some(atom) => Ok(vec![atom.clone()]), None => Err(ExecError::from("Index is out of bounds")), @@ -1309,8 +1309,8 @@ impl Grounded for RandomIntOp { impl CustomExecute for RandomIntOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("random-int expects two arguments: number (start) and number (end)"); - let start: i64 = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?.into(); - let end: i64 = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?.into(); + let start: i64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into(); + let end: i64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into(); let range = start..end; if range.is_empty() { return Err(ExecError::from("Range is empty")); @@ -1338,8 +1338,8 @@ impl Grounded for RandomFloatOp { impl CustomExecute for RandomFloatOp { fn execute(&self, args: &[Atom]) -> Result, ExecError> { let arg_error = || ExecError::from("random-float expects two arguments: number (start) and number (end)"); - let start: f64 = AsPrimitive::from_atom(args.get(0).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?.into(); - let end: f64 = AsPrimitive::from_atom(args.get(1).ok_or_else(arg_error)?).as_number().ok_or_else(arg_error)?.into(); + let start: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into(); + let end: f64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into(); let range = start..end; if range.is_empty() { return Err(ExecError::from("Range is empty")); @@ -3080,14 +3080,14 @@ mod tests { fn random_op() { let res = RandomIntOp{}.execute(&mut vec![expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]); let range = 0..5; - let res_i64: i64 = AsPrimitive::from_atom(res.unwrap().get(0).unwrap()).as_number().unwrap().into(); + let res_i64: i64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into(); assert!(range.contains(&res_i64)); let res = RandomIntOp{}.execute(&mut vec![expr!({Number::Integer(2)}), expr!({Number::Integer(-2)})]); assert_eq!(res, Err(ExecError::from("Range is empty"))); let res = RandomFloatOp{}.execute(&mut vec![expr!({Number::Integer(0)}), expr!({Number::Integer(5)})]); let range = 0.0..5.0; - let res_f64: f64 = AsPrimitive::from_atom(res.unwrap().get(0).unwrap()).as_number().unwrap().into(); + let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into(); assert!(range.contains(&res_f64)); let res = RandomFloatOp{}.execute(&mut vec![expr!({Number::Integer(0)}), expr!({Number::Integer(0)})]); assert_eq!(res, Err(ExecError::from("Range is empty"))); diff --git a/lib/src/metta/runner/string.rs b/lib/src/metta/runner/string.rs index b187b7fba..82ab90ca8 100644 --- a/lib/src/metta/runner/string.rs +++ b/lib/src/metta/runner/string.rs @@ -80,9 +80,6 @@ impl serial::Serializer for StrSerializer { } impl serial::ConvertingSerializer for StrSerializer { - fn as_mut(&mut self) -> &mut dyn serial::Serializer { - self - } fn into_type(self) -> Option { self.value } From 7a6b1f03f1410f81b53735ddb2c87a1b6e70d1e1 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 14 Nov 2024 13:39:54 +0300 Subject: [PATCH 3/3] Panic if ConvertingSerializer returns error --- lib/src/atom/serial.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/atom/serial.rs b/lib/src/atom/serial.rs index 9d80af566..c7d0b8c6f 100644 --- a/lib/src/atom/serial.rs +++ b/lib/src/atom/serial.rs @@ -30,6 +30,7 @@ pub trait Serializer { } /// Serialization error code +#[derive(Debug)] pub enum Error { /// Serialization of the type is not supported by serializer. NotSupported, @@ -52,7 +53,7 @@ pub trait ConvertingSerializer: Serializer { .cloned() .or_else(|| { let mut serializer = Self::default(); - let _ = gnd.serialize(&mut serializer); + gnd.serialize(&mut serializer).expect("ConvertingSerializer is not expected returning error"); serializer.into_type() }) })