feat: if statements

This commit is contained in:
Roman Godmaire 2024-02-16 08:13:10 -05:00
parent bd12bd998b
commit f93baad338
2 changed files with 40 additions and 20 deletions

View file

@ -40,8 +40,7 @@ pub fn core_environment() -> Rc<Environment> {
.unwrap_or(Rc::new(Expression::Int(0))); .unwrap_or(Rc::new(Expression::Int(0)));
Ok(res) Ok(res)
}) }),
.into(),
), ),
( (
"-".to_string(), "-".to_string(),
@ -57,8 +56,7 @@ pub fn core_environment() -> Rc<Environment> {
.unwrap_or(Rc::new(Expression::Int(0))); .unwrap_or(Rc::new(Expression::Int(0)));
Ok(res) Ok(res)
}) }),
.into(),
), ),
( (
"*".to_string(), "*".to_string(),
@ -74,8 +72,7 @@ pub fn core_environment() -> Rc<Environment> {
.unwrap_or(Rc::new(Expression::Int(0))); .unwrap_or(Rc::new(Expression::Int(0)));
Ok(res) Ok(res)
}) }),
.into(),
), ),
( (
"/".to_string(), "/".to_string(),
@ -91,13 +88,12 @@ pub fn core_environment() -> Rc<Environment> {
.unwrap_or(Rc::new(Expression::Int(0))); .unwrap_or(Rc::new(Expression::Int(0)));
Ok(res) Ok(res)
}) }),
.into(),
), ),
// Collections // Collections
( (
"vector".to_string(), "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(), "hashmap".to_string(),
@ -121,8 +117,26 @@ pub fn core_environment() -> Rc<Environment> {
.collect(); .collect();
Ok(Rc::new(Expression::HashMap(res))) 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 // Environment Manipulation
( (
@ -143,8 +157,7 @@ pub fn core_environment() -> Rc<Environment> {
env.set(key, val.clone()); env.set(key, val.clone());
Ok(val) Ok(val)
}) }),
.into(),
), ),
( (
"let*".to_string(), "let*".to_string(),
@ -173,13 +186,14 @@ pub fn core_environment() -> Rc<Environment> {
}; };
eval_ast_node(Rc::new(new_env), args.next().unwrap()) eval_ast_node(Rc::new(new_env), args.next().unwrap())
}) }),
.into(),
), ),
]; ]
.into_iter()
.map(|(k, v)| (k, Rc::new(v)));
Environment { Environment {
current: RefCell::new(env.into()), current: RefCell::new(HashMap::from_iter(env)),
outer: None, outer: None,
} }
.into() .into()

View file

@ -68,8 +68,8 @@ impl std::fmt::Display for Expression {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum Error { pub enum Error {
#[error("could not find symbol in environment")] #[error("could not find symbol '{0}' in environment")]
NotInEnv, NotInEnv(String),
#[error("expression does not have a valid operator")] #[error("expression does not have a valid operator")]
InvalidOperator, InvalidOperator,
#[error("expected symbol")] #[error("expected symbol")]
@ -112,7 +112,7 @@ fn eval_ast_node(env: Rc<Environment>, ast_node: Node) -> Result<Rc<Expression>>
_ => Err(Error::InvalidOperator)?, _ => 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::Int(i) => Expression::Int(i).into(),
Node::Keyword(val) => Expression::Keyword(val).into(), Node::Keyword(val) => Expression::Keyword(val).into(),
@ -161,6 +161,12 @@ mod test {
// Environment manipulation // Environment manipulation
#[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")] #[case("(define! asdf (+ 2 2)) (+ asdf 2)", "4\n6")]
#[case("(let* (a 2) (+ a 2))", "4")] #[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) { fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
let env = core_environment(); let env = core_environment();
let tokens = lexer::read(input).unwrap(); let tokens = lexer::read(input).unwrap();