Interpreter - WIP refactor to prepare for using resolved variables
This commit is contained in:
parent
d528ce8dc3
commit
10223cbb4e
6 changed files with 104 additions and 79 deletions
|
@ -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<Value>) -> SloxResult<Value>;
|
||||
fn call(&self, environment: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value>;
|
||||
}
|
||||
|
||||
/// A reference to a callable.
|
||||
pub type CallableRef = Rc<RefCell<dyn Callable>>;
|
||||
pub(super) type CallableRef = Rc<RefCell<dyn Callable>>;
|
||||
|
|
|
@ -8,15 +8,15 @@ use crate::{
|
|||
use super::{native_fn, CallableRef, Value};
|
||||
|
||||
/// A mutable reference to an environment.
|
||||
pub type EnvironmentRef = Rc<RefCell<Environment>>;
|
||||
pub(super) type EnvironmentRef = Rc<RefCell<Environment>>;
|
||||
|
||||
/// A variable.
|
||||
pub type Variable = Option<Value>;
|
||||
pub(super) type Variable = Option<Value>;
|
||||
|
||||
/// The execution environment.
|
||||
#[derive(Debug)]
|
||||
pub struct Environment {
|
||||
enclosing: Option<EnvironmentRef>,
|
||||
pub(super) struct Environment {
|
||||
pub(super) enclosing: Option<EnvironmentRef>,
|
||||
values: HashMap<String, Variable>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Value>) -> SloxResult<Value> {
|
||||
fn call(&self, es: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
|
||||
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),
|
||||
|
|
|
@ -11,13 +11,26 @@ use crate::{
|
|||
/// Evaluate an interpretable, returning its value.
|
||||
pub fn evaluate(ast: &ast::ProgramNode, vars: ResolvedVariables) -> SloxResult<Value> {
|
||||
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<InterpreterFlowControl>;
|
|||
|
||||
/// 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<T>(token: &Token, message: &str) -> SloxResult<T> {
|
|||
* ----------------------------- */
|
||||
|
||||
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<ast::ExprNode>,
|
||||
) -> 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<Box<ast::StmtNode>>,
|
||||
) -> 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<Token>,
|
||||
condition: &ast::ExprNode,
|
||||
body: &ast::StmtNode,
|
||||
after_body: &Option<Box<ast::StmtNode>>,
|
||||
) -> 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<ast::ExprNode>,
|
||||
) -> 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<ast::ExprNode>,
|
||||
) -> 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")
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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<Value>) -> SloxResult<Value> {
|
||||
fn call(&self, _environment: &mut InterpreterState, _arguments: Vec<Value>) -> SloxResult<Value> {
|
||||
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 {}))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue