diff --git a/src/core.rs b/src/core.rs index c229d52..e655d19 100644 --- a/src/core.rs +++ b/src/core.rs @@ -35,25 +35,16 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> MalRet { } } -fn if_number(val: &MalType) -> Result { - match val { - Int(val) => Ok(*val), - _ => Err(MalErr::unrecoverable( - format!("{:?} is not a number", prt(val)).as_str(), - )), - } -} - pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet { if args.is_empty() { return Ok(Int(set)); } - let mut left = if_number(&args[0])?; + let mut left = args[0].if_number()?; if args.len() > 1 { let right = &args[1..]; for el in right { - left = f(left, if_number(el)?); + left = f(left, el.if_number()?); } } @@ -64,9 +55,9 @@ use MalType::{Bool, Nil}; pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet { let (left, rights) = car_cdr(args)?; - let mut left = if_number(left)?; + let mut left = left.if_number()?; for right in rights { - let right = if_number(right)?; + let right = right.if_number()?; if !f(left, right) { return Ok(Nil); } diff --git a/src/env.rs b/src/env.rs index b661172..00f5b47 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,5 +1,5 @@ use crate::core::{arithmetic_op, comparison_op, core_exit}; -use crate::types::{MalErr, MalType::*}; +use crate::types::MalErr; use crate::types::{MalMap, MalRet, MalType}; use std::cell::RefCell; use std::rc::Rc; @@ -54,7 +54,7 @@ pub fn env_set(env: &Env, sym: &str, val: &MalType) -> MalType { val.clone() } -pub fn env_get(env: &Env, sym: &String) -> MalRet { +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() { @@ -66,32 +66,17 @@ pub fn env_get(env: &Env, sym: &String) -> MalRet { } } -use crate::printer::prt; - pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result { let env = env_new(Some(outer)); - match binds { - List(binds) => { - if binds.len() != exprs.len() { - return Err(MalErr::unrecoverable("Env init with unmatched length")); - } // 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(&env, sym, expr); - } - _ => { - return Err(MalErr::unrecoverable( - format!("Initializing environment: {:?} is not a symbol", prt(bind)) - .as_str(), - )) - } - } - } - Ok(env) - } - _ => Err(MalErr::unrecoverable("init: first argument must be a list")), + let binds = binds.if_list()?; + if binds.len() != exprs.len() { + return Err(MalErr::unrecoverable("Wrong number of arguments")); + } // TODO: May be possible to leave this be and not set additional elements at all + for (bind, expr) in binds.iter().zip(exprs.iter()) { + let bind = bind.if_symbol()?; + env_set(&env, bind, expr); } + Ok(env) } use crate::types::MalType::{Fun, Str}; diff --git a/src/eval.rs b/src/eval.rs index 998df3c..404d692 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use crate::core::{call_func, car_cdr, scream}; +use crate::core::{call_func, car_cdr}; use crate::env::Env; use crate::env::{env_get, env_new, env_set}; use crate::printer::prt; @@ -10,13 +10,9 @@ use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType}; /// Resolve the first element of the list as the function name and call it /// with the other elements as arguments fn eval_func(list: &MalType) -> MalRet { - match list { - List(list) => { - let (func, args) = car_cdr(list)?; - call_func(func, args) - } - _ => scream(), - } + let list = list.if_list()?; + let (func, args) = car_cdr(list)?; + call_func(func, args) } // When evaluating an expression it's possible @@ -35,17 +31,9 @@ fn def_bang_form(list: &[MalType], env: Env) -> MalRet { if list.len() != 2 { return Err(MalErr::unrecoverable("def! form: needs 2 arguments")); } - match &list[0] { - Sym(sym) => Ok(env_set(&env, sym.as_str(), &eval(&list[1], env.clone())?)), - _ => Err(MalErr::unrecoverable( - format!( - "def! Assigning {:?} to {:?}, which is not a symbol", - prt(&list[1]), - prt(&list[0]) - ) - .as_str(), - )), - } + let (car, _) = car_cdr(list)?; + let sym = car.if_symbol()?; + Ok(env_set(&env, sym, &eval(&list[1], env.clone())?)) } /// let* special form: @@ -56,22 +44,20 @@ fn let_star_form(list: &[MalType], env: Env) -> MalRet { let inner_env = env_new(Some(env.clone())); // change the inner environment let (car, cdr) = car_cdr(list)?; - match car { - List(list) if list.len() % 2 == 0 => { - // TODO: 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 list = car.if_list()?; + if list.len() % 2 == 0 { + // TODO: 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())?; } - _ => Err(MalErr::unrecoverable( - "First argument of let* must be a list of pair definitions", - )), + if cdr.is_empty() { + // TODO: check if it exists a better way to do this + Ok(Nil) + } else { + eval(&cdr[0], inner_env) + } + } else { + Err(MalErr::unrecoverable("let* form, number of arguments must be even")) } } @@ -118,20 +104,13 @@ use crate::printer::print_malfun; pub fn help_form(list: &[MalType], env: Env) -> MalRet { let (sym, _) = car_cdr(list)?; - match car_cdr(list)?.0 { - Sym(sym_str) => { - match eval(sym, env.clone())? { - Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc), - MalFun { params, ast, .. } => print_malfun(sym_str, params, ast), - _ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)), - } - Ok(Bool(true)) - } - _ => { - println!("{} is not a symbol", prt(sym)); - Ok(Nil) - } + 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), + _ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)), } + Ok(Bool(true)) } /// Intermediate function to discern special forms from defined symbols diff --git a/src/printer.rs b/src/printer.rs index 8b87d89..96f1f75 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -49,7 +49,7 @@ pub fn prt(ast: &MalType) -> String { pr_str(ast, true) } -pub fn print_malfun(sym: &String, params: Rc, ast: Rc) { +pub fn print_malfun(sym: &str, params: Rc, ast: Rc) { println!("{}\t[function]: {}", sym, prt(¶ms)); match ast.as_ref() { List(list) => { diff --git a/src/types.rs b/src/types.rs index 236b60b..c2c5f73 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,6 +23,31 @@ pub enum MalType { Nil, } +impl MalType { + pub fn if_number(&self) -> Result { + match self { + Self::Int(val) => Ok(*val), + _ => Err(MalErr::unrecoverable( + format!("{:?} is not a number", prt(self)).as_str(), + )), + } + } + + 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())) + } + } + + 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())) + } + } +} + #[derive(PartialEq, Clone, Copy)] pub enum Severity { Recoverable,