diff --git a/src/core.rs b/src/core.rs index 8f24f0f..44683c2 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,6 +1,8 @@ use std::{cell::RefCell, env, rc::Rc}; -use crate::env::{any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_exit, Env}; +use crate::env::{ + any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_car, mal_cdr, mal_exit, Env, +}; // This is the first time I implement a macro, and I'm copying it // so I will comment this a LOT @@ -59,6 +61,8 @@ pub fn ns_init() -> Env { "type" => Fun(|a| Ok(car(a)?.label_type()), "Returns a label indicating the type of it's argument"), "count" => Fun(|a| Ok(Int(car(a)?.if_list()?.len() as isize)), "Return the number of elements in the first argument"), "=" => Fun(mal_equals, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements (NOT IMPLEMENTED for 'Map', 'Fun' and 'MalFun')"), + "car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"), + "cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"), "assert" => Fun(mal_assert, "Return an error if assertion fails"), "read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"), "slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string"), diff --git a/src/env.rs b/src/env.rs index c43c480..1e746ec 100644 --- a/src/env.rs +++ b/src/env.rs @@ -170,6 +170,20 @@ pub fn cdr(list: &[MalType]) -> &[MalType] { } } +pub fn mal_cdr(arg: &MalType) -> MalRet { + let list = arg.if_list()?; + Ok(MalType::List(cdr(list).into())) +} + +pub fn mal_car(arg: &MalType) -> MalRet { + let list = arg.if_list()?; + if list.is_empty() { + Ok(Nil) + } else { + Ok(list[0].clone()) + } +} + /// Extract the car and cdr from a list pub fn car_cdr(list: &[MalType]) -> Result<(&MalType, &[MalType]), MalErr> { Ok((car(list)?, cdr(list))) diff --git a/src/mal_tests/mod.rs b/src/mal_tests/mod.rs index 62d8b2d..cc6c746 100644 --- a/src/mal_tests/mod.rs +++ b/src/mal_tests/mod.rs @@ -55,4 +55,9 @@ mod functional { fn atoms() { test!("atoms") } + + #[test] + fn car_cdr() { + test!("car-cdr") + } } diff --git a/tests/car-cdr.mal b/tests/car-cdr.mal new file mode 100644 index 0000000..0c66b60 --- /dev/null +++ b/tests/car-cdr.mal @@ -0,0 +1,7 @@ +;; Test car +(assert-eq (car '(1 2 3)) 1) +(assert-eq (car '()) nil) + +;; Test cdr +(assert-eq (cdr '(1 2 3)) '(2 3)) +(assert-eq (cdr '()) '())