mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
66
src/eval.rs
66
src/eval.rs
@ -4,7 +4,7 @@ use crate::core::{call_func, car_cdr};
|
||||
use crate::env::Env;
|
||||
use crate::env::{env_get, env_new, env_set};
|
||||
use crate::printer::prt;
|
||||
use crate::types::MalType::*;
|
||||
use crate::types::MalType as M;
|
||||
use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType};
|
||||
|
||||
/// Resolve the first element of the list as the function name and call it
|
||||
@ -45,19 +45,20 @@ fn let_star_form(list: &[MalType], env: Env) -> MalRet {
|
||||
// change the inner environment
|
||||
let (car, cdr) = car_cdr(list)?;
|
||||
let list = car.if_list()?;
|
||||
if list.len() % 2 == 0 {
|
||||
// TODO: Find a way to avoid index looping that is ugly
|
||||
if list.len() % 2 != 0 {
|
||||
Err(MalErr::unrecoverable(
|
||||
"let* form, number of arguments must be even",
|
||||
))
|
||||
} else {
|
||||
// TO-DO: Find a way to avoid index looping that is ugly
|
||||
for i in (0..list.len()).step_by(2) {
|
||||
def_bang_form(&list[i..=i + 1], inner_env.clone())?;
|
||||
}
|
||||
if cdr.is_empty() {
|
||||
// TODO: check if it exists a better way to do this
|
||||
Ok(Nil)
|
||||
} else {
|
||||
eval(&cdr[0], inner_env)
|
||||
let mut last = M::Nil;
|
||||
for expr in cdr {
|
||||
last = eval(expr, inner_env.clone())?;
|
||||
}
|
||||
} else {
|
||||
Err(MalErr::unrecoverable("let* form, number of arguments must be even"))
|
||||
Ok(last)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,8 +69,9 @@ fn do_form(list: &[MalType], env: Env) -> MalRet {
|
||||
if list.is_empty() {
|
||||
return Err(MalErr::unrecoverable("do form: provide a list as argument"));
|
||||
}
|
||||
// TODO: this may be different
|
||||
match eval_ast(&list[0], env)? {
|
||||
List(list) => Ok(list.last().unwrap_or(&Nil).clone()),
|
||||
M::List(list) => Ok(list.last().unwrap_or(&M::Nil).clone()),
|
||||
_ => Err(MalErr::unrecoverable("do form: argument must be a list")),
|
||||
}
|
||||
}
|
||||
@ -82,8 +84,8 @@ fn if_form(list: &[MalType], env: Env) -> MalRet {
|
||||
}
|
||||
let (cond, branches) = car_cdr(list)?;
|
||||
match eval(cond, env.clone())? {
|
||||
Nil | Bool(false) => match branches.len() {
|
||||
1 => Ok(Nil),
|
||||
M::Nil | M::Bool(false) => match branches.len() {
|
||||
1 => Ok(M::Nil),
|
||||
_ => eval(&branches[1], env),
|
||||
},
|
||||
_ => eval(&branches[0], env),
|
||||
@ -92,10 +94,10 @@ fn if_form(list: &[MalType], env: Env) -> MalRet {
|
||||
|
||||
fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
|
||||
let (binds, exprs) = car_cdr(list)?;
|
||||
Ok(MalFun {
|
||||
Ok(M::MalFun {
|
||||
eval: eval_ast,
|
||||
params: Rc::new(binds.clone()),
|
||||
ast: Rc::new(List(exprs.to_vec())),
|
||||
ast: Rc::new(M::List(exprs.to_vec())),
|
||||
env,
|
||||
})
|
||||
}
|
||||
@ -106,25 +108,25 @@ pub fn help_form(list: &[MalType], env: Env) -> MalRet {
|
||||
let (sym, _) = car_cdr(list)?;
|
||||
let sym_str = sym.if_symbol()?;
|
||||
match eval(sym, env.clone())? {
|
||||
Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc),
|
||||
MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
||||
M::Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc),
|
||||
M::MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
||||
_ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)),
|
||||
}
|
||||
Ok(Bool(true))
|
||||
Ok(M::Bool(true))
|
||||
}
|
||||
|
||||
/// Intermediate function to discern special forms from defined symbols
|
||||
fn apply(list: &MalArgs, env: Env) -> MalRet {
|
||||
let (car, cdr) = car_cdr(list)?;
|
||||
match car {
|
||||
Sym(sym) if sym == "def!" => def_bang_form(cdr, env), // Set for env
|
||||
Sym(sym) if sym == "let*" => let_star_form(cdr, env), // Clone the env
|
||||
Sym(sym) if sym == "do" => do_form(cdr, env),
|
||||
Sym(sym) if sym == "if" => if_form(cdr, env),
|
||||
Sym(sym) if sym == "fn*" => fn_star_form(cdr, env),
|
||||
Sym(sym) if sym == "help" => help_form(cdr, env),
|
||||
M::Sym(sym) if sym == "def!" => def_bang_form(cdr, env), // Set for env
|
||||
M::Sym(sym) if sym == "let*" => let_star_form(cdr, env), // Clone the env
|
||||
M::Sym(sym) if sym == "do" => do_form(cdr, env),
|
||||
M::Sym(sym) if sym == "if" => if_form(cdr, env),
|
||||
M::Sym(sym) if sym == "fn*" => fn_star_form(cdr, env),
|
||||
M::Sym(sym) if sym == "help" => help_form(cdr, env),
|
||||
// Filter out special forms
|
||||
_ => eval_func(&eval_ast(&List(list.to_vec()), env)?),
|
||||
_ => eval_func(&eval_ast(&M::List(list.to_vec()), env)?),
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,8 +135,8 @@ fn apply(list: &MalArgs, env: Env) -> MalRet {
|
||||
/// evaluate asts
|
||||
pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
match &ast {
|
||||
List(list) if list.is_empty() => Ok(ast.clone()),
|
||||
List(list) if !list.is_empty() => apply(list, env),
|
||||
M::List(list) if list.is_empty() => Ok(ast.clone()),
|
||||
M::List(list) if !list.is_empty() => apply(list, env),
|
||||
_ => eval_ast(ast, env),
|
||||
}
|
||||
}
|
||||
@ -154,16 +156,16 @@ fn eval_map(map: &MalMap, env: Env) -> MalRet {
|
||||
for (k, v) in map {
|
||||
ret.insert(k.to_string(), eval(v, env.clone())?);
|
||||
}
|
||||
Ok(Map(ret))
|
||||
Ok(M::Map(ret))
|
||||
}
|
||||
|
||||
/// Eval the provided ast
|
||||
fn eval_ast(ast: &MalType, env: Env) -> MalRet {
|
||||
match ast {
|
||||
Sym(sym) => env_get(&env, sym),
|
||||
List(list) => Ok(List(eval_collection(list, env)?)),
|
||||
Vector(vec) => Ok(Vector(eval_collection(vec, env)?)),
|
||||
Map(map) => eval_map(map, env),
|
||||
M::Sym(sym) => env_get(&env, sym),
|
||||
M::List(list) => Ok(M::List(eval_collection(list, env)?)),
|
||||
M::Vector(vec) => Ok(M::Vector(eval_collection(vec, env)?)),
|
||||
M::Map(map) => eval_map(map, env),
|
||||
_ => Ok(ast.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,23 +2,23 @@ use std::rc::Rc;
|
||||
|
||||
use crate::types::escape_str;
|
||||
use crate::types::MalType;
|
||||
use crate::types::MalType::*;
|
||||
use crate::types::MalType as M;
|
||||
|
||||
pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
match ast {
|
||||
Nil => "nil".to_string(),
|
||||
Sym(sym) => sym.to_string(),
|
||||
Key(sym) => sym[2..].to_string(),
|
||||
Int(val) => val.to_string(),
|
||||
Bool(val) => val.to_string(),
|
||||
Str(str) => {
|
||||
M::Nil => "nil".to_string(),
|
||||
M::Sym(sym) => sym.to_string(),
|
||||
M::Key(sym) => sym[2..].to_string(),
|
||||
M::Int(val) => val.to_string(),
|
||||
M::Bool(val) => val.to_string(),
|
||||
M::Str(str) => {
|
||||
if print_readably {
|
||||
escape_str(str)
|
||||
} else {
|
||||
str.to_string()
|
||||
}
|
||||
}
|
||||
List(el) => format!(
|
||||
M::List(el) => format!(
|
||||
"({})",
|
||||
el.iter()
|
||||
.map(|e| pr_str(e, print_readably))
|
||||
@ -26,22 +26,22 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
.join(" ")
|
||||
),
|
||||
// This is truly horrible
|
||||
Vector(el) => format!(
|
||||
M::Vector(el) => format!(
|
||||
"[{}]",
|
||||
el.iter()
|
||||
.map(|e| pr_str(e, print_readably))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
),
|
||||
Map(el) => format!(
|
||||
M::Map(el) => format!(
|
||||
"{{{}}}",
|
||||
el.iter()
|
||||
.map(|sub| [sub.0.to_string(), pr_str(sub.1, print_readably)].join(" "))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
),
|
||||
Fun(..) => "#<builtin>".to_string(),
|
||||
MalFun { .. } => "#<function>".to_string(),
|
||||
M::Fun(..) => "#<builtin>".to_string(),
|
||||
M::MalFun { .. } => "#<function>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ pub fn prt(ast: &MalType) -> String {
|
||||
pub fn print_malfun(sym: &str, params: Rc<MalType>, ast: Rc<MalType>) {
|
||||
println!("{}\t[function]: {}", sym, prt(¶ms));
|
||||
match ast.as_ref() {
|
||||
List(list) => {
|
||||
M::List(list) => {
|
||||
for el in list {
|
||||
println!("; {}", prt(el))
|
||||
}
|
||||
|
||||
@ -36,14 +36,18 @@ impl MalType {
|
||||
pub fn if_list(&self) -> Result<&[MalType], MalErr> {
|
||||
match self {
|
||||
Self::List(list) => Ok(list),
|
||||
_ => Err(MalErr::unrecoverable(format!("{:?} is not a list", prt(self)).as_str()))
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!("{:?} is not a list", prt(self)).as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_symbol(&self) -> Result<&str, MalErr> {
|
||||
match self {
|
||||
Self::Sym(sym) => Ok(sym),
|
||||
_ => Err(MalErr::unrecoverable(format!("{:?} is not a symbol", prt(self)).as_str()))
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!("{:?} is not a symbol", prt(self)).as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user