From 80f8ede0bbc9799d5199707e1e1ad8e80e4ca7ac Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Sun, 7 Mar 2021 15:29:59 -0500 Subject: Initial commit --- src/interpreter/mod.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/interpreter/mod.rs (limited to 'src/interpreter/mod.rs') diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 000000000000..adff3568c2c3 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1,125 @@ +mod error; +mod value; + +pub use self::error::{Error, Result}; +pub use self::value::Value; +use crate::ast::{BinaryOperator, Expr, Ident, Literal, UnaryOperator}; +use crate::common::env::Env; + +#[derive(Debug, Default)] +pub struct Interpreter<'a> { + env: Env<'a, Value>, +} + +impl<'a> Interpreter<'a> { + pub fn new() -> Self { + Self::default() + } + + fn resolve(&self, var: &'a Ident<'a>) -> Result { + self.env + .resolve(var) + .cloned() + .ok_or_else(|| Error::UndefinedVariable(var.to_owned())) + } + + pub fn eval(&mut self, expr: &'a Expr<'a>) -> Result { + match expr { + Expr::Ident(id) => self.resolve(id), + Expr::Literal(Literal::Int(i)) => Ok((*i).into()), + Expr::UnaryOp { op, rhs } => { + let rhs = self.eval(rhs)?; + match op { + UnaryOperator::Neg => -rhs, + _ => unimplemented!(), + } + } + Expr::BinaryOp { lhs, op, rhs } => { + let lhs = self.eval(lhs)?; + let rhs = self.eval(rhs)?; + match op { + BinaryOperator::Add => lhs + rhs, + BinaryOperator::Sub => lhs - rhs, + BinaryOperator::Mul => lhs * rhs, + BinaryOperator::Div => lhs / rhs, + BinaryOperator::Pow => todo!(), + BinaryOperator::Equ => Ok(lhs.eq(&rhs).into()), + BinaryOperator::Neq => todo!(), + } + } + Expr::Let { bindings, body } => { + self.env.push(); + for (id, val) in bindings { + let val = self.eval(val)?; + self.env.set(id, val); + } + let res = self.eval(body)?; + self.env.pop(); + Ok(res) + } + Expr::If { + condition, + then, + else_, + } => { + let condition = self.eval(condition)?; + if *(condition.into_type::()?) { + self.eval(then) + } else { + self.eval(else_) + } + } + } + } +} + +pub fn eval<'a>(expr: &'a Expr<'a>) -> Result { + let mut interpreter = Interpreter::new(); + interpreter.eval(expr) +} + +#[cfg(test)] +mod tests { + use std::convert::TryFrom; + + use super::value::{TypeOf, Val}; + use super::*; + use BinaryOperator::*; + + fn int_lit(i: u64) -> Box> { + Box::new(Expr::Literal(Literal::Int(i))) + } + + fn parse_eval(src: &str) -> T + where + for<'a> &'a T: TryFrom<&'a Val>, + T: Clone + TypeOf, + { + let expr = crate::parser::expr(src).unwrap().1; + let res = eval(&expr).unwrap(); + res.into_type::().unwrap().clone() + } + + #[test] + fn simple_addition() { + let expr = Expr::BinaryOp { + lhs: int_lit(1), + op: Mul, + rhs: int_lit(2), + }; + let res = eval(&expr).unwrap(); + assert_eq!(*res.into_type::().unwrap(), 2); + } + + #[test] + fn variable_shadowing() { + let res = parse_eval::("let x = 1 in (let x = 2 in x) + x"); + assert_eq!(res, 3); + } + + #[test] + fn conditional_with_equals() { + let res = parse_eval::("let x = 1 in if x == 1 then 2 else 4"); + assert_eq!(res, 2); + } +} -- cgit 1.4.1