From 3b896fa6c4562467c3dce4ca397cd9c8aaaac5ed Mon Sep 17 00:00:00 2001 From: teo3300 Date: Mon, 17 Jun 2024 02:16:51 +0900 Subject: [PATCH] Adding map for HOF --- Makefile | 4 +++- src/core.rs | 4 +++- src/env.rs | 32 ++++++++++++++++++++++++++++++++ src/mal_tests/mod.rs | 5 +++++ tests/map.mal | 5 +++++ 5 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 tests/map.mal diff --git a/Makefile b/Makefile index 732661a..fab3894 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,10 @@ build-release: test: cargo test --release -install: build-release +conf: mkdir -p ${HOME}/.config/mal cp -f core/core.mal ${HOME}/.config/mal/ + +install: build-release conf sudo cp target/release/rust-mal /usr/local/bin/mal sudo chown ${USER} /usr/local/bin/mal diff --git a/src/core.rs b/src/core.rs index fa8a286..770c383 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,7 +1,8 @@ use std::{cell::RefCell, env, rc::Rc}; use crate::env::{ - any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_car, mal_cdr, mal_exit, Env, + any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_car, mal_cdr, mal_exit, + mal_map, Env, }; // This is the first time I implement a macro, and I'm copying it @@ -80,6 +81,7 @@ pub fn ns_init() -> Env { "atom" => Fun(|a| Ok(Atom(Rc::new(RefCell::new(car(a).unwrap_or_default().clone())))), "Return an atom pointing to the given arg"), "deref" => Fun(|a| if_atom!(car(a)?), "Return the content of the atom argumet"), "reset!" => Fun(reset_bang, "Change the value of the Atom (frist argument) to the second argument"), + "map" => Fun(mal_map, "Apply the first argument to all the elements of the second arguments"), "env" => Fun(|a| match env::var(car(a)?.if_string()?) { Ok(s) => Ok(Str(s.into())), _ => Ok(Nil), diff --git a/src/env.rs b/src/env.rs index 04d5839..5ae74a3 100644 --- a/src/env.rs +++ b/src/env.rs @@ -189,6 +189,38 @@ pub fn car_cdr(list: &[MalType]) -> Result<(&MalType, &[MalType]), MalErr> { Ok((car(list)?, cdr(list))) } +pub fn mal_map(args: &[MalType]) -> MalRet { + let mut ret = Vec::new(); + let (lambda, list) = car_cdr(args)?; + let list = car(list)?.if_list()?; + match lambda { + M::Fun(func, _) => { + for el in list { + ret.push(func(&[el.clone()])?); + } + } + M::MalFun { + params, ast, env, .. + } => { + for el in list { + let inner_env = env_binds(env.clone(), params, &[el.clone()])?; + match ast.as_ref() { + M::List(ops) => { + let mut last = MalType::Nil; + for x in &ops[0..ops.len()] { + last = eval(x, inner_env.clone())?; + } + ret.push(last) + } + _ => scream!(), + } + } + } + _ => scream!(), + }; + Ok(MalType::List(ret.into())) +} + fn first(list: &[MalType]) -> &[MalType] { if list.len() > 1 { &list[..list.len() - 1] diff --git a/src/mal_tests/mod.rs b/src/mal_tests/mod.rs index cc6c746..c1db999 100644 --- a/src/mal_tests/mod.rs +++ b/src/mal_tests/mod.rs @@ -60,4 +60,9 @@ mod functional { fn car_cdr() { test!("car-cdr") } + + #[test] + fn map() { + test!("map") + } } diff --git a/tests/map.mal b/tests/map.mal new file mode 100644 index 0000000..8d035fc --- /dev/null +++ b/tests/map.mal @@ -0,0 +1,5 @@ +; builtin map +(assert-eq '((1) (2) (3)) (map list '(1 2 3))) + +; lambda map +(assert-eq '(2 3 4) (map (fn* [x] (+ x 1)) '(1 2 3)))