diff --git a/schemius/src/core/builtins/base.rs b/schemius/src/core/builtins/base_procs.rs similarity index 91% rename from schemius/src/core/builtins/base.rs rename to schemius/src/core/builtins/base_procs.rs index fb3c0c7..6080513 100644 --- a/schemius/src/core/builtins/base.rs +++ b/schemius/src/core/builtins/base_procs.rs @@ -1,12 +1,13 @@ use super::{ eval, + s_list::SList, s_procedure::{ProcedureArgs, ProcedureEnv, ProcedureOutput}, Accessor, SExpr, SchemeEnvironment, SchemeList, }; pub fn r_apply(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 2 { - return Err(format!("Exception in apply: expected 2 arguments, found {}", args.len())); + if args.s_len() != 2 { + return Err(format!("Exception in apply: expected 2 arguments, found {}", args.s_len())); } let symbol = &args[0]; @@ -28,16 +29,16 @@ pub fn r_apply(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { } pub fn r_eval(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!("Exception in eval: expected 1 argument, found {}", args.len())); + if args.s_len() != 1 { + return Err(format!("Exception in eval: expected 1 argument, found {}", args.s_len())); } eval(&args[0], env.clone()) } pub fn r_display(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!("Exception in display: expected 1 argument, found {}", args.len())); + if args.s_len() != 1 { + return Err(format!("Exception in display: expected 1 argument, found {}", args.s_len())); } match eval(&args[0], env.clone()) { diff --git a/schemius/src/core/builtins/booleans.rs b/schemius/src/core/builtins/boolean_procs.rs similarity index 100% rename from schemius/src/core/builtins/booleans.rs rename to schemius/src/core/builtins/boolean_procs.rs diff --git a/schemius/src/core/builtins/lists.rs b/schemius/src/core/builtins/list_procs.rs similarity index 65% rename from schemius/src/core/builtins/lists.rs rename to schemius/src/core/builtins/list_procs.rs index 80fe84e..6add3f1 100644 --- a/schemius/src/core/builtins/lists.rs +++ b/schemius/src/core/builtins/list_procs.rs @@ -1,12 +1,13 @@ use super::{ eval, + s_list::SList, s_procedure::{ProcedureArgs, ProcedureEnv, ProcedureOutput}, Accessor, SExpr, SchemeList, SchemePair, }; pub fn r_set_car(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 2 { - return Err(format!("Exception in set-car!: expected 2 arguments, found {}", args.len())); + if args.s_len() != 2 { + return Err(format!("Exception in set-car!: expected 2 arguments, found {}", args.s_len())); } match &args[0] { @@ -36,8 +37,8 @@ pub fn r_set_car(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { } pub fn r_cons(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 2 { - return Err(format!("Exception in cons: expected 2 arguments, found {}", args.len())); + if args.s_len() != 2 { + return Err(format!("Exception in cons: expected 2 arguments, found {}", args.s_len())); } let car = eval(&args[0], env.clone()); @@ -75,30 +76,38 @@ pub fn r_list(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { } pub fn r_flatten(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!("Exception in flatten: expected 1 argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in flatten: expected 1 argument, found {}", length)); } args[0].flatten() } pub fn r_unflatten(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!("Exception in unflatten: expected 1 argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in unflatten: expected 1 argument, found {}", length)); } args[0].unflatten() } pub fn r_car(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!("Exception in car: expected 1 argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in car: expected 1 argument, found {}", length)); } match eval(&args[0], env.clone())? { - SExpr::List(vec) => { - if vec.borrow().len() > 0 { - Ok(vec.borrow()[0].clone()) + SExpr::Pair(pair) => { + let car = pair.borrow().0.clone(); + Ok(*car) + } + SExpr::List(list) => { + if list.borrow().s_len() > 0 { + let car = list.borrow().s_car().unwrap().clone(); + Ok(car) } else { Err(String::from("Exception: # cannot take a quoted empty list")) } @@ -108,20 +117,42 @@ pub fn r_car(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { } pub fn r_cdr(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { + if args.s_len() != 1 { return Err(String::from("Exception: # can take one only argument")); } match eval(&args[0], env.clone())? { - SExpr::List(vec) => match vec.borrow().len() { - 1.. => { - let mut cdr = vec.borrow().clone(); - cdr.remove(0); - - Ok(SExpr::List(SchemeList::new(cdr))) + SExpr::Pair(pair) => { + let cdr = pair.borrow().1.clone(); + Ok(*cdr) + } + SExpr::List(list) => { + let list = list.borrow(); + match list.s_len() { + 1.. => { + let cdr = list.s_cdr().map(|x| x.clone()).collect(); + Ok(SExpr::List(SchemeList::new(cdr))) + } + _ => { + Err(String::from("Exception: # cannot take a quoted empty list")) + } } - _ => Err(String::from("Exception: # cannot take a quoted empty list")), - }, + } _ => Err(String::from("Exception: # cannot be applied to quoted symbol")), } } + +pub fn r_reverse(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in #: expected 1 argument, found {}", length)); + } + + match &args[0] { + SExpr::List(list) => { + let reversed = list.borrow().s_reverse(); + Ok(SExpr::List(SchemeList::new(reversed))) + } + _ => Err(String::from("Exception in #: expected a list")), + } +} diff --git a/schemius/src/core/builtins/mod.rs b/schemius/src/core/builtins/mod.rs index 24ada69..0d9d9db 100644 --- a/schemius/src/core/builtins/mod.rs +++ b/schemius/src/core/builtins/mod.rs @@ -1,18 +1,18 @@ use super::{accessor::*, environment::*, evaluator::*, s_expression::*}; -mod base; -mod booleans; -mod lists; -mod numbers; +mod base_procs; +mod boolean_procs; +mod list_procs; +mod number_procs; mod special_forms; -mod strings; +mod string_procs; -use base::*; -use booleans::*; -use lists::*; -use numbers::*; +use base_procs::*; +use boolean_procs::*; +use list_procs::*; +use number_procs::*; use special_forms::*; -use strings::*; +use string_procs::*; pub struct Primitive; pub struct SpecialForm; @@ -55,6 +55,7 @@ impl Primitive { pub const IS_VECTOR: ProcedureSignature = r_is_vector; pub const IS_ZERO: ProcedureSignature = r_is_zero; pub const ENVIRONMENT_BINDINGS: ProcedureSignature = r_environment_bindings; + pub const REVERSE: ProcedureSignature = r_reverse; pub const MAKE_STRING: ProcedureSignature = r_make_string; pub const STRING: ProcedureSignature = r_string; pub const STRING_APPEND: ProcedureSignature = r_string_append; diff --git a/schemius/src/core/builtins/numbers.rs b/schemius/src/core/builtins/number_procs.rs similarity index 100% rename from schemius/src/core/builtins/numbers.rs rename to schemius/src/core/builtins/number_procs.rs diff --git a/schemius/src/core/builtins/special_forms.rs b/schemius/src/core/builtins/special_forms.rs index fdef966..fd2eb89 100644 --- a/schemius/src/core/builtins/special_forms.rs +++ b/schemius/src/core/builtins/special_forms.rs @@ -2,6 +2,7 @@ use std::time::Instant; use super::{ eval, r_eval, + s_list::SList, s_procedure::{Procedure, ProcedureArgs, ProcedureEnv, ProcedureOutput, SpecialFormOutput}, Accessor, Environment, SExpr, SchemeEnvironment, SchemeList, }; @@ -20,10 +21,11 @@ fn list_args(list: &[SExpr]) -> Result, String> { } pub fn r_lambda(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() < 2 { + let length = args.s_len(); + if length < 2 { return Err(format!( "Exception in lambda: expected at least 2 arguments, found {}", - args.len() + length )); } @@ -41,7 +43,7 @@ pub fn r_lambda(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { pub fn r_define(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { // TODO: Improve this mess! - match args.len() { + match args.s_len() { 1 => Ok(SExpr::Ok), 2.. => match &args[0] { SExpr::Symbol(name) => match eval(&args[1], env.clone()) { @@ -59,7 +61,7 @@ pub fn r_define(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { Err(e) => Err(e), }, SExpr::List(list) => { - if list.borrow().len() == 0 { + if list.borrow().s_len() == 0 { return Err(String::from("Exception (TODO?): deal with empty lists")); } @@ -67,7 +69,7 @@ pub fn r_define(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { let mut lambda_args: Vec = vec![]; let lambda_body = &mut args[1..].to_vec(); - if list.borrow().len() > 1 { + if list.borrow().s_len() > 1 { for arg in &list.borrow()[1..] { lambda_args.push(arg.clone()); } @@ -95,8 +97,9 @@ pub fn r_define(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_set(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() != 2 { - return Err(format!("Exception in set!: expected 2 arguments, found {}", args.len())); + let length = args.s_len(); + if length != 2 { + return Err(format!("Exception in set!: expected 2 arguments, found {}", length)); } match &args[0] { @@ -119,10 +122,11 @@ pub fn r_set(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_let(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() < 2 { + let length = args.s_len(); + if length < 2 { return Err(format!( "Exception in let: expected at least 2 arguments, found {}", - args.len() + args.s_len() )); } @@ -168,11 +172,9 @@ pub fn r_let(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_let_star(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() < 2 { - return Err(format!( - "Exception in let: expected at least 2 arguments, found {}", - args.len() - )); + let length = args.s_len(); + if length < 2 { + return Err(format!("Exception in let: expected at least 2 arguments, found {}", length)); } let mut inner_env = env; @@ -219,16 +221,14 @@ pub fn r_let_star(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_if(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() != 2 && args.len() != 3 { - return Err(format!( - "Exception in if: expected two or three arguments, found {}", - args.len() - )); + let length = args.s_len(); + if length != 2 && length != 3 { + return Err(format!("Exception in if: expected two or three arguments, found {}", length)); } match eval(&args[0], env.clone()) { Ok(condition) => match condition { - SExpr::Boolean(false) => match args.len() { + SExpr::Boolean(false) => match length { 2 => Ok(SExpr::Ok), 3 => Ok(args[2].clone()), _ => Err(String::from("Exception: wrong number of arguments for if")), @@ -240,8 +240,9 @@ pub fn r_if(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_not(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() != 1 { - return Err(format!("Exception in not: expected one argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in not: expected one argument, found {}", length)); } match eval(&args[0], env.clone()) { @@ -257,7 +258,7 @@ pub fn r_begin(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { if args.is_empty() { return Err(format!( "Exception in begin: expected at least 1 argument, found {}", - args.len() + args.s_len() )); } @@ -274,16 +275,18 @@ pub fn r_begin(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { } pub fn r_quote(args: ProcedureArgs, _: ProcedureEnv) -> SpecialFormOutput { - if args.len() != 1 { - return Err(format!("Exception in ': expected 1 argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in ': expected 1 argument, found {}", length)); } Ok(args[0].clone()) } pub fn r_quasiquote(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { - if args.len() != 1 { - return Err(format!("Exception in `: expected 1 argument, found {}", args.len())); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in `: expected 1 argument, found {}", length)); } match &args[0] { @@ -373,7 +376,7 @@ pub fn r_quasiquote(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput borrowed_list[(lparen_idx + 1)..rparen_idx].to_vec(); // The expression... Must be a non-self-evaluating one! - if raw_expr.len() == 1 { + if raw_expr.s_len() == 1 { let suspect = raw_expr.first().unwrap(); let mut incriminated = false; @@ -423,13 +426,13 @@ pub fn r_quasiquote(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput Ok(ref res) => match res { SExpr::List(internal) => { let borrowed_internal = internal.borrow(); - offset -= (borrowed_internal.len() - 1) as i32; + offset -= (borrowed_internal.s_len() - 1) as i32; for i in (first_idx..last_idx).rev() { borrowed_list.remove(i); } - for i in (0..internal.borrow().len()).rev() { + for i in (0..internal.borrow().s_len()).rev() { borrowed_list.splice( first_idx..first_idx, [borrowed_internal[i].clone()], @@ -465,22 +468,23 @@ pub fn r_cond(args: ProcedureArgs, env: ProcedureEnv) -> SpecialFormOutput { if args.is_empty() { return Err(format!( "Exception in cond: expected at least 1 argument, found {}", - args.len() + args.s_len() )); } - let have_else_clause = args.len() > 3 - && match &args[args.len() - 2] { + let length = args.s_len(); + let have_else_clause = length > 3 + && match &args[length - 2] { SExpr::Symbol(clause) => *clause == "else", _ => false, }; - let iterator = if have_else_clause { &args[0..args.len() - 2] } else { &args }; + let iterator = if have_else_clause { &args[0..length - 2] } else { &args }; for block in iterator { match block { SExpr::List(list) => { - if list.borrow().len() != 2 { + if list.borrow().s_len() != 2 { return Err(String::from( "Exception: malformed args provided to #", )); @@ -525,7 +529,7 @@ pub fn r_time(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { } pub fn r_and(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - let n_args = args.len(); + let n_args = args.s_len(); if n_args == 0 { return Ok(SExpr::Boolean(true)); } @@ -546,7 +550,7 @@ pub fn r_and(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { } pub fn r_or(args: ProcedureArgs, env: ProcedureEnv) -> ProcedureOutput { - let n_args = args.len(); + let n_args = args.s_len(); if n_args == 0 { return Ok(SExpr::Boolean(true)); } diff --git a/schemius/src/core/builtins/strings.rs b/schemius/src/core/builtins/string_procs.rs similarity index 85% rename from schemius/src/core/builtins/strings.rs rename to schemius/src/core/builtins/string_procs.rs index 240f5b1..9664893 100644 --- a/schemius/src/core/builtins/strings.rs +++ b/schemius/src/core/builtins/string_procs.rs @@ -1,4 +1,5 @@ use super::{ + s_list::SList, s_number::NativeInt, s_procedure::{ProcedureArgs, ProcedureEnv, ProcedureOutput}, Accessor, SExpr, SchemeNumber, SchemeString, @@ -9,10 +10,11 @@ pub fn r_string(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { return Err("Exception in string: one or more arguments are not characters".to_string()); } - match args.len() { - 0 => { - Err(format!("Exception in string: expected at least 1 argument, found {}", args.len())) - } + match args.s_len() { + 0 => Err(format!( + "Exception in string: expected at least 1 argument, found {}", + args.s_len() + )), 1 => Ok(SExpr::String(SchemeString::new(args[0].to_string()))), 2.. => { let mut output = String::new(); @@ -25,10 +27,11 @@ pub fn r_string(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { } pub fn r_make_string(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 && args.len() != 2 { + let length = args.s_len(); + if length != 1 && length != 2 { return Err(format!( "Exception in make-string: expected 1 or 2 arguments, found {}", - args.len() + length )); } @@ -36,7 +39,7 @@ pub fn r_make_string(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { SExpr::Number(n) => { let n = n.to_int().unwrap(); let mut output = String::new(); - let character = if args.len() == 2 { + let character = if length == 2 { match &args[1] { SExpr::Char(c) => *c, other => { @@ -71,8 +74,9 @@ pub fn r_string_append(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput } pub fn r_string_ref(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 2 { - return Err(format!("Exception in string-ref: expected 2 arguments, found {}", args.len())); + let length = args.s_len(); + if length != 2 { + return Err(format!("Exception in string-ref: expected 2 arguments, found {}", length)); } match &args[0] { @@ -95,7 +99,8 @@ pub fn r_string_ref(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { } pub fn r_string_set(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 3 { + let length = args.s_len(); + if length != 3 { return Err(format!( "Exception in string-set!: expected 3 arguments, found {}", args.len() @@ -132,11 +137,9 @@ pub fn r_string_set(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { } pub fn r_string_upcase(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!( - "Exception in string-upcase: expected 1 argument, found {}", - args.len() - )); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in string-upcase: expected 1 argument, found {}", length)); } match &args[0] { @@ -149,11 +152,9 @@ pub fn r_string_upcase(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput } pub fn r_string_downcase(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!( - "Exception in string-downcase: expected 1 argument, found {}", - args.len() - )); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in string-downcase: expected 1 argument, found {}", length)); } match &args[0] { @@ -166,11 +167,9 @@ pub fn r_string_downcase(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutpu } pub fn r_string_length(args: ProcedureArgs, _: ProcedureEnv) -> ProcedureOutput { - if args.len() != 1 { - return Err(format!( - "Exception in string-length: expected 1 argument, found {}", - args.len() - )); + let length = args.s_len(); + if length != 1 { + return Err(format!("Exception in string-length: expected 1 argument, found {}", length)); } match &args[0] { diff --git a/schemius/src/core/environment.rs b/schemius/src/core/environment.rs index 3ad9122..9d28be6 100644 --- a/schemius/src/core/environment.rs +++ b/schemius/src/core/environment.rs @@ -156,6 +156,7 @@ impl Default for Environment { "procedure?" => IS_PROCEDURE "null?" => IS_NULL "environment-bindings" => ENVIRONMENT_BINDINGS + "reverse" => REVERSE "string" => STRING "make-string" => MAKE_STRING "string-append" => STRING_APPEND diff --git a/schemius/src/core/evaluator.rs b/schemius/src/core/evaluator.rs index 7c9aa2a..73da750 100644 --- a/schemius/src/core/evaluator.rs +++ b/schemius/src/core/evaluator.rs @@ -47,7 +47,7 @@ pub fn eval(expression: &SExpr, env: ProcedureEnv) -> EvalOutput { } } SExpr::List(list) => { - if list.borrow().len() > 0 { + if list.borrow().s_len() > 0 { let first = eval(&list.borrow()[0], current_env.clone()); match first { Ok(res) => match res { @@ -124,16 +124,28 @@ pub fn eval(expression: &SExpr, env: ProcedureEnv) -> EvalOutput { ref body, ref closure_env, ) => { - if arg_names.len() != args.len() { + if arg_names.s_len() != args.s_len() { return Err(String::from("Exception: found different lengths for arguments and their names")); } let mut expanded_args = vec![]; for arg in args.iter() { - match eval(arg, current_env.clone()) { - Ok(res) => expanded_args.push(res), - Err(e) => return Err(e), + match arg { + SExpr::List(list) + if list + .borrow() + .s_car() + .unwrap() + .is_quote() + .unwrap() => + { + expanded_args.push(arg.clone()) + } + _ => match eval(arg, current_env.clone()) { + Ok(res) => expanded_args.push(res), + Err(e) => return Err(e), + }, } } diff --git a/schemius/src/core/s_expression/mod.rs b/schemius/src/core/s_expression/mod.rs index f5123ca..ff0ab00 100644 --- a/schemius/src/core/s_expression/mod.rs +++ b/schemius/src/core/s_expression/mod.rs @@ -1,10 +1,11 @@ +pub mod s_list; pub mod s_number; pub mod s_procedure; use super::accessor::*; use std::{fmt, result}; -pub use self::{s_number::*, s_procedure::*}; +pub use self::{s_list::*, s_number::*, s_procedure::*}; type SAccessor = ThreadSafeAccessor; pub type SchemeBoolean = bool; @@ -131,6 +132,13 @@ impl SExpr { } } + pub fn is_quote(&self) -> Result { + Ok(self.symbol_is("'").unwrap() + || self.symbol_is("quote").unwrap() + || self.symbol_is("`").unwrap() + || self.symbol_is("quasiquote").unwrap()) + } + pub fn is_string(&self) -> Result { match self { SExpr::String(_) => Ok(true), @@ -338,7 +346,7 @@ impl SExpr { pairs .iter() .map(|(left, right)| { - if *left == 0 && *right == list.len() - 1 { + if *left == 0 && *right == list.s_len() - 1 { (left, right, 0) } else { ( diff --git a/schemius/src/core/s_expression/s_list.rs b/schemius/src/core/s_expression/s_list.rs new file mode 100644 index 0000000..527790c --- /dev/null +++ b/schemius/src/core/s_expression/s_list.rs @@ -0,0 +1,61 @@ +pub trait SList +where + T: Clone, +{ + fn s_car(&self) -> Option<&T>; + fn s_cdr(&self) -> Box + '_>; + fn s_len(&self) -> usize; + fn s_reverse(&self) -> Self; +} + +impl SList for Vec +where + T: Clone, +{ + fn s_car(&self) -> Option<&T> { + self.first() + } + + fn s_cdr(&self) -> Box + '_> { + Box::new(self.iter().skip(1)) + } + + fn s_len(&self) -> usize { + self.len() + } + + fn s_reverse(&self) -> Self { + self.iter().rev().cloned().collect() + } +} + +#[cfg(test)] +pub mod tests_slist_vector { + use super::*; + + #[test] + fn test_slist_vector_car() { + let list = vec![1, 2, 3, 4, 5]; + assert_eq!(list.s_car(), Some(&1)); + } + + #[test] + fn test_slist_vector_cdr() { + let list = vec![1, 2, 3, 4, 5]; + let cdr: Vec = list.s_cdr().cloned().collect(); + assert_eq!(cdr, vec![2, 3, 4, 5]); + } + + #[test] + fn test_slist_vector_len() { + let list = vec![1, 2, 3, 4, 5]; + assert_eq!(list.s_len(), 5); + } + + #[test] + fn test_slist_reverse() { + let list = vec![1, 2, 3, 4, 5]; + let reversed = list.s_reverse(); + assert_eq!(reversed, vec![5, 4, 3, 2, 1]); + } +} diff --git a/schemius/src/core/s_expression/s_pair.rs b/schemius/src/core/s_expression/s_pair.rs new file mode 100644 index 0000000..e69de29 diff --git a/schemius/tests/r7rs_compliance.rs b/schemius/tests/r7rs_compliance.rs index 3efbcc8..7179f78 100644 --- a/schemius/tests/r7rs_compliance.rs +++ b/schemius/tests/r7rs_compliance.rs @@ -200,3 +200,21 @@ fn interpreter_r7rs_numbers_is() { // { expression: "(nan? 1+2i) ", expected: "#f" }; } } + +#[test] +fn interpreter_r7rs_pair_list_procedures() { + integration_subtest_eval_to! { + { expression: "(car '(a b c))", expected: "a"}; + { expression: "(car '((a) b c d))", expected: "(a)"}; + { expression: "(car '(1 . 2))", expected: "1"}; + { expression: "(cdr '((a) b c d))", expected: "(b c d)"}; + { expression: "(cdr '(1 . 2))", expected: "2"}; + { expression: "(reverse '(a b c))", expected: "(c b a)"}; + { expression: "(reverse '(a (b c) d (e (f))))", expected: "((e (f)) d (b c) a)" }; + } + + integration_subtest_is_err! { + expression: "(car '())"; + expression: "(cdr '())"; + } +} diff --git a/schemius/tests/sparse.rs b/schemius/tests/sparse.rs index 86ac428..2388738 100644 --- a/schemius/tests/sparse.rs +++ b/schemius/tests/sparse.rs @@ -351,6 +351,24 @@ fn interpreter_sexpr_null() { } } +#[test] +fn interpreter_r7rs_pair_list_procedures() { + integration_subtest_eval_to! { + { expression: "(car '(1 2 3))", expected: "1" }; + { expression: "(cdr '(1 2 3))", expected: "(2 3)" }; + { expression: "(cadr '(1 2 3))", expected: "2" }; + { expression: "(caar '((1 2) 3 4))", expected: "1" }; + { expression: "(cdar '((1 2) 3 4))", expected: "(2)" }; + { expression: "(cddar '((1 2) 3 4))", expected: "()" }; + } + + integration_subtest_is_err! { + expression: "(caaar '((1 2) 3 4))"; + expression: "(caadr '((1 2) 3 4))"; + expression: "(cdaar '((1 2) 3 4))"; + } +} + #[test] fn interpreter_strings() { integration_subtest_eval_to! {