mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Fixing function call
Previously only returned last element of ast, without evaluating other elements recursive env_get is now iterative Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
44
src/env.rs
44
src/env.rs
@ -1,3 +1,4 @@
|
||||
use crate::eval::eval;
|
||||
use crate::types::MalErr;
|
||||
use crate::types::{MalMap, MalRet, MalType};
|
||||
use std::cell::RefCell;
|
||||
@ -15,7 +16,7 @@ impl EnvType {
|
||||
.data
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(k, _)| k.clone())
|
||||
.map(|(k, _)| k.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
keys.sort_unstable();
|
||||
keys
|
||||
@ -26,27 +27,31 @@ pub type Env = Rc<EnvType>;
|
||||
// Following rust implementation, using shorthand to always pas Reference count
|
||||
|
||||
pub fn env_new(outer: Option<Env>) -> Env {
|
||||
Rc::new(EnvType {
|
||||
Env::new(EnvType {
|
||||
data: RefCell::new(MalMap::new()),
|
||||
outer,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn env_set(env: &Env, sym: &str, val: &MalType) -> MalType {
|
||||
pub fn env_set(env: &Env, sym: &str, val: &MalType) {
|
||||
env.data.borrow_mut().insert(sym.to_string(), val.clone());
|
||||
val.clone()
|
||||
}
|
||||
|
||||
pub fn env_get(env: &Env, sym: &str) -> MalRet {
|
||||
match env.data.borrow().get(sym) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => match env.outer.clone() {
|
||||
Some(outer) => env_get(&outer, sym),
|
||||
None => Err(MalErr::unrecoverable(
|
||||
format!("symbol {:?} not defined", sym).as_str(),
|
||||
)),
|
||||
},
|
||||
let mut iter_env = env;
|
||||
loop {
|
||||
if let Some(val) = iter_env.data.borrow().get(sym) {
|
||||
return Ok(val.clone());
|
||||
}
|
||||
if let Some(outer) = &iter_env.outer {
|
||||
iter_env = &outer;
|
||||
continue;
|
||||
}
|
||||
return Err(MalErr::unrecoverable(
|
||||
format!("symbol {:?} not defined", sym).as_str(),
|
||||
));
|
||||
}
|
||||
// Recursive was prettier, but we hate recursion
|
||||
}
|
||||
|
||||
pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env, MalErr> {
|
||||
@ -93,10 +98,15 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet {
|
||||
// It's fine to clone the environment here
|
||||
// since this is when the function is actually called
|
||||
match ast.as_ref() {
|
||||
M::List(list) => Ok(CallFunc::MalFun(
|
||||
list.last().unwrap_or(&Nil).clone(),
|
||||
inner_env,
|
||||
)),
|
||||
M::List(list) => {
|
||||
for x in &list[0..list.len() - 1] {
|
||||
eval(x, inner_env.clone())?;
|
||||
}
|
||||
Ok(CallFunc::MalFun(
|
||||
list.last().unwrap_or(&Nil).clone(),
|
||||
inner_env,
|
||||
))
|
||||
}
|
||||
_ => scream!(),
|
||||
}
|
||||
}
|
||||
@ -176,7 +186,7 @@ fn first(list: &[MalType]) -> &[MalType] {
|
||||
// FIXME: Treat as result for now, change later
|
||||
fn last(list: &[MalType]) -> Result<&MalType, MalErr> {
|
||||
match list.len() {
|
||||
0 => Err(MalErr::unrecoverable("Mi sono cacato le mutande")),
|
||||
0 => Ok(&MalType::Nil),
|
||||
_ => Ok(&list[list.len() - 1]),
|
||||
}
|
||||
}
|
||||
|
||||
261
src/eval.rs
261
src/eval.rs
@ -42,7 +42,9 @@ fn def_bang_form(list: &[MalType], env: Env) -> MalRet {
|
||||
}
|
||||
let (car, _) = car_cdr(list)?;
|
||||
let sym = car.if_symbol()?;
|
||||
Ok(env_set(&env, sym, &eval(&list[1], env.clone())?))
|
||||
let val = &eval(&list[1], env.clone())?;
|
||||
env_set(&env, sym, val);
|
||||
Ok(val.clone())
|
||||
}
|
||||
|
||||
/// let* special form:
|
||||
@ -187,17 +189,6 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch ast evaluation depending on it being a list or not and return the
|
||||
/// result of the evaluation, this function calls "eval_ast" to recursively
|
||||
/// evaluate asts
|
||||
/*pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
match &ast {
|
||||
M::List(list) if list.is_empty() => Ok(ast.clone()),
|
||||
M::List(list) if !list.is_empty() => apply(list, env),
|
||||
_ => eval_ast(ast, env),
|
||||
}
|
||||
}*/
|
||||
|
||||
/// Separately evaluate all elements in a collection (list or vector)
|
||||
fn eval_collection(list: &MalArgs, env: Env) -> Result<MalArgs, MalErr> {
|
||||
let mut ret = Vec::new();
|
||||
@ -227,248 +218,4 @@ fn eval_ast(ast: &MalType, env: Env) -> MalRet {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::env::Env;
|
||||
|
||||
macro_rules! load2 {
|
||||
($input:expr) => {{
|
||||
use crate::reader::{read_str, Reader};
|
||||
|
||||
let r = Reader::new();
|
||||
r.push($input);
|
||||
&match read_str(&r) {
|
||||
Ok(v) => match v {
|
||||
MalType::List(v) => v,
|
||||
_ => panic!("Not a list"),
|
||||
},
|
||||
_ => panic!("Bad command"),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! load {
|
||||
($input:expr) => {{
|
||||
use crate::env::cdr;
|
||||
cdr(load2!($input))
|
||||
}};
|
||||
}
|
||||
|
||||
/*macro_rules! load_f {
|
||||
($input:expr, $env:expr) => {{
|
||||
use crate::reader::{read_str, Reader};
|
||||
use std::rc::Rc;
|
||||
|
||||
let r = Reader::new();
|
||||
r.push($input);
|
||||
let args = match read_str(&r) {
|
||||
Ok(v) => match v {
|
||||
MalType::List(v) => v,
|
||||
_ => panic!("Bad command"),
|
||||
},
|
||||
_ => panic!("Bad command"),
|
||||
};
|
||||
&MalType::List(Rc::new(if args.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let f_str = match &args[0] {
|
||||
MalType::Sym(s) => s.as_str(),
|
||||
_ => panic!("Can't solve function"),
|
||||
};
|
||||
let f = match env_get(&$env.clone(), f_str) {
|
||||
Ok(v) => v,
|
||||
_ => panic!("No such function in env"),
|
||||
};
|
||||
[&[f], &args[1..]].concat()
|
||||
}))
|
||||
}};
|
||||
}*/
|
||||
|
||||
fn _env_empty() -> Env {
|
||||
use crate::env::env_new;
|
||||
env_new(None)
|
||||
}
|
||||
|
||||
mod forms {
|
||||
use crate::env::env_get;
|
||||
use crate::eval::tests::_env_empty;
|
||||
use crate::eval::{def_bang_form, fn_star_form, if_form, let_star_form};
|
||||
use crate::types::MalType;
|
||||
|
||||
#[test]
|
||||
fn def_bang() {
|
||||
let env = _env_empty();
|
||||
|
||||
assert!(matches!( //x empty
|
||||
def_bang_form(
|
||||
load!("(def!) ; empty"),
|
||||
env.clone()),
|
||||
Err(e)
|
||||
if !e.is_recoverable()));
|
||||
assert!(matches!( //x 1 arg
|
||||
def_bang_form(
|
||||
load!("(def! a) ; 1 arg"),
|
||||
env.clone()),
|
||||
Err(e)
|
||||
if !e.is_recoverable()));
|
||||
assert!(matches!( //x 3 args
|
||||
def_bang_form(
|
||||
load!("(def! a 1 2) ; 3 args"),
|
||||
env.clone()),
|
||||
Err(e)
|
||||
if !e.is_recoverable()));
|
||||
|
||||
assert!(matches!(
|
||||
//v 2 args
|
||||
def_bang_form(load!("(def! a 1) ; correct a = 1"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(env_get(&env, "a"), Ok(MalType::Int(1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_star() {
|
||||
let env = _env_empty();
|
||||
assert!(
|
||||
matches!(let_star_form(load!("(let*)"), env.clone()), Err(e) if !e.is_recoverable())
|
||||
);
|
||||
assert!(
|
||||
matches!(let_star_form(load!("(let* 1)"), env.clone()), Err(e) if !e.is_recoverable())
|
||||
);
|
||||
assert!(
|
||||
matches!(let_star_form(load!("(let* [a])"), env.clone()), Err(e) if !e.is_recoverable())
|
||||
); /*
|
||||
assert!(matches!(
|
||||
let_star_form(load!("(let* ())"), env.clone()),
|
||||
Ok(MalType::Nil)
|
||||
));
|
||||
assert!(matches!(
|
||||
let_star_form(load!("(let* (a 1))"), env.clone()),
|
||||
Ok(MalType::Nil)
|
||||
));
|
||||
assert!(matches!(env_get(&env.clone(), "a"), Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
let_star_form(load!("(let* (a 1 b 2) a b)"), env.clone()),
|
||||
Ok(MalType::Int(2))
|
||||
));
|
||||
assert!(matches!(env_get(&env.clone(), "a"), Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(env_get(&env.clone(), "b"), Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
let_star_form(load!("(let* (a 1 b 2) (def! c 1) a b)"), env.clone()),
|
||||
Ok(MalType::Int(2))
|
||||
));*/
|
||||
assert!(matches!(env_get(&env.clone(), "c"), Err(e) if !e.is_recoverable()));
|
||||
}
|
||||
|
||||
/*#[test]
|
||||
fn _do_form() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
do_form(load!("(do)"), env.clone()),
|
||||
Ok(MalType::Nil)
|
||||
));
|
||||
assert!(matches!(
|
||||
do_form(load!("(do true)"), env.clone()),
|
||||
Ok(MalType::Bool(true))
|
||||
));
|
||||
assert!(matches!(
|
||||
do_form(load!("(do (def! a 1) 2)"), env.clone()),
|
||||
Ok(MalType::Int(2))
|
||||
));
|
||||
assert!(matches!(env_get(&env.clone(), "a"), Ok(MalType::Int(1))));
|
||||
}*/
|
||||
|
||||
#[test]
|
||||
fn _if_form() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
if_form(load!("(if)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if 1)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if 1 2 3 4)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if nil 1)"), env.clone()),
|
||||
Ok(MalType::Nil)
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if nil 1 2)"), env.clone()),
|
||||
Ok(MalType::Int(2))
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if true 1)"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(
|
||||
if_form(load!("(if true 1 2)"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_star() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn* [a b] 1 2)"), env.clone()),
|
||||
Ok(MalType::MalFun {params, ast, .. })
|
||||
if matches!((*params).clone(), MalType::Vector(v)
|
||||
if matches!(&v[0], MalType::Sym(v) if v == "a")
|
||||
&& matches!(&v[1], MalType::Sym(v) if v == "b")
|
||||
&& matches!((*ast).clone(), MalType::List(v)
|
||||
if matches!(&v[0], MalType::Int(1))
|
||||
&& matches!(&v[1], MalType::Int(2))))));
|
||||
// We trust the fact that the env does not do silly stuff
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn*)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
fn_star_form(load!("(fn* 1)"), env.clone()),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn _eval_func() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
def_bang_form(load!("(def! or (fn* (a b) (if a a b)))"), env.clone()),
|
||||
Ok(_)
|
||||
));
|
||||
assert!(matches!(
|
||||
eval_func(&MalType::Int(1)),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("()", env.clone())),
|
||||
Err(e) if !e.is_recoverable()));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or nil nil)", env.clone())),
|
||||
Ok(v) if matches!(v, MalType::Nil)));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or 1 nil)", env.clone())),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(
|
||||
eval_func(load_f!("(or nil 1)", env.clone())),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
}*/
|
||||
|
||||
/*#[test]
|
||||
fn _apply() {
|
||||
let env = _env_empty();
|
||||
assert!(matches!(
|
||||
apply(load2!("(def! a 1)"), env.clone()),
|
||||
Ok(MalType::Int(1))
|
||||
));
|
||||
assert!(matches!(env_get(&env, "a"), Ok(MalType::Int(1))));
|
||||
}*/
|
||||
}
|
||||
}
|
||||
// all tests moved to mal
|
||||
|
||||
@ -28,7 +28,7 @@ mod functional {
|
||||
|
||||
#[test]
|
||||
fn builtin_equals() {
|
||||
test!("equals");
|
||||
test!("equals")
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -38,6 +38,11 @@ mod functional {
|
||||
|
||||
#[test]
|
||||
fn fibonacci() {
|
||||
test!("fibonacci");
|
||||
test!("fibonacci")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forms() {
|
||||
test!("forms")
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,10 +311,10 @@ mod tests {
|
||||
MalMap::new()
|
||||
}
|
||||
};
|
||||
assert!(matches!(t.get("n"), Some(x) if matches!(x.clone(), M::Nil)));
|
||||
assert!(matches!(t.get("t"), Some(x) if matches!(x.clone(), M::Bool(v) if v)));
|
||||
assert!(matches!(t.get("i"), Some(x) if matches!(x.clone(), M::Int(v) if v == 1)));
|
||||
assert!(matches!(t.get("s"), Some(x) if matches!(x.clone(), M::Str(v) if v == "str")));
|
||||
assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(x.clone(), M::Key(v) if v == "ʞ:sym")));
|
||||
assert!(matches!(t.get("n"), Some(x) if matches!(&x, M::Nil)));
|
||||
assert!(matches!(t.get("t"), Some(x) if matches!(&x, M::Bool(v) if *v)));
|
||||
assert!(matches!(t.get("i"), Some(x) if matches!(&x, M::Int(v) if *v == 1)));
|
||||
assert!(matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if v == "str")));
|
||||
assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(&x, M::Key(v) if v == "ʞ:sym")));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user