From 0984c258eb41c6d1c6b5800b6c4c3dfa7351eec2 Mon Sep 17 00:00:00 2001 From: cowuake Date: Tue, 16 Apr 2024 08:40:19 +0200 Subject: [PATCH] Improve predicate support --- schemius/src/core/builtins.rs | 5 ++- schemius/src/core/environment.rs | 2 + schemius/src/core/reader.rs | 6 +++ schemius/src/core/s_expression/mod.rs | 63 +++++++++++++++++---------- schemius/tests/sparse.rs | 21 ++++++++- 5 files changed, 72 insertions(+), 25 deletions(-) diff --git a/schemius/src/core/builtins.rs b/schemius/src/core/builtins.rs index 349cc8d..cbc5990 100644 --- a/schemius/src/core/builtins.rs +++ b/schemius/src/core/builtins.rs @@ -30,9 +30,11 @@ impl Primitive { pub const IS_NUMBER: ProcedureSignature = r_is_number; pub const IS_EXACT: ProcedureSignature = r_is_exact; pub const IS_PAIR: ProcedureSignature = r_is_pair; + pub const IS_SYMBOL: ProcedureSignature = r_is_symbol; pub const IS_LIST: ProcedureSignature = r_is_list; pub const IS_VECTOR: ProcedureSignature = r_is_vector; pub const IS_PROCEDURE: ProcedureSignature = r_is_procedure; + pub const IS_NULL: ProcedureSignature = r_is_null; pub const ENVIRONMENT_BINDINGS: ProcedureSignature = r_environment_bindings; pub const STRING_SET: ProcedureSignature = r_string_set; pub const FLATTEN: ProcedureSignature = r_flatten; @@ -860,7 +862,8 @@ fn_is! { r_is_pair, is_pair, "pair?" r_is_vector, is_vector, "vector?" r_is_procedure, is_procedure, "procedure?" - // r_is_symbol, is_symbol, "symbol?" + r_is_symbol, is_symbol, "symbol?" + r_is_null, is_null, "null?" } fn r_string_set(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { diff --git a/schemius/src/core/environment.rs b/schemius/src/core/environment.rs index 2148f58..b04af42 100644 --- a/schemius/src/core/environment.rs +++ b/schemius/src/core/environment.rs @@ -133,6 +133,7 @@ impl Default for Environment { (String::from("cond"), SExpr::Procedure(Procedure::SpecialForm(SpecialForm::COND))), (String::from("display"), SExpr::Procedure(Procedure::Primitive(Primitive::DISPLAY))), (String::from("char?"), SExpr::Procedure(Procedure::Primitive(Primitive::IS_CHAR))), + (String::from("symbol?"), SExpr::Procedure(Procedure::Primitive(Primitive::IS_SYMBOL))), (String::from("string?"), SExpr::Procedure(Procedure::Primitive(Primitive::IS_STRING))), ( String::from("boolean?"), @@ -147,6 +148,7 @@ impl Default for Environment { String::from("procedure?"), SExpr::Procedure(Procedure::Primitive(Primitive::IS_PROCEDURE)), ), + (String::from("null?"), SExpr::Procedure(Procedure::Primitive(Primitive::IS_NULL))), (String::from("time"), SExpr::Procedure(Procedure::SpecialForm(SpecialForm::TIME))), ( String::from("environment-bindings"), diff --git a/schemius/src/core/reader.rs b/schemius/src/core/reader.rs index 563c398..7fb7d44 100644 --- a/schemius/src/core/reader.rs +++ b/schemius/src/core/reader.rs @@ -69,6 +69,12 @@ fn parse_token(line: &mut String, token: &String) -> SExpr { return SExpr::Boolean(true); } else if token == "#f" { return SExpr::Boolean(false); + } else if token == "-nan.0" || token == "+nan.0" { + return SExpr::Number(SNumber::Float(NativeFloat::NAN)); + } else if token == "-inf.0" { + return SExpr::Number(SNumber::Float(NativeFloat::NEG_INFINITY)); + } else if token == "+inf.0" { + return SExpr::Number(SNumber::Float(NativeFloat::INFINITY)); } else if token.starts_with('"') { return SExpr::String(SchemeString::new( token.get(1..token.len() - 1).unwrap().to_string(), diff --git a/schemius/src/core/s_expression/mod.rs b/schemius/src/core/s_expression/mod.rs index 5e02c9a..f32c0a5 100644 --- a/schemius/src/core/s_expression/mod.rs +++ b/schemius/src/core/s_expression/mod.rs @@ -2,7 +2,7 @@ pub mod s_number; pub mod s_procedure; use super::accessor::*; -use std::fmt; +use std::{fmt, result}; pub use self::{s_number::*, s_procedure::*}; type SAccessor = ThreadSafeAccessor; @@ -75,26 +75,23 @@ impl fmt::Display for SExpr { } impl SExpr { - pub fn is_symbol(&self, repr: Option<&str>) -> Result { + pub fn symbol_is(&self, repr: &str) -> Result { match self { - SExpr::Symbol(val) => match repr { - Some(token) => { - if val.as_str() == token { - Ok(true) - } else { - Ok(false) - } + SExpr::Symbol(val) => { + if val.as_str() == repr { + Ok(true) + } else { + Ok(false) } - None => Ok(true), - }, + } _ => Ok(false), } } #[allow(dead_code)] fn is_left_bracket(&self) -> Result { - if self.is_symbol(Some(Bracket::LEFT_ROUND)).unwrap() - || self.is_symbol(Some(Bracket::LEFT_SQUARE)).unwrap() + if self.symbol_is(Bracket::LEFT_ROUND).unwrap() + || self.symbol_is(Bracket::LEFT_SQUARE).unwrap() { Ok(true) } else { @@ -104,8 +101,8 @@ impl SExpr { #[allow(dead_code)] fn is_right_bracket(&self) -> Result { - if self.is_symbol(Some(Bracket::RIGHT_ROUND)).unwrap() - || self.is_symbol(Some(Bracket::RIGHT_SQUARE)).unwrap() + if self.symbol_is(Bracket::RIGHT_ROUND).unwrap() + || self.symbol_is(Bracket::RIGHT_SQUARE).unwrap() { Ok(true) } else { @@ -120,6 +117,13 @@ impl SExpr { } } + pub fn is_symbol(&self) -> Result { + match self { + SExpr::Symbol(_) => Ok(true), + _ => Ok(false), + } + } + pub fn is_string(&self) -> Result { match self { SExpr::String(_) => Ok(true), @@ -183,11 +187,24 @@ impl SExpr { } } + pub fn is_null(&self) -> result::Result { + match self { + SExpr::List(list) => { + if list.borrow().is_empty() { + Ok(true) + } else { + Ok(false) + } + } + _ => Ok(false), + } + } + pub fn matching_brackets(&self) -> Option> { match self { SExpr::List(list) => { let list = list.borrow(); - if !list.first().unwrap().is_symbol(Some("(")).unwrap() { + if !list.first().unwrap().symbol_is("(").unwrap() { return None; } @@ -198,7 +215,7 @@ impl SExpr { .enumerate() .filter(|x| { (pairs.is_empty() || pairs.iter().all(|(_, right)| right != &x.0)) - && x.1.is_symbol(Some(")")).unwrap() + && x.1.symbol_is(")").unwrap() }) .min_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) @@ -208,7 +225,7 @@ impl SExpr { .enumerate() .filter(|x| { (pairs.is_empty() || pairs.iter().all(|(left, _)| left != &x.0)) - && x.1.is_symbol(Some("(")).unwrap() + && x.1.symbol_is("(").unwrap() }) .filter(|x| x.0 < right) .max_by(|x, y| (x.0).cmp(&y.0)) @@ -246,14 +263,14 @@ impl SExpr { Ok(SExpr::List(flattened)) => { let borrowed_flattened = flattened.borrow(); - if borrowed_flattened.first().unwrap().is_symbol(Some("(")).unwrap() { + if borrowed_flattened.first().unwrap().symbol_is("(").unwrap() { return None; } let indexes: Vec = borrowed_flattened .iter() .enumerate() - .filter(|(_, x)| x.is_symbol(Some(symbol)).unwrap()) + .filter(|(_, x)| x.symbol_is(symbol).unwrap()) .map(|(i, _)| i - 1) .collect(); @@ -306,7 +323,7 @@ impl SExpr { let cloned = list.clone(); let mut unflattened = cloned.borrow_mut(); - if !unflattened.first().unwrap().is_symbol(Some("(")).unwrap() { + if !unflattened.first().unwrap().symbol_is("(").unwrap() { return Ok(self.clone()); } @@ -320,7 +337,7 @@ impl SExpr { match unflattened .iter() .enumerate() - .filter(|x| x.1.is_symbol(Some(")")).unwrap()) + .filter(|x| x.1.symbol_is(")").unwrap()) .min_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) { @@ -328,7 +345,7 @@ impl SExpr { match unflattened .iter() .enumerate() - .filter(|x| x.1.is_symbol(Some("(")).unwrap()) + .filter(|x| x.1.symbol_is("(").unwrap()) .filter(|x| x.0 < r) .max_by(|x, y| (x.0).cmp(&y.0)) .map(|x| x.0) diff --git a/schemius/tests/sparse.rs b/schemius/tests/sparse.rs index dfdea7b..ef6e310 100644 --- a/schemius/tests/sparse.rs +++ b/schemius/tests/sparse.rs @@ -186,7 +186,7 @@ fn interpreter_number_comparison() { } #[test] -fn interpreter_sepr_type() { +fn interpreter_sexpr_type() { integration_subtest_eval_to! { { expression: "(boolean? #f)", expected: "#t" }; { expression: r#"(string? "hello")"#, expected: "#t" }; @@ -204,5 +204,24 @@ fn interpreter_sepr_type() { { expression: "(list? '(1 . 2))", expected: "#f" }; { expression: "(pair? '(1 2 3))", expected: "#t" }; { expression: "(pair? '(1 . 2))", expected: "#t" }; + { expression: "(symbol? 'hello)", expected: "#t" }; + { expression: r#"(symbol? "hello")"#, expected: "#f" }; + } +} + +#[test] +fn interpreter_sexpr_null() { + integration_subtest_eval_to! { + { expression: "(null? '())", expected: "#t" }; + { expression: "(null? '(1 2 3))", expected: "#f" }; + { expression: "(null? 1)", expected: "#f" }; + { expression: "(null? #f)", expected: "#f" }; + { expression: "(null? 'hello)", expected: "#f" }; + { expression: "(null? 0)", expected: "#f" }; + { expression: "(null? 0.0)", expected: "#f" }; + { expression: "(null? 1/2)", expected: "#f" }; + { expression: "(null? +nan.0)", expected: "#f" }; + { expression: "(null? -inf.0)", expected: "#f" }; + { expression: "(null? +inf.0)", expected: "#f" }; } }