Interpreter - WIP refactor to prepare for using resolved variables

This commit is contained in:
Emmanuel BENOîT 2023-01-05 07:54:18 +01:00
parent d528ce8dc3
commit 10223cbb4e
6 changed files with 104 additions and 79 deletions

View file

@ -2,17 +2,17 @@ use std::{cell::RefCell, fmt::Debug, rc::Rc};
use crate::errors::SloxResult; use crate::errors::SloxResult;
use super::{EnvironmentRef, Value}; use super::{InterpreterState, Value};
/// A callable is some object that supports being called. /// 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. /// Return the amount of arguments supported by the callable.
fn arity(&self) -> usize; fn arity(&self) -> usize;
/// Run the callable in the execution environment with the specified /// Run the callable in the execution environment with the specified
/// arguments. /// arguments.
fn call(&self, environment: &EnvironmentRef, arguments: Vec<Value>) -> SloxResult<Value>; fn call(&self, environment: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value>;
} }
/// A reference to a callable. /// A reference to a callable.
pub type CallableRef = Rc<RefCell<dyn Callable>>; pub(super) type CallableRef = Rc<RefCell<dyn Callable>>;

View file

@ -8,15 +8,15 @@ use crate::{
use super::{native_fn, CallableRef, Value}; use super::{native_fn, CallableRef, Value};
/// A mutable reference to an environment. /// A mutable reference to an environment.
pub type EnvironmentRef = Rc<RefCell<Environment>>; pub(super) type EnvironmentRef = Rc<RefCell<Environment>>;
/// A variable. /// A variable.
pub type Variable = Option<Value>; pub(super) type Variable = Option<Value>;
/// The execution environment. /// The execution environment.
#[derive(Debug)] #[derive(Debug)]
pub struct Environment { pub(super) struct Environment {
enclosing: Option<EnvironmentRef>, pub(super) enclosing: Option<EnvironmentRef>,
values: HashMap<String, Variable>, values: HashMap<String, Variable>,
} }

View file

@ -2,14 +2,10 @@ use std::{cell::RefCell, rc::Rc};
use itertools::izip; use itertools::izip;
use crate::{ use super::{
ast, Callable, Environment, Interpretable, InterpreterFlowControl, InterpreterState, Value,
errors::SloxResult,
interpreter::{Environment, Interpretable, InterpreterFlowControl},
tokens::Token,
}; };
use crate::{ast, errors::SloxResult, tokens::Token};
use super::{Callable, EnvironmentRef, Value};
/// A function implemented in the Lox-ish language. /// A function implemented in the Lox-ish language.
#[derive(Debug)] #[derive(Debug)]
@ -39,16 +35,28 @@ impl Callable for Function {
self.params.len() self.params.len()
} }
fn call(&self, environment: &EnvironmentRef, arguments: Vec<Value>) -> SloxResult<Value> { fn call(&self, es: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
assert_eq!(arguments.len(), self.arity()); 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()) { 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(&param_env); let mut child = InterpreterState {
environment: Environment::create_child(&param_env.environment),
globals: es.globals.clone(),
variables: es.variables,
};
for stmt in self.body.iter() { for stmt in self.body.iter() {
let result = stmt.interpret(&child)?; let result = stmt.interpret(&mut child)?;
match result { match result {
InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Result(_) => (),
InterpreterFlowControl::Return(v) => return Ok(v), InterpreterFlowControl::Return(v) => return Ok(v),

View file

@ -11,13 +11,26 @@ use crate::{
/// Evaluate an interpretable, returning its value. /// Evaluate an interpretable, returning its value.
pub fn evaluate(ast: &ast::ProgramNode, vars: ResolvedVariables) -> SloxResult<Value> { pub fn evaluate(ast: &ast::ProgramNode, vars: ResolvedVariables) -> SloxResult<Value> {
let env = Rc::new(RefCell::new(Environment::default())); 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 * * 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 /// Interpreter flow control, which may be either a value, a loop break or a
/// loop continuation. /// loop continuation.
#[derive(Debug)] #[derive(Debug)]
@ -62,7 +75,7 @@ pub(super) type InterpreterResult = SloxResult<InterpreterFlowControl>;
/// An Interpretable can be evaluated and will return a value. /// An Interpretable can be evaluated and will return a value.
pub(super) trait Interpretable { pub(super) trait Interpretable {
fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult; fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult;
} }
/// Generate an error with a static message. /// Generate an error with a static message.
@ -79,9 +92,9 @@ fn error<T>(token: &Token, message: &str) -> SloxResult<T> {
* ----------------------------- */ * ----------------------------- */
impl Interpretable for ast::ProgramNode { impl Interpretable for ast::ProgramNode {
fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult {
for stmt in self.0.iter() { for stmt in self.0.iter() {
stmt.interpret(environment)?; stmt.interpret(es)?;
} }
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
@ -92,32 +105,32 @@ impl Interpretable for ast::ProgramNode {
* ------------------------------- */ * ------------------------------- */
impl Interpretable for ast::StmtNode { impl Interpretable for ast::StmtNode {
fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult {
match self { 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 } => { 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::Expression(expr) => expr.interpret(es),
ast::StmtNode::Print(expr) => self.on_print(environment, expr), ast::StmtNode::Print(expr) => self.on_print(es, expr),
ast::StmtNode::Block(statements) => self.on_block(environment, statements), ast::StmtNode::Block(statements) => self.on_block(es, statements),
ast::StmtNode::If { ast::StmtNode::If {
condition, condition,
then_branch, then_branch,
else_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 { ast::StmtNode::Loop {
label, label,
condition, condition,
body, body,
after_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 { ast::StmtNode::LoopControl {
is_break, is_break,
loop_name, loop_name,
} => self.on_loop_control_statemement(*is_break, loop_name), } => self.on_loop_control_statemement(*is_break, loop_name),
ast::StmtNode::Return { token: _, value } => { 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 { impl ast::StmtNode {
/// Handle the `print` statement. /// Handle the `print` statement.
fn on_print(&self, environment: &EnvironmentRef, expr: &ast::ExprNode) -> InterpreterResult { fn on_print(&self, es: &mut InterpreterState, expr: &ast::ExprNode) -> InterpreterResult {
let value = expr.interpret(environment)?.result(); let value = expr.interpret(es)?.result();
let output = match value { let output = match value {
Value::Nil => String::from("nil"), Value::Nil => String::from("nil"),
Value::Boolean(true) => String::from("true"), Value::Boolean(true) => String::from("true"),
@ -142,38 +155,42 @@ impl ast::StmtNode {
/// Handle a variable declaration. /// Handle a variable declaration.
fn on_var_decl( fn on_var_decl(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
name: &Token, name: &Token,
initializer: &Option<ast::ExprNode>, initializer: &Option<ast::ExprNode>,
) -> InterpreterResult { ) -> InterpreterResult {
let variable = match initializer { let variable = match initializer {
Some(expr) => Some(expr.interpret(environment)?.result()), Some(expr) => Some(expr.interpret(es)?.result()),
None => None, None => None,
}; };
environment.borrow_mut().define(name, variable)?; es.environment.borrow_mut().define(name, variable)?;
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
/// Handle a function declaration. /// Handle a function declaration.
fn on_fun_decl( fn on_fun_decl(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
name: &Token, name: &Token,
params: &[Token], params: &[Token],
body: &[ast::StmtNode], body: &[ast::StmtNode],
) -> InterpreterResult { ) -> InterpreterResult {
let fun = Function::new(Some(name), params, body); let fun = Function::new(Some(name), params, body);
environment es.environment
.borrow_mut() .borrow_mut()
.define(name, Some(Value::Callable(fun)))?; .define(name, Some(Value::Callable(fun)))?;
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
/// Execute the contents of a block. /// Execute the contents of a block.
fn on_block(&self, environment: &EnvironmentRef, stmts: &[ast::StmtNode]) -> InterpreterResult { fn on_block(&self, es: &mut InterpreterState, stmts: &[ast::StmtNode]) -> InterpreterResult {
let child = Environment::create_child(environment); let mut child = InterpreterState{
environment: Environment::create_child(&es.environment),
globals: es.globals.clone(),
variables: es.variables,
};
for stmt in stmts.iter() { for stmt in stmts.iter() {
let result = stmt.interpret(&child)?; let result = stmt.interpret(&mut child)?;
if result.is_flow_control() { if result.is_flow_control() {
return Ok(result); return Ok(result);
} }
@ -184,15 +201,15 @@ impl ast::StmtNode {
/// Execute an if statement. /// Execute an if statement.
fn on_if_statement( fn on_if_statement(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
condition: &ast::ExprNode, condition: &ast::ExprNode,
then_branch: &ast::StmtNode, then_branch: &ast::StmtNode,
else_branch: &Option<Box<ast::StmtNode>>, else_branch: &Option<Box<ast::StmtNode>>,
) -> InterpreterResult { ) -> InterpreterResult {
if condition.interpret(environment)?.result().is_truthy() { if condition.interpret(es)?.result().is_truthy() {
then_branch.interpret(environment) then_branch.interpret(es)
} else if let Some(else_stmt) = else_branch { } else if let Some(else_stmt) = else_branch {
else_stmt.interpret(environment) else_stmt.interpret(es)
} else { } else {
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
@ -201,15 +218,15 @@ impl ast::StmtNode {
/// Execute a while statement. /// Execute a while statement.
fn on_loop_statement( fn on_loop_statement(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
label: &Option<Token>, label: &Option<Token>,
condition: &ast::ExprNode, condition: &ast::ExprNode,
body: &ast::StmtNode, body: &ast::StmtNode,
after_body: &Option<Box<ast::StmtNode>>, after_body: &Option<Box<ast::StmtNode>>,
) -> InterpreterResult { ) -> InterpreterResult {
let ln = label.as_ref().map(|token| token.lexeme.clone()); let ln = label.as_ref().map(|token| token.lexeme.clone());
while condition.interpret(environment)?.result().is_truthy() { while condition.interpret(es)?.result().is_truthy() {
let result = body.interpret(environment)?; let result = body.interpret(es)?;
match &result { match &result {
InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Result(_) => (),
InterpreterFlowControl::Continue(lv) if lv == &ln => (), InterpreterFlowControl::Continue(lv) if lv == &ln => (),
@ -217,7 +234,7 @@ impl ast::StmtNode {
_ => return Ok(result), _ => return Ok(result),
} }
if let Some(stmt) = after_body { if let Some(stmt) = after_body {
let result = stmt.interpret(environment)?; let result = stmt.interpret(es)?;
match &result { match &result {
InterpreterFlowControl::Result(_) => (), InterpreterFlowControl::Result(_) => (),
InterpreterFlowControl::Continue(lv) if lv == &ln => (), InterpreterFlowControl::Continue(lv) if lv == &ln => (),
@ -246,12 +263,12 @@ impl ast::StmtNode {
/// Execute a return statement. /// Execute a return statement.
fn on_return_statement( fn on_return_statement(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
value: &Option<ast::ExprNode>, value: &Option<ast::ExprNode>,
) -> InterpreterResult { ) -> InterpreterResult {
let rv = match value { let rv = match value {
None => Value::Nil, None => Value::Nil,
Some(expr) => expr.interpret(environment)?.result(), Some(expr) => expr.interpret(es)?.result(),
}; };
Ok(InterpreterFlowControl::Return(rv)) Ok(InterpreterFlowControl::Return(rv))
} }
@ -262,32 +279,32 @@ impl ast::StmtNode {
* -------------------------------- */ * -------------------------------- */
impl Interpretable for ast::ExprNode { impl Interpretable for ast::ExprNode {
fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult { fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult {
match self { match self {
ast::ExprNode::Assignment { name, value } => { ast::ExprNode::Assignment { name, value } => {
let value = value.interpret(environment)?.result(); let value = value.interpret(es)?.result();
environment.borrow_mut().assign(name, value)?; es.environment.borrow_mut().assign(name, value)?;
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
ast::ExprNode::Logical { ast::ExprNode::Logical {
left, left,
operator, operator,
right, right,
} => self.on_logic(environment, left, operator, right), } => self.on_logic(es, left, operator, right),
ast::ExprNode::Binary { ast::ExprNode::Binary {
left, left,
operator, operator,
right, right,
} => self.on_binary(environment, left, operator, right), } => self.on_binary(es, left, operator, right),
ast::ExprNode::Unary { operator, right } => self.on_unary(environment, operator, right), ast::ExprNode::Unary { operator, right } => self.on_unary(es, operator, right),
ast::ExprNode::Grouping { expression } => expression.interpret(environment), ast::ExprNode::Grouping { expression } => expression.interpret(es),
ast::ExprNode::Litteral { value } => self.on_litteral(value), 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 { ast::ExprNode::Call {
callee, callee,
right_paren, right_paren,
arguments, arguments,
} => self.on_call(environment, callee, right_paren, arguments), } => self.on_call(es, callee, right_paren, arguments),
ast::ExprNode::Lambda { params, body } => { ast::ExprNode::Lambda { params, body } => {
Ok(Value::Callable(Function::new(None, params, body)).into()) Ok(Value::Callable(Function::new(None, params, body)).into())
} }
@ -299,31 +316,31 @@ impl ast::ExprNode {
/// Evaluate a logical operator. /// Evaluate a logical operator.
fn on_logic( fn on_logic(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
left: &ast::ExprNode, left: &ast::ExprNode,
operator: &Token, operator: &Token,
right: &ast::ExprNode, right: &ast::ExprNode,
) -> InterpreterResult { ) -> 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() if operator.token_type == TokenType::Or && left_value.is_truthy()
|| operator.token_type == TokenType::And && !left_value.is_truthy() || operator.token_type == TokenType::And && !left_value.is_truthy()
{ {
Ok(left_value.into()) Ok(left_value.into())
} else { } else {
right.interpret(environment) right.interpret(es)
} }
} }
/// Evaluate a binary operator. /// Evaluate a binary operator.
fn on_binary( fn on_binary(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
left: &ast::ExprNode, left: &ast::ExprNode,
operator: &Token, operator: &Token,
right: &ast::ExprNode, right: &ast::ExprNode,
) -> InterpreterResult { ) -> InterpreterResult {
let left_value = left.interpret(environment)?.result(); let left_value = left.interpret(es)?.result();
let right_value = right.interpret(environment)?.result(); let right_value = right.interpret(es)?.result();
match operator.token_type { match operator.token_type {
TokenType::Plus => match (left_value, right_value) { TokenType::Plus => match (left_value, right_value) {
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b).into()), (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b).into()),
@ -388,11 +405,11 @@ impl ast::ExprNode {
/// Evaluate an unary operator. /// Evaluate an unary operator.
fn on_unary( fn on_unary(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
operator: &Token, operator: &Token,
right: &ast::ExprNode, right: &ast::ExprNode,
) -> InterpreterResult { ) -> InterpreterResult {
let right_value = right.interpret(environment)?.result(); let right_value = right.interpret(es)?.result();
match operator.token_type { match operator.token_type {
TokenType::Minus => { TokenType::Minus => {
if let Value::Number(n) = right_value { if let Value::Number(n) = right_value {
@ -427,16 +444,16 @@ impl ast::ExprNode {
/// Evaluate a function call. /// Evaluate a function call.
fn on_call( fn on_call(
&self, &self,
environment: &EnvironmentRef, es: &mut InterpreterState,
callee: &ast::ExprNode, callee: &ast::ExprNode,
right_paren: &Token, right_paren: &Token,
arguments: &Vec<ast::ExprNode>, arguments: &Vec<ast::ExprNode>,
) -> InterpreterResult { ) -> InterpreterResult {
let callee = callee.interpret(environment)?.result(); let callee = callee.interpret(es)?.result();
let arg_values = { let arg_values = {
let mut v = Vec::with_capacity(arguments.len()); let mut v = Vec::with_capacity(arguments.len());
for argument in arguments.iter() { for argument in arguments.iter() {
v.push(argument.interpret(environment)?.result()); v.push(argument.interpret(es)?.result());
} }
v v
}; };
@ -453,7 +470,7 @@ impl ast::ExprNode {
), ),
)) ))
} else { } else {
Ok(callable.call(environment, arg_values)?.into()) Ok(callable.call(es, arg_values)?.into())
} }
} else { } else {
error(right_paren, "can only call functions and classes") error(right_paren, "can only call functions and classes")

View file

@ -5,7 +5,7 @@ mod interpretable;
mod native_fn; mod native_fn;
mod value; mod value;
pub use callable::{Callable, CallableRef}; pub(self) use callable::{Callable, CallableRef};
pub use environment::*; pub(self) use environment::*;
pub use interpretable::*; pub use interpretable::*;
pub use value::*; pub use value::*;

View file

@ -6,7 +6,7 @@ use std::{
use crate::errors::SloxResult; use crate::errors::SloxResult;
use super::{Callable, CallableRef, EnvironmentRef, Value}; use super::{Callable, CallableRef, InterpreterState, Value};
/* ----------- * /* ----------- *
* clock() * * clock() *
@ -20,7 +20,7 @@ impl Callable for Clock {
0 0
} }
fn call(&self, _environment: &EnvironmentRef, _arguments: Vec<Value>) -> SloxResult<Value> { fn call(&self, _environment: &mut InterpreterState, _arguments: Vec<Value>) -> SloxResult<Value> {
let now = SystemTime::now(); let now = SystemTime::now();
let since_epoch = now let since_epoch = now
.duration_since(UNIX_EPOCH) .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 {})) Rc::new(RefCell::new(Clock {}))
} }