Defining map and filter with lisp

This commit is contained in:
teo3300
2024-06-19 09:11:34 +09:00
parent 3b896fa6c4
commit 35716afee9
6 changed files with 60 additions and 36 deletions

View File

@ -36,6 +36,12 @@
(>= b a)))
; Other functions in core.rs
(def! int? (fn* [a]
(= (type a) :int)))
(def! sym? (fn* [a]
(= (type a) :symbol)))
(def! list? (fn* [a]
(= (type a) :list)))
@ -109,13 +115,11 @@
(def! help (fn* [symbol] "==SPECIAL FORM=="
"symbol: Sym"
"display an helper f or the specified symbol"
"#alias: h"
"#returns: NIL"))
(def! find (fn* [substring...] "==SPECIAL FORM=="
"print all the known symbols partially matching <substring> in"
"the current environment"
"#alias f"
"#returns: NIL"))
(def! quote (fn* [statement] "==SPECIAL FORM=="
@ -137,3 +141,33 @@
"; (help <symbol>) : print information about a symbol\n"
";\n"
"; enjoy ^.^\n"))
(def! reverse (fn* [x]
"Reverses order of elements in the arg"
(def! reverse-r (fn* [x t]
(if (empty? x)
t
(reverse-r (cdr x) (cons (car x) t)))))
(reverse-r x '())))
(def! map (fn* [f l]
"Apply function f to all elements of l"
(def! l (reverse l))
(def! map-r (fn* [l p]
(if (empty? l)
p
(map-r (cdr l) (cons (f (car l)) p)))))
(map-r l '())))
(def! filter (fn* [f l]
"Remove all elements that don't satisfy f"
(def! l (reverse l))
(def! filter-r (fn* [l p]
(if (empty? l)
p
(do
(def! t (car l))
(if (f t)
(filter-r (cdr l) (cons (car l) p))
(filter-r (cdr l) p))))))
(filter-r l '())))

View File

@ -1,8 +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,
mal_map, Env,
any_zero, arithmetic_op, car, comparison_op, env_new, env_set, mal_car, mal_cdr, mal_cons,
mal_exit, Env,
};
// This is the first time I implement a macro, and I'm copying it
@ -81,7 +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"),
"cons" => Fun(mal_cons, "Push to front if second element is a list"),
"env" => Fun(|a| match env::var(car(a)?.if_string()?) {
Ok(s) => Ok(Str(s.into())),
_ => Ok(Nil),

View File

@ -189,36 +189,18 @@ 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()])?);
}
// TODO: fix these chonky functions
pub fn mal_cons(args: &[MalType]) -> MalRet {
match args.len() {
2 => {
let mut car = vec![args[0].clone()];
let cdr = args[1].if_list()?;
car.extend_from_slice(cdr);
Ok(M::List(car.into()))
}
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()))
_ => Err(MalErr::unrecoverable("cons: requires 2 arguments")),
}
}
fn first(list: &[MalType]) -> &[MalType] {

View File

@ -21,7 +21,6 @@ forms!(NAME_DEF : "def!",
NAME_FN : "fn*",
NAME_FN_ALT : "λ",
NAME_HELP : "help",
NAME_HELP_ALT: "h",
NAME_FIND : "find",
NAME_QUOTE : "quote",
NAME_OK : "ok?",
@ -189,7 +188,7 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
NAME_FN | NAME_FN_ALT /* :) */ => {
return fn_star_form(args, env.clone())
}
NAME_HELP | NAME_HELP_ALT => return help_form(args, env.clone()),
NAME_HELP => return help_form(args, env.clone()),
NAME_FIND => return find_form(args, env.clone()),
// Oh God, what have I done
NAME_QUOTE => return Ok(car(args)?.clone()),

View File

@ -65,4 +65,9 @@ mod functional {
fn map() {
test!("map")
}
#[test]
fn fil() {
test!("fil")
}
}

4
tests/fil.mal Normal file
View File

@ -0,0 +1,4 @@
; filter with builtin function
(assert-eq (list '(1) '(2) '(3)) (filter car (list '(1) '() '(2) '() '(3))))
; filter with lambda function
(assert-eq (list 1 2 3) (filter int? (list 1 "string" 2 'symbol 3 :label)))