diff --git a/src/interpreter.rs b/src/interpreter.rs index 1f85981..987198e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -18,7 +18,7 @@ pub fn interpret(ast: &Ast) -> Result<()> { #[cfg(test)] pub fn interpret_expr(expr: &Spanned) -> Result { - Interpreter::interpret_expr(expr, &Env::default()) + Interpreter::with_output(Box::new(std::io::sink())).interpret_expr(expr, &Env::default()) } pub struct Interpreter<'a> { @@ -52,26 +52,26 @@ impl<'a> Interpreter<'a> { impl<'a> Interpreter<'a> { pub fn interpret(&mut self, ast: &Ast) -> Result<()> { for stmt in &ast.0 { - Self::execute_stmt(stmt, &mut self.output, &self.env)?; + self.execute_stmt(stmt, &self.env.clone())?; } Ok(()) } #[tracing::instrument(name = "stmt", skip_all)] - fn execute_stmt(stmt: &Spanned, output: &mut dyn Write, env: &Env) -> Result<()> { + fn execute_stmt(&mut self, stmt: &Spanned, env: &Env) -> Result<()> { match stmt.as_ref() { Stmt::Expr(e) => { - Self::interpret_expr(e, env)?; + self.interpret_expr(e, env)?; } Stmt::Print(e) => { - let value = Self::interpret_expr(e, env)?; - writeln!(output, "{value}")?; + let value = self.interpret_expr(e, env)?; + writeln!(&mut self.output, "{value}")?; } Stmt::VarDecl { name, initializer } => { let val = initializer .as_ref() - .map(|e| Self::interpret_expr(e, env)) + .map(|e| self.interpret_expr(e, env)) .transpose()? .unwrap_or(Value::Nil); env.define(name.clone(), val); @@ -80,7 +80,7 @@ impl<'a> Interpreter<'a> { Stmt::Block(stmts) => { let child_env = env.new_child(); for stmt in stmts { - Self::execute_stmt(stmt, output, &child_env)?; + self.execute_stmt(stmt, &child_env)?; } } Stmt::If { @@ -88,18 +88,18 @@ impl<'a> Interpreter<'a> { then, otherwise, } => { - let condition_val = Self::interpret_expr(condition, env)?; + let condition_val = self.interpret_expr(condition, env)?; if condition_val.is_truthy() { - Self::execute_stmt(then, output, env)?; + self.execute_stmt(then, env)?; } else if let Some(otherwise) = otherwise { - Self::execute_stmt(otherwise, output, env)?; + self.execute_stmt(otherwise, env)?; } } Stmt::While { condition, body } => { - let mut condition_val = Self::interpret_expr(condition, env)?; + let mut condition_val = self.interpret_expr(condition, env)?; while condition_val.is_truthy() { - Self::execute_stmt(body, output, env)?; - condition_val = Self::interpret_expr(condition, env)?; + self.execute_stmt(body, env)?; + condition_val = self.interpret_expr(condition, env)?; } } } @@ -108,15 +108,15 @@ impl<'a> Interpreter<'a> { } #[tracing::instrument(name = "expr", skip_all)] - fn interpret_expr(expr: &Spanned, env: &Env) -> Result { + fn interpret_expr(&mut self, expr: &Spanned, env: &Env) -> Result { match expr.as_ref() { - Expr::Binary { left, op, right } => Self::interpret_binary_op(left, op, right, env), - Expr::Grouping { inner } => Self::interpret_grouping(inner, env), + Expr::Binary { left, op, right } => self.interpret_binary_op(left, op, right, env), + Expr::Grouping { inner } => self.interpret_grouping(inner, env), Expr::Literal { lit } => { tracing::trace!("resolving literal"); Ok(Value::from(lit.as_ref())) } - Expr::Unary { op, expr } => Self::interpret_unary(op, expr, env), + Expr::Unary { op, expr } => self.interpret_unary(op, expr, env), Expr::Var { name } => env .get(name) .ok_or_else(|| RuntimeError::undefined_var(name)), @@ -124,7 +124,7 @@ impl<'a> Interpreter<'a> { let name = match target.as_ref() { Lval::Ident { name } => name.clone(), }; - let value = Self::interpret_expr(value, env)?; + let value = self.interpret_expr(value, env)?; if env.assign(name.clone(), value.clone()) { Ok(value) } else { @@ -135,19 +135,19 @@ impl<'a> Interpreter<'a> { } } Expr::Logical { left, op, right } => { - let left_val = Self::interpret_expr(left, env)?; + let left_val = self.interpret_expr(left, env)?; match op.as_ref() { // short circuit LogicalOp::Or if left_val.is_truthy() => Ok(left_val), LogicalOp::And if !left_val.is_truthy() => Ok(left_val), // evaluate and return RHS - _ => Self::interpret_expr(right, env), + _ => self.interpret_expr(right, env), } } Expr::Call { callee, arguments } => { // Evaluate the callee and ensure it's actually callable - let callable = match Self::interpret_expr(callee, env)? { + let callable = match self.interpret_expr(callee, env)? { Value::Callable(callee) => callee, other => return Err(RuntimeError::called_non_callable(&other, callee.span())), }; @@ -165,7 +165,7 @@ impl<'a> Interpreter<'a> { // Interpret each of the arguments let evaluated_args = arguments .iter() - .map(|arg| Self::interpret_expr(arg, env)) + .map(|arg| self.interpret_expr(arg, env)) .collect::, _>>()?; // Define a new env for the call @@ -182,16 +182,17 @@ impl<'a> Interpreter<'a> { #[tracing::instrument(name = "binary", skip_all)] fn interpret_binary_op( + &mut self, left: &Spanned, op: &Spanned, right: &Spanned, env: &Env, ) -> Result { tracing::trace!("evaluating lhs of binary expr"); - let left_val = Self::interpret_expr(left, env)?; + let left_val = self.interpret_expr(left, env)?; tracing::trace!("evaluating rhs of binary expr"); - let right_val = Self::interpret_expr(right, env)?; + let right_val = self.interpret_expr(right, env)?; match op.as_ref() { BinaryOp::Add => match (&left_val, &right_val) { @@ -286,16 +287,21 @@ impl<'a> Interpreter<'a> { } #[tracing::instrument(name = "grouping", skip_all)] - fn interpret_grouping(inner: &Spanned, env: &Env) -> Result { + fn interpret_grouping(&mut self, inner: &Spanned, env: &Env) -> Result { tracing::trace!("descending to group"); // TODO: investigate using more span features here - Self::interpret_expr(inner, env) + self.interpret_expr(inner, env) } #[tracing::instrument(name = "unary", skip_all)] - fn interpret_unary(op: &Spanned, expr: &Spanned, env: &Env) -> Result { + fn interpret_unary( + &mut self, + op: &Spanned, + expr: &Spanned, + env: &Env, + ) -> Result { tracing::trace!("descending into unary"); - let val = Self::interpret_expr(expr, env)?; + let val = self.interpret_expr(expr, env)?; match op.as_ref() { UnaryOp::Negative => { tracing::trace!("negating value");