diff --git a/src/env.rs b/src/env.rs index c5a0ae2..5a99593 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,5 +1,34 @@ 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 +macro_rules! env_init { + ($outer:expr) => { + // match any istance with no args + { + // the macro prevent the macro from disrupting the external code + // this is the block of code that will substitute the macro + Env::new($outer) + // returns an empty map + } + }; + ($outer:expr, $($key:expr => $val:expr),*) => { + // Only if previous statements did not match, + // note that the expression with fat arrow is arbitrary, + // could have been slim arrow, comma or any other + // recognizable structure + { + // create an empty map + let mut map = env_init!($outer); + $( // Do this for all elements of the arguments list + map.set($key, &$val); + )* + // return the new map + map + } + }; +} + pub struct Env { data: MalMap, outer: Option>, @@ -27,3 +56,18 @@ impl Env { } } } + +use crate::types::int_op; +use crate::types::MalType::{Fun, Str}; +use std::process::exit; + +pub fn env_init() -> Env { + env_init!(None, + "test" => Fun(|_| Ok(Str("This is a test function".to_string()))), + "quit" => Fun(|_| {println!("Bye!"); exit(0)}), + "+" => Fun(|a| int_op(0, |a, b| a + b, a)), + "-" => Fun(|a| int_op(0, |a, b| a - b, a)), + "*" => Fun(|a| int_op(1, |a, b| a * b, a)), + "/" => Fun(|a| int_op(1, |a, b| a / b, a)) + ) +} diff --git a/src/eval.rs b/src/eval.rs index 4f608aa..ec394b6 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -12,33 +12,66 @@ fn call_func(func: &MalType, args: &[MalType]) -> MalRet { fn eval_func(list: &MalType) -> MalRet { match list { List(list) => { - let func = &list[0]; - let args = if list.len() > 1 { - &list[1..] - } else { - &list[0..0] - }; + let (func, args) = car_cdr(list); call_func(func, args) } - _ => Err("YOU SHOULD NOT BE HERE".to_string()), + _ => todo!("Yep! I hate it"), } } -pub fn eval(ast: &MalType, env: &Env) -> MalRet { - match &ast { - List(list) => { - if list.is_empty() { - // Ok(Nil) // Should be the normal behavior - Ok(ast.clone()) - } else { - eval_func(&eval_ast(ast, env)?) +fn car_cdr(list: &[MalType]) -> (&MalType, &[MalType]) { + ( + &list[0], + if list.len() > 1 { + &list[1..] + } else { + &list[0..0] + }, + ) +} + +fn def_bang(list: &[MalType], env: &mut Env) -> MalRet { + match list.len() { + 2 => match &list[0] { + Sym(sym) => { + let cdr = eval(&list[1], env)?; + env.set(sym.as_str(), &cdr); + Ok(cdr) } - } + _ => Err(format!( + "Assigning {:?} to {:?}, which is not a symbol", + &list[1], &list[0] + )), + }, + _ => Err("def! macro has too many arguments, may be less strict in future".to_string()), + } +} + +fn apply(list: &MalArgs, env: &mut Env) -> MalRet { + let (car, cdr) = car_cdr(list); + match car { + Sym(sym) => match sym.as_str() { + "def!" => def_bang(cdr, env), // already remove the def + "let*" => todo!("set new environment and add definitions to it"), + // default if no match + _ => eval_func(&eval_ast(&List(list.to_vec()), env)?), + // Hate this line, should not need to create a whole new vector + }, + _ => Err("First element not a symbol".to_string()), + } +} + +pub fn eval(ast: &MalType, env: &mut Env) -> MalRet { + match &ast { + List(list) => match list.len() { + 0 => Ok(ast.clone()), + _ => apply(list, env), + }, _ => eval_ast(ast, env), } } -fn eval_collection(list: &MalArgs, env: &Env) -> Result { +fn eval_collection(list: &MalArgs, env: &mut Env) -> Result { let mut ret = MalArgs::new(); for el in list { match eval(el, env) { @@ -49,7 +82,7 @@ fn eval_collection(list: &MalArgs, env: &Env) -> Result { Ok(ret) } -fn eval_map(map: &MalMap, env: &Env) -> MalRet { +fn eval_map(map: &MalMap, env: &mut Env) -> MalRet { let mut ret = MalMap::new(); for (k, v) in map { @@ -62,7 +95,7 @@ fn eval_map(map: &MalMap, env: &Env) -> MalRet { Ok(Map(ret)) } -fn eval_ast(ast: &MalType, env: &Env) -> MalRet { +fn eval_ast(ast: &MalType, env: &mut Env) -> MalRet { match ast { Sym(sym) => env.get(sym), List(list) => Ok(List(eval_collection(list, env)?)), diff --git a/src/main.rs b/src/main.rs index e0855d2..e689dd8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,17 +6,14 @@ mod eval; mod printer; mod reader; mod types; -use types::env_init; - -use env::Env; +use env::env_init; mod step3_env; use step3_env::rep; fn main() { let mut num = 0; - let mut reply_env = Env::new(None); - env_init(&mut reply_env); + let mut reply_env = env_init(); loop { let mut input = String::new(); @@ -25,7 +22,7 @@ fn main() { // Flush the prompt to appear before command let _ = io::stdout().flush(); - // Read line to compose program inpug + // Read line to compose program input let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); diff --git a/src/types.rs b/src/types.rs index 777743a..8cfee65 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::process::exit; // All Mal types should inherit from this #[derive(Debug, Clone)] @@ -93,18 +92,3 @@ pub fn int_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> Mal Ok(Int(left)) } - -use crate::env::Env; -use MalType::Fun; - -pub fn env_init(env: &mut Env) { - env.set("quit", &Fun(|_| exit(0))); - env.set("+", &Fun(|a: &[MalType]| int_op(0, |a, b| a + b, a))); - env.set("-", &Fun(|a: &[MalType]| int_op(0, |a, b| a - b, a))); - env.set("*", &Fun(|a: &[MalType]| int_op(1, |a, b| a * b, a))); - env.set("/", &Fun(|a: &[MalType]| int_op(1, |a, b| a / b, a))); - env.set( - "test", - &Fun(|_| Ok(Str("This is a test function".to_string()))), - ); -}