mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
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:
46
src/env.rs
46
src/env.rs
@ -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;
|
||||
|
||||
31
src/eval.rs
31
src/eval.rs
@ -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)?),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,5 +39,6 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
||||
.join(" ")
|
||||
),
|
||||
Fun(func) => format!("{:?}", func),
|
||||
MalFun { .. } => "#<function>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
Reference in New Issue
Block a user