Implemented fn* special form

- Don't really like how it is behaving: turning expressions into a list
of expression and evaluating them separately

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-07-27 16:50:45 +02:00
parent fc7328167a
commit 24a42d5628
4 changed files with 64 additions and 23 deletions

View File

@ -1,5 +1,5 @@
use crate::types::MalType::*;
use crate::types::{MalArgs, MalMap, MalRet, MalType};
use crate::types::{MalMap, MalRet, MalType};
// This is the first time I implement a macro, and I'm copying it
// so I will comment this a LOT
@ -30,7 +30,7 @@ macro_rules! env_init {
};
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Env {
data: MalMap,
outer: Option<Box<Env>>,
@ -44,24 +44,6 @@ impl Env {
}
}
pub fn init(&mut self, binds: MalArgs, exprs: MalArgs) -> Result<&mut Self, String> {
if binds.len() != exprs.len() {
return Err("Env init with unmatched length".to_string());
} // TODO: May be possible to leave this be and not set additional elements at all
for (bind, expr) in binds.iter().zip(exprs.iter()) {
match bind {
Sym(sym) => self.set(sym, expr),
_ => {
return Err(format!(
"Initializing environment: {:?} is not a symbol",
bind
))
}
}
}
Ok(self)
}
pub fn set(&mut self, sym: &str, val: &MalType) {
self.data.insert(sym.to_string(), val.clone());
}
@ -77,6 +59,30 @@ impl Env {
}
}
pub fn env_binds(outer: &Env, binds: &MalType, exprs: &[MalType]) -> Result<Env, String> {
let mut env = Env::new(Some(Box::new(outer.clone())));
match binds {
List(binds) => {
if binds.len() != exprs.len() {
return Err("Env init with unmatched length".to_string());
} // TODO: May be possible to leave this be and not set additional elements at all
for (bind, expr) in binds.iter().zip(exprs.iter()) {
match bind {
Sym(sym) => env.set(sym, expr),
_ => {
return Err(format!(
"Initializing environment: {:?} is not a symbol",
bind
))
}
}
}
Ok(env)
}
_ => Err("init: first argument must be a list".to_string()),
}
}
use crate::types::MalType::{Fun, Str};
use crate::types::{arithmetic_op, comparison_op};
use std::process::exit;

View File

@ -1,3 +1,4 @@
use crate::env::env_binds;
use crate::env::Env;
use crate::types::car_cdr;
use crate::types::MalType::*;
@ -6,6 +7,19 @@ use crate::types::{MalArgs, MalMap, MalRet, MalType};
fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
match func {
Fun(func) => func(args),
MalFun {
eval,
params,
ast,
env,
} => {
let mut inner_env = env_binds(env, &**params, args)?;
let mut ret = Ok(Nil);
for el in ast.iter() {
ret = eval(el, &mut inner_env);
}
ret
}
_ => Err(format!("{:?} is not a function", func)),
}
}
@ -93,6 +107,19 @@ fn if_form(list: &[MalType], env: &mut Env) -> MalRet {
}
}
fn fn_star_form(list: &[MalType], env: &Env) -> MalRet {
if list.is_empty() {
return Err("fn* form: specify lambda arguments".to_string());
}
let (binds, exprs) = car_cdr(list);
Ok(MalFun {
eval: eval,
params: Box::new(binds.clone()),
ast: Box::new(exprs.to_vec()),
env: env.clone(),
})
}
/// Intermediate function to discern special forms from defined symbols
fn apply(list: &MalArgs, env: &mut Env) -> MalRet {
let (car, cdr) = car_cdr(list);
@ -101,9 +128,9 @@ fn apply(list: &MalArgs, env: &mut Env) -> MalRet {
Sym(sym) if sym == "let*" => let_star_form(cdr, 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),
// Filter out special forms
Sym(_) => eval_func(&eval_ast(&List(list.to_vec()), env)?),
_ => Err(format!("{:?} is not a symbol", car)),
_ => eval_func(&eval_ast(&List(list.to_vec()), env)?),
}
}

View File

@ -39,5 +39,6 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
.join(" ")
),
Fun(func) => format!("{:?}", func),
MalFun { .. } => "#<function>".to_string(),
}
}

View File

@ -1,3 +1,4 @@
use crate::env::Env;
use std::collections::HashMap;
// All Mal types should inherit from this
@ -6,7 +7,13 @@ pub enum MalType {
List(MalArgs),
Vector(MalArgs),
Map(MalMap),
Fun(fn(&[MalType]) -> MalRet),
Fun(fn(&[MalType]) -> MalRet), // Used for base functions, implemented using the underlying language (rust)
MalFun {
eval: fn(ast: &MalType, env: &mut Env) -> MalRet,
params: Box<MalType>,
ast: Box<MalArgs>,
env: Env,
}, // Used for functions defined within mal
Sym(String),
Key(String),
Str(String),