diff --git a/schemius/src/core/builtins/list_procs.rs b/schemius/src/core/builtins/list_procs.rs index 55d9f65..21b4eff 100644 --- a/schemius/src/core/builtins/list_procs.rs +++ b/schemius/src/core/builtins/list_procs.rs @@ -261,7 +261,7 @@ pub fn r_length(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { match args.s_car().unwrap() { SExpr::List(list) => { let len = list.access().s_len(); - Ok(SExpr::Number(SNumber::Int(NativeInt::from(len as NativeInt)))) + Ok(SExpr::from(len as NativeInt)) } _ => Err(String::from("Exception in #: expected a list")), } diff --git a/schemius/src/core/builtins/number_procs.rs b/schemius/src/core/builtins/number_procs.rs index c01d3bb..5ae50e4 100644 --- a/schemius/src/core/builtins/number_procs.rs +++ b/schemius/src/core/builtins/number_procs.rs @@ -9,7 +9,7 @@ macro_rules! fn_compute_sum_prod { $( pub fn $fn(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { match args.len() { - 0 => Ok(SExpr::Number(SNumber::Int($neutral))), + 0 => Ok(SExpr::from($neutral)), _ => { let mut res = SNumber::Int($neutral); diff --git a/schemius/src/core/evaluator.rs b/schemius/src/core/evaluator.rs index 0a742cc..7057a7e 100644 --- a/schemius/src/core/evaluator.rs +++ b/schemius/src/core/evaluator.rs @@ -163,14 +163,14 @@ pub fn eval(expression: &SExpr, env: ProcedureEnv) -> EvalOutput { #[cfg(test)] mod tests { - use crate::core::s_expression::{s_number::SNumber, SExpr}; + use crate::core::s_expression::SExpr; use super::Evaluator; #[test] fn evaluator_ok_int() { let evaluator = Evaluator::default(); - let expression = SExpr::Number(SNumber::Int(0)); + let expression = SExpr::from(0); let res = evaluator.eval(&expression); assert!(res.is_ok()) diff --git a/schemius/src/core/reader.rs b/schemius/src/core/reader.rs index 345ea10..b1ae0a9 100644 --- a/schemius/src/core/reader.rs +++ b/schemius/src/core/reader.rs @@ -98,11 +98,9 @@ fn parse_token(line: &mut String, token: &str) -> Result { match token { tokens::TRUE => Ok(SExpr::Boolean(true)), tokens::FALSE => Ok(SExpr::Boolean(false)), - tokens::NEGATIVE_NAN | tokens::POSITIVE_NAN => { - Ok(SExpr::Number(SNumber::Float(NativeFloat::NAN))) - } - tokens::NEGATIVE_INFINITY => Ok(SExpr::Number(SNumber::Float(NativeFloat::NEG_INFINITY))), - tokens::POSITIVE_INFINITY => Ok(SExpr::Number(SNumber::Float(NativeFloat::INFINITY))), + tokens::NEGATIVE_NAN | tokens::POSITIVE_NAN => Ok(SExpr::from(NativeFloat::NAN)), + tokens::NEGATIVE_INFINITY => Ok(SExpr::from(NativeFloat::NEG_INFINITY)), + tokens::POSITIVE_INFINITY => Ok(SExpr::from(NativeFloat::INFINITY)), token if token.starts_with('"') => { Ok(SExpr::String(SchemeString::new(token.get(1..token.len() - 1).unwrap().to_string()))) } @@ -189,27 +187,25 @@ fn parse_token(line: &mut String, token: &str) -> Result { let number = if n_prefixes == 1 { &token[2..] } else { &token[4..] }; match NativeInt::from_str_radix(number, radix) { Ok(n) => match is_exact { - Some(true) | None => Ok(SExpr::Number(SNumber::Int(n))), - Some(false) => Ok(SExpr::Number(SNumber::Float(n as NativeFloat))), + Some(true) | None => Ok(SExpr::from(n)), + Some(false) => Ok(SExpr::from(n as NativeFloat)), }, _ => match NativeBigInt::from_str_radix(number, radix) { Ok(n) => match is_exact { - Some(true) | None => Ok(SExpr::Number(SNumber::BigInt(n))), - Some(false) => Ok(SExpr::Number(SNumber::Float(n.to_float().unwrap()))), + Some(true) | None => Ok(SExpr::from(n)), + Some(false) => Ok(SExpr::from(n.to_float().unwrap())), }, _ => match NativeRational::from_str_radix(number, radix) { Ok(q) => match is_exact { - Some(true) | None => Ok(SExpr::Number(SNumber::Rational(q))), - Some(false) => { - Ok(SExpr::Number(SNumber::Float(q.to_float().unwrap()))) - } + Some(true) | None => Ok(SExpr::from(q)), + Some(false) => Ok(SExpr::from(q.to_float().unwrap())), }, _ => match NativeFloat::from_str_radix(number, radix) { Ok(f) => match is_exact { - Some(true) => Ok(SExpr::Number(SNumber::Rational( - NativeRational::from_float(f).unwrap(), - ))), - Some(false) | None => Ok(SExpr::Number(SNumber::Float(f))), + Some(true) => { + Ok(SExpr::from(NativeRational::from_float(f).unwrap())) + } + Some(false) | None => Ok(SExpr::from(f)), }, _ => Ok(SExpr::Symbol(token.to_string())), }, @@ -218,15 +214,15 @@ fn parse_token(line: &mut String, token: &str) -> Result { } } else { match token.parse::() { - Ok(n) => Ok(SExpr::Number(SNumber::Int(n))), + Ok(n) => Ok(SExpr::from(n)), _ => match token.parse::() { - Ok(n) => Ok(SExpr::Number(SNumber::BigInt(n))), + Ok(n) => Ok(SExpr::from(n)), _ => match token.parse::() { - Ok(q) => Ok(SExpr::Number(SNumber::Rational(q))), + Ok(q) => Ok(SExpr::from(q)), _ => match token.parse::() { - Ok(f) => Ok(SExpr::Number(SNumber::Float(f))), + Ok(f) => Ok(SExpr::from(f)), _ => match token.parse::() { - Ok(c) => Ok(SExpr::Number(SNumber::Complex(c))), + Ok(c) => Ok(SExpr::from(c)), _ => match COMPLEX_POLAR_REGEX.captures(token) { Some(_) => Ok(parse_polar_complex(token)), None => Ok(SExpr::Symbol(token.to_string())), @@ -254,7 +250,7 @@ fn parse_polar_complex(token: &str) -> SExpr { let magnitude = parts[0]; let angle = parts[1]; - SExpr::Number(SNumber::Complex(NativeComplex::from_polar(magnitude, angle))) + SExpr::from(NativeComplex::from_polar(magnitude, angle)) } #[cfg(test)] diff --git a/schemius/src/core/s_expression/mod.rs b/schemius/src/core/s_expression/mod.rs index 947a5d0..ec26ed6 100644 --- a/schemius/src/core/s_expression/mod.rs +++ b/schemius/src/core/s_expression/mod.rs @@ -46,6 +46,41 @@ pub enum SExpr { Ok, } +macro_rules! impl_from_primitive { + ($($source:ident, $target:ident)*) => { + $( + impl From<$source> for SExpr { + fn from(val: $source) -> Self { + SExpr::$target(val) + } + } + )*} +} + +macro_rules! impl_from_number { + ($($source:ident, $target:ident)*) => { + $( + impl From<$source> for SExpr { + fn from(val: $source) -> Self { + SExpr::Number(SNumber::$target(val)) + } + } + )*} +} + +impl_from_primitive! { + bool, Boolean + char, Char +} + +impl_from_number! { + NativeInt, Int + NativeBigInt, BigInt + NativeRational, Rational + NativeComplex, Complex + NativeFloat, Float +} + impl fmt::Display for SExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -552,7 +587,7 @@ impl SExpr { } #[cfg(test)] -mod tests { +mod s_expression_tests { use crate::core::builtins::Primitive; use super::*; @@ -565,20 +600,20 @@ mod tests { #[test] fn test_sexpr_as_int() { - let sexpr = SExpr::Number(SNumber::Int(42)); + let sexpr = SExpr::from(42); assert_eq!(sexpr.as_int().unwrap(), 42); } #[test] fn test_sexpr_quote() { - let expression = SExpr::Number(SNumber::Int(42)); + let expression = SExpr::from(42); let quoted = expression.quote().unwrap(); assert!(quoted.is_list().unwrap() && quoted.is_quoted_list().unwrap()); } #[test] fn test_sexpr_unquote() { - let internal = SExpr::Number(SNumber::Int(42)); + let internal = SExpr::from(42); let expression = internal.quote().unwrap(); let unquoted = expression.unquote().unwrap(); assert!(unquoted.is_number().unwrap()); @@ -594,16 +629,58 @@ mod tests { fn test_sexpr_is_applyable() { let sexpr = SExpr::List(SchemeList::new(ListImplementation::from_iter([ SExpr::Procedure(Procedure::Primitive(Primitive::SUM)), - SExpr::Number(SNumber::Int(1)), - SExpr::Number(SNumber::Int(2)), + SExpr::from(1), + SExpr::from(2), ]))); assert!(sexpr.is_applyable().unwrap()); } #[test] fn test_sexpr_is_atom() { - let sexpr = SExpr::Number(SNumber::Int(NativeInt::from(3))); + let sexpr = SExpr::from(3); let is_atom = sexpr.is_atom().unwrap(); assert!(is_atom) } + + #[test] + fn test_sexpr_from_int() { + let sexpr = SExpr::from(42); + assert!(sexpr.is_number().unwrap()); + } + + #[test] + fn test_sexpr_from_big_int() { + let sexpr = SExpr::from(NativeBigInt::from(42)); + assert!(sexpr.is_number().unwrap()); + } + + #[test] + fn test_sexpr_from_rational() { + let sexpr = SExpr::from(NativeRational::new(NativeBigInt::from(1), NativeBigInt::from(2))); + assert!(sexpr.is_number().unwrap()); + } + + #[test] + fn test_sexpr_from_float() { + let sexpr = SExpr::from(NativeFloat::from(1.0)); + assert!(sexpr.is_number().unwrap()); + } + + #[test] + fn test_sexpr_from_complex() { + let sexpr = SExpr::from(NativeComplex::new(NativeFloat::from(1.0), NativeFloat::from(2.0))); + assert!(sexpr.is_number().unwrap()); + } + + #[test] + fn test_sexpr_from_char() { + let sexpr = SExpr::from('a'); + assert!(sexpr.is_char().unwrap()); + } + + #[test] + fn test_sexpr_from_bool() { + let sexpr = SExpr::from(true); + assert!(sexpr.is_boolean().unwrap()); + } }