diff --git a/src/evaluator/env.rs b/src/evaluator/env.rs index 0d70323..92f4ca7 100644 --- a/src/evaluator/env.rs +++ b/src/evaluator/env.rs @@ -40,8 +40,7 @@ pub fn core_environment() -> Rc { .unwrap_or(Rc::new(Expression::Int(0))); Ok(res) - }) - .into(), + }), ), ( "-".to_string(), @@ -57,8 +56,7 @@ pub fn core_environment() -> Rc { .unwrap_or(Rc::new(Expression::Int(0))); Ok(res) - }) - .into(), + }), ), ( "*".to_string(), @@ -74,8 +72,7 @@ pub fn core_environment() -> Rc { .unwrap_or(Rc::new(Expression::Int(0))); Ok(res) - }) - .into(), + }), ), ( "/".to_string(), @@ -91,13 +88,12 @@ pub fn core_environment() -> Rc { .unwrap_or(Rc::new(Expression::Int(0))); Ok(res) - }) - .into(), + }), ), // Collections ( "vector".to_string(), - Expression::NativeFunc(|args| Ok(Rc::new(Expression::Vector(args)))).into(), + Expression::NativeFunc(|args| Ok(Rc::new(Expression::Vector(args)))), ), ( "hashmap".to_string(), @@ -121,8 +117,26 @@ pub fn core_environment() -> Rc { .collect(); Ok(Rc::new(Expression::HashMap(res))) - }) - .into(), + }), + ), + // Branching + ( + "if".to_string(), + Expression::NativeFunc(|args| { + if args.len() != 3 { + Err(Error::MismatchedArgCount)? + } + + let (cond, consequence, alternative) = + (args[0].clone(), args[1].clone(), args[2].clone()); + + // If the value is anything other than true or nil, then we return the alternative + if *cond == Expression::Boolean(true) || *cond == Expression::Nil { + return Ok(consequence); + }; + + Ok(alternative) + }), ), // Environment Manipulation ( @@ -143,8 +157,7 @@ pub fn core_environment() -> Rc { env.set(key, val.clone()); Ok(val) - }) - .into(), + }), ), ( "let*".to_string(), @@ -173,13 +186,14 @@ pub fn core_environment() -> Rc { }; eval_ast_node(Rc::new(new_env), args.next().unwrap()) - }) - .into(), + }), ), - ]; + ] + .into_iter() + .map(|(k, v)| (k, Rc::new(v))); Environment { - current: RefCell::new(env.into()), + current: RefCell::new(HashMap::from_iter(env)), outer: None, } .into() diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index 807f3b6..cfbd766 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -68,8 +68,8 @@ impl std::fmt::Display for Expression { #[derive(Debug, Error)] pub enum Error { - #[error("could not find symbol in environment")] - NotInEnv, + #[error("could not find symbol '{0}' in environment")] + NotInEnv(String), #[error("expression does not have a valid operator")] InvalidOperator, #[error("expected symbol")] @@ -112,7 +112,7 @@ fn eval_ast_node(env: Rc, ast_node: Node) -> Result> _ => Err(Error::InvalidOperator)?, } } - Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv)?.to_owned(), + Node::Symbol(sym) => env.get(&sym).ok_or(Error::NotInEnv(sym))?.to_owned(), Node::Int(i) => Expression::Int(i).into(), Node::Keyword(val) => Expression::Keyword(val).into(), @@ -161,6 +161,12 @@ mod test { // Environment manipulation #[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")] #[case("(let* (a 2) (+ a 2))", "4")] + // If-else + #[case("(if true true false)", "true")] + #[case("(if nil true false)", "true")] + #[case("(if false true false)", "false")] + #[case("(if 4 true false)", "false")] + #[case("(if \"blue\" true false)", "false")] fn test_evaluator(#[case] input: &str, #[case] expected: &str) { let env = core_environment(); let tokens = lexer::read(input).unwrap();