From 10223cbb4eca0630831a7b289a65ccf241f487f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Thu, 5 Jan 2023 07:54:18 +0100 Subject: [PATCH] Interpreter - WIP refactor to prepare for using resolved variables --- src/interpreter/callable.rs | 8 +- src/interpreter/environment.rs | 8 +- src/interpreter/functions.rs | 32 +++++--- src/interpreter/interpretable.rs | 125 ++++++++++++++++++------------- src/interpreter/mod.rs | 4 +- src/interpreter/native_fn.rs | 6 +- 6 files changed, 104 insertions(+), 79 deletions(-) diff --git a/src/interpreter/callable.rs b/src/interpreter/callable.rs index 20ac38e..b84c846 100644 --- a/src/interpreter/callable.rs +++ b/src/interpreter/callable.rs @@ -2,17 +2,17 @@ use std::{cell::RefCell, fmt::Debug, rc::Rc}; use crate::errors::SloxResult; -use super::{EnvironmentRef, Value}; +use super::{InterpreterState, Value}; /// A callable is some object that supports being called. -pub trait Callable: Debug + ToString { +pub(super) trait Callable: Debug + ToString { /// Return the amount of arguments supported by the callable. fn arity(&self) -> usize; /// Run the callable in the execution environment with the specified /// arguments. - fn call(&self, environment: &EnvironmentRef, arguments: Vec) -> SloxResult; + fn call(&self, environment: &mut InterpreterState, arguments: Vec) -> SloxResult; } /// A reference to a callable. -pub type CallableRef = Rc>; +pub(super) type CallableRef = Rc>; diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs index f68d47a..7ec42d8 100644 --- a/src/interpreter/environment.rs +++ b/src/interpreter/environment.rs @@ -8,15 +8,15 @@ use crate::{ use super::{native_fn, CallableRef, Value}; /// A mutable reference to an environment. -pub type EnvironmentRef = Rc>; +pub(super) type EnvironmentRef = Rc>; /// A variable. -pub type Variable = Option; +pub(super) type Variable = Option; /// The execution environment. #[derive(Debug)] -pub struct Environment { - enclosing: Option, +pub(super) struct Environment { + pub(super) enclosing: Option, values: HashMap, } diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index 5fab45c..1c960cf 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -2,14 +2,10 @@ use std::{cell::RefCell, rc::Rc}; use itertools::izip; -use crate::{ - ast, - errors::SloxResult, - interpreter::{Environment, Interpretable, InterpreterFlowControl}, - tokens::Token, +use super::{ + Callable, Environment, Interpretable, InterpreterFlowControl, InterpreterState, Value, }; - -use super::{Callable, EnvironmentRef, Value}; +use crate::{ast, errors::SloxResult, tokens::Token}; /// A function implemented in the Lox-ish language. #[derive(Debug)] @@ -39,16 +35,28 @@ impl Callable for Function { self.params.len() } - fn call(&self, environment: &EnvironmentRef, arguments: Vec) -> SloxResult { + fn call(&self, es: &mut InterpreterState, arguments: Vec) -> SloxResult { assert_eq!(arguments.len(), self.arity()); - let param_env = Environment::create_child(environment); + let param_env = InterpreterState { + environment: Environment::create_child(&es.environment), + globals: es.globals.clone(), + variables: es.variables, + }; for (arg, value) in izip!(self.params.iter(), arguments.into_iter()) { - param_env.borrow_mut().define(arg, Some(value)).unwrap(); + param_env + .environment + .borrow_mut() + .define(arg, Some(value)) + .unwrap(); } - let child = Environment::create_child(¶m_env); + let mut child = InterpreterState { + environment: Environment::create_child(¶m_env.environment), + globals: es.globals.clone(), + variables: es.variables, + }; for stmt in self.body.iter() { - let result = stmt.interpret(&child)?; + let result = stmt.interpret(&mut child)?; match result { InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Return(v) => return Ok(v), diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs index 6f54712..3cb1dc7 100644 --- a/src/interpreter/interpretable.rs +++ b/src/interpreter/interpretable.rs @@ -11,13 +11,26 @@ use crate::{ /// Evaluate an interpretable, returning its value. pub fn evaluate(ast: &ast::ProgramNode, vars: ResolvedVariables) -> SloxResult { let env = Rc::new(RefCell::new(Environment::default())); - ast.interpret(&env).map(|v| v.result()) + let mut state = InterpreterState{ + environment: env.clone(), + globals: env, + variables: &vars, + }; + ast.interpret(&mut state).map(|v| v.result()) } /* ------- * * HELPERS * * ------- */ +/// The state of the interpreter. +#[derive(Debug)] +pub(super) struct InterpreterState<'a> { + pub(super) globals: EnvironmentRef, + pub(super) environment: EnvironmentRef, + pub(super) variables: &'a ResolvedVariables, +} + /// Interpreter flow control, which may be either a value, a loop break or a /// loop continuation. #[derive(Debug)] @@ -62,7 +75,7 @@ pub(super) type InterpreterResult = SloxResult; /// An Interpretable can be evaluated and will return a value. pub(super) trait Interpretable { - fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult; + fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult; } /// Generate an error with a static message. @@ -79,9 +92,9 @@ fn error(token: &Token, message: &str) -> SloxResult { * ----------------------------- */ impl Interpretable for ast::ProgramNode { - fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { + fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult { for stmt in self.0.iter() { - stmt.interpret(environment)?; + stmt.interpret(es)?; } Ok(InterpreterFlowControl::default()) } @@ -92,32 +105,32 @@ impl Interpretable for ast::ProgramNode { * ------------------------------- */ impl Interpretable for ast::StmtNode { - fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { + fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult { match self { - ast::StmtNode::VarDecl(name, expr) => self.on_var_decl(environment, name, expr), + ast::StmtNode::VarDecl(name, expr) => self.on_var_decl(es, name, expr), ast::StmtNode::FunDecl { name, params, body } => { - self.on_fun_decl(environment, name, params, body) + self.on_fun_decl(es, name, params, body) } - ast::StmtNode::Expression(expr) => expr.interpret(environment), - ast::StmtNode::Print(expr) => self.on_print(environment, expr), - ast::StmtNode::Block(statements) => self.on_block(environment, statements), + ast::StmtNode::Expression(expr) => expr.interpret(es), + ast::StmtNode::Print(expr) => self.on_print(es, expr), + ast::StmtNode::Block(statements) => self.on_block(es, statements), ast::StmtNode::If { condition, then_branch, else_branch, - } => self.on_if_statement(environment, condition, then_branch, else_branch), + } => self.on_if_statement(es, condition, then_branch, else_branch), ast::StmtNode::Loop { label, condition, body, after_body, - } => self.on_loop_statement(environment, label, condition, body, after_body), + } => self.on_loop_statement(es, label, condition, body, after_body), ast::StmtNode::LoopControl { is_break, loop_name, } => self.on_loop_control_statemement(*is_break, loop_name), ast::StmtNode::Return { token: _, value } => { - self.on_return_statement(environment, value) + self.on_return_statement(es, value) } } } @@ -125,8 +138,8 @@ impl Interpretable for ast::StmtNode { impl ast::StmtNode { /// Handle the `print` statement. - fn on_print(&self, environment: &EnvironmentRef, expr: &ast::ExprNode) -> InterpreterResult { - let value = expr.interpret(environment)?.result(); + fn on_print(&self, es: &mut InterpreterState, expr: &ast::ExprNode) -> InterpreterResult { + let value = expr.interpret(es)?.result(); let output = match value { Value::Nil => String::from("nil"), Value::Boolean(true) => String::from("true"), @@ -142,38 +155,42 @@ impl ast::StmtNode { /// Handle a variable declaration. fn on_var_decl( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, name: &Token, initializer: &Option, ) -> InterpreterResult { let variable = match initializer { - Some(expr) => Some(expr.interpret(environment)?.result()), + Some(expr) => Some(expr.interpret(es)?.result()), None => None, }; - environment.borrow_mut().define(name, variable)?; + es.environment.borrow_mut().define(name, variable)?; Ok(InterpreterFlowControl::default()) } /// Handle a function declaration. fn on_fun_decl( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, name: &Token, params: &[Token], body: &[ast::StmtNode], ) -> InterpreterResult { let fun = Function::new(Some(name), params, body); - environment + es.environment .borrow_mut() .define(name, Some(Value::Callable(fun)))?; Ok(InterpreterFlowControl::default()) } /// Execute the contents of a block. - fn on_block(&self, environment: &EnvironmentRef, stmts: &[ast::StmtNode]) -> InterpreterResult { - let child = Environment::create_child(environment); + fn on_block(&self, es: &mut InterpreterState, stmts: &[ast::StmtNode]) -> InterpreterResult { + let mut child = InterpreterState{ + environment: Environment::create_child(&es.environment), + globals: es.globals.clone(), + variables: es.variables, + }; for stmt in stmts.iter() { - let result = stmt.interpret(&child)?; + let result = stmt.interpret(&mut child)?; if result.is_flow_control() { return Ok(result); } @@ -184,15 +201,15 @@ impl ast::StmtNode { /// Execute an if statement. fn on_if_statement( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, condition: &ast::ExprNode, then_branch: &ast::StmtNode, else_branch: &Option>, ) -> InterpreterResult { - if condition.interpret(environment)?.result().is_truthy() { - then_branch.interpret(environment) + if condition.interpret(es)?.result().is_truthy() { + then_branch.interpret(es) } else if let Some(else_stmt) = else_branch { - else_stmt.interpret(environment) + else_stmt.interpret(es) } else { Ok(InterpreterFlowControl::default()) } @@ -201,15 +218,15 @@ impl ast::StmtNode { /// Execute a while statement. fn on_loop_statement( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, label: &Option, condition: &ast::ExprNode, body: &ast::StmtNode, after_body: &Option>, ) -> InterpreterResult { let ln = label.as_ref().map(|token| token.lexeme.clone()); - while condition.interpret(environment)?.result().is_truthy() { - let result = body.interpret(environment)?; + while condition.interpret(es)?.result().is_truthy() { + let result = body.interpret(es)?; match &result { InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Continue(lv) if lv == &ln => (), @@ -217,7 +234,7 @@ impl ast::StmtNode { _ => return Ok(result), } if let Some(stmt) = after_body { - let result = stmt.interpret(environment)?; + let result = stmt.interpret(es)?; match &result { InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Continue(lv) if lv == &ln => (), @@ -246,12 +263,12 @@ impl ast::StmtNode { /// Execute a return statement. fn on_return_statement( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, value: &Option, ) -> InterpreterResult { let rv = match value { None => Value::Nil, - Some(expr) => expr.interpret(environment)?.result(), + Some(expr) => expr.interpret(es)?.result(), }; Ok(InterpreterFlowControl::Return(rv)) } @@ -262,32 +279,32 @@ impl ast::StmtNode { * -------------------------------- */ impl Interpretable for ast::ExprNode { - fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { + fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult { match self { ast::ExprNode::Assignment { name, value } => { - let value = value.interpret(environment)?.result(); - environment.borrow_mut().assign(name, value)?; + let value = value.interpret(es)?.result(); + es.environment.borrow_mut().assign(name, value)?; Ok(InterpreterFlowControl::default()) } ast::ExprNode::Logical { left, operator, right, - } => self.on_logic(environment, left, operator, right), + } => self.on_logic(es, left, operator, right), ast::ExprNode::Binary { left, operator, right, - } => self.on_binary(environment, left, operator, right), - ast::ExprNode::Unary { operator, right } => self.on_unary(environment, operator, right), - ast::ExprNode::Grouping { expression } => expression.interpret(environment), + } => self.on_binary(es, left, operator, right), + ast::ExprNode::Unary { operator, right } => self.on_unary(es, operator, right), + ast::ExprNode::Grouping { expression } => expression.interpret(es), ast::ExprNode::Litteral { value } => self.on_litteral(value), - ast::ExprNode::Variable { name } => Ok(environment.borrow().get(name)?.into()), + ast::ExprNode::Variable { name } => Ok(es.environment.borrow().get(name)?.into()), ast::ExprNode::Call { callee, right_paren, arguments, - } => self.on_call(environment, callee, right_paren, arguments), + } => self.on_call(es, callee, right_paren, arguments), ast::ExprNode::Lambda { params, body } => { Ok(Value::Callable(Function::new(None, params, body)).into()) } @@ -299,31 +316,31 @@ impl ast::ExprNode { /// Evaluate a logical operator. fn on_logic( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, left: &ast::ExprNode, operator: &Token, right: &ast::ExprNode, ) -> InterpreterResult { - let left_value = left.interpret(environment)?.result(); + let left_value = left.interpret(es)?.result(); if operator.token_type == TokenType::Or && left_value.is_truthy() || operator.token_type == TokenType::And && !left_value.is_truthy() { Ok(left_value.into()) } else { - right.interpret(environment) + right.interpret(es) } } /// Evaluate a binary operator. fn on_binary( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, left: &ast::ExprNode, operator: &Token, right: &ast::ExprNode, ) -> InterpreterResult { - let left_value = left.interpret(environment)?.result(); - let right_value = right.interpret(environment)?.result(); + let left_value = left.interpret(es)?.result(); + let right_value = right.interpret(es)?.result(); match operator.token_type { TokenType::Plus => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b).into()), @@ -388,11 +405,11 @@ impl ast::ExprNode { /// Evaluate an unary operator. fn on_unary( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, operator: &Token, right: &ast::ExprNode, ) -> InterpreterResult { - let right_value = right.interpret(environment)?.result(); + let right_value = right.interpret(es)?.result(); match operator.token_type { TokenType::Minus => { if let Value::Number(n) = right_value { @@ -427,16 +444,16 @@ impl ast::ExprNode { /// Evaluate a function call. fn on_call( &self, - environment: &EnvironmentRef, + es: &mut InterpreterState, callee: &ast::ExprNode, right_paren: &Token, arguments: &Vec, ) -> InterpreterResult { - let callee = callee.interpret(environment)?.result(); + let callee = callee.interpret(es)?.result(); let arg_values = { let mut v = Vec::with_capacity(arguments.len()); for argument in arguments.iter() { - v.push(argument.interpret(environment)?.result()); + v.push(argument.interpret(es)?.result()); } v }; @@ -453,7 +470,7 @@ impl ast::ExprNode { ), )) } else { - Ok(callable.call(environment, arg_values)?.into()) + Ok(callable.call(es, arg_values)?.into()) } } else { error(right_paren, "can only call functions and classes") diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 007ff4a..a5ca495 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -5,7 +5,7 @@ mod interpretable; mod native_fn; mod value; -pub use callable::{Callable, CallableRef}; -pub use environment::*; +pub(self) use callable::{Callable, CallableRef}; +pub(self) use environment::*; pub use interpretable::*; pub use value::*; diff --git a/src/interpreter/native_fn.rs b/src/interpreter/native_fn.rs index 988edb1..a0e8719 100644 --- a/src/interpreter/native_fn.rs +++ b/src/interpreter/native_fn.rs @@ -6,7 +6,7 @@ use std::{ use crate::errors::SloxResult; -use super::{Callable, CallableRef, EnvironmentRef, Value}; +use super::{Callable, CallableRef, InterpreterState, Value}; /* ----------- * * clock() * @@ -20,7 +20,7 @@ impl Callable for Clock { 0 } - fn call(&self, _environment: &EnvironmentRef, _arguments: Vec) -> SloxResult { + fn call(&self, _environment: &mut InterpreterState, _arguments: Vec) -> SloxResult { let now = SystemTime::now(); let since_epoch = now .duration_since(UNIX_EPOCH) @@ -35,6 +35,6 @@ impl ToString for Clock { } } -pub(crate) fn clock() -> CallableRef { +pub(super) fn clock() -> CallableRef { Rc::new(RefCell::new(Clock {})) }