|
|
@ -3,8 +3,9 @@ use crate::environment::Environments; |
|
|
|
use crate::error_handler::ErrorHandler;
|
|
|
|
use crate::expr::ExprValue::Callable;
|
|
|
|
use crate::expr::{
|
|
|
|
AssignExpr, BinaryExpr, CallExpr, Expr, ExprError, ExprValue, GetExpr, GroupingExpr,
|
|
|
|
LiteralExpr, LogicalExpr, RealExprError, SetExpr, ThisExpr, UnaryExpr, VariableExpr,
|
|
|
|
AssignExpr, BinaryExpr, CallExpr, Expr, ExprContent, ExprError, ExprValue, GetExpr,
|
|
|
|
GroupingExpr, LiteralExpr, LogicalExpr, RealExprError, SetExpr, SuperExpr, ThisExpr, UnaryExpr,
|
|
|
|
VariableExpr,
|
|
|
|
};
|
|
|
|
use crate::lox_class::LoxClass;
|
|
|
|
use crate::lox_function::LoxFunction;
|
|
|
@ -16,6 +17,7 @@ use crate::stmt::{ |
|
|
|
use crate::tokens::{Token, TokenType};
|
|
|
|
use crate::visitor::{ExprVisitor, StmtVisitor};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
pub struct Interpreter {
|
|
|
|
pub environments: Environments,
|
|
|
@ -97,6 +99,32 @@ impl<'a> Interpreter { |
|
|
|
None => self.environments.get(0, &name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_callable_expr(
|
|
|
|
&mut self,
|
|
|
|
e: &CallExpr,
|
|
|
|
func: &mut Box<dyn crate::callable::Callable>,
|
|
|
|
) -> Result<ExprValue, ExprError> {
|
|
|
|
{
|
|
|
|
let mut arguments: Vec<ExprValue> = Vec::new();
|
|
|
|
for arg in &e.arguments {
|
|
|
|
arguments.push(self.evaluate(&arg)?);
|
|
|
|
}
|
|
|
|
|
|
|
|
if arguments.len() != func.arity() {
|
|
|
|
return Err(ExprError::Error(RealExprError {
|
|
|
|
token: e.paren.clone(),
|
|
|
|
msg: format!(
|
|
|
|
"Expected {} arguments but got {}.",
|
|
|
|
func.arity(),
|
|
|
|
arguments.len()
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
func.call(self, arguments)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Interpreter {
|
|
|
@ -259,29 +287,15 @@ impl ExprVisitor<Result<ExprValue, ExprError>> for Interpreter { |
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_call_expr(&mut self, _: usize, e: &CallExpr) -> Result<ExprValue, ExprError> {
|
|
|
|
if let Callable(mut func) = self.evaluate(&e.callee.as_ref())? {
|
|
|
|
let mut arguments: Vec<ExprValue> = Vec::new();
|
|
|
|
for arg in &e.arguments {
|
|
|
|
arguments.push(self.evaluate(&arg)?);
|
|
|
|
match self.evaluate(&e.callee.as_ref())? {
|
|
|
|
Callable(mut func) => self.visit_callable_expr(e, &mut func),
|
|
|
|
ExprValue::Class(func) => {
|
|
|
|
self.visit_callable_expr(e, &mut (func as Box<dyn crate::callable::Callable>))
|
|
|
|
}
|
|
|
|
|
|
|
|
if arguments.len() != func.arity() {
|
|
|
|
return Err(ExprError::Error(RealExprError {
|
|
|
|
token: e.paren.clone(),
|
|
|
|
msg: format!(
|
|
|
|
"Expected {} arguments but got {}.",
|
|
|
|
func.arity(),
|
|
|
|
arguments.len()
|
|
|
|
),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
func.call(self, arguments)
|
|
|
|
} else {
|
|
|
|
Err(ExprError::Error(RealExprError {
|
|
|
|
_ => Err(ExprError::Error(RealExprError {
|
|
|
|
token: e.paren.clone(),
|
|
|
|
msg: "Can only call functions and classes.".to_string(),
|
|
|
|
}))
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@ -314,6 +328,37 @@ impl ExprVisitor<Result<ExprValue, ExprError>> for Interpreter { |
|
|
|
fn visit_this_expr(&mut self, id: usize, e: &ThisExpr) -> Result<ExprValue, ExprError> {
|
|
|
|
self.look_up_variable(&e.keyword, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_super_expr(&mut self, id: usize, e: &SuperExpr) -> Result<ExprValue, ExprError> {
|
|
|
|
let distance = self.locals.get(&id).unwrap();
|
|
|
|
let superclass = self
|
|
|
|
.environments
|
|
|
|
.get_at(self.current_env, *distance, "super")?;
|
|
|
|
if let ExprValue::Class(superclass) = superclass {
|
|
|
|
// "this" is always 1 closer than "super"
|
|
|
|
let instance = self
|
|
|
|
.environments
|
|
|
|
.get_at(self.current_env, distance - 1, "this")?;
|
|
|
|
if let ExprValue::Instance(instance) = instance {
|
|
|
|
let method = match superclass.find_method(&e.method.lexeme) {
|
|
|
|
Some(x) => x,
|
|
|
|
None => {
|
|
|
|
return Err(ExprError::Error(RealExprError {
|
|
|
|
token: e.method.clone(),
|
|
|
|
msg: format!("Undefined property '{}'.", e.method.lexeme),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(ExprValue::Callable(Box::new(
|
|
|
|
method.bind(&mut self.environments, instance),
|
|
|
|
)))
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StmtVisitor<Result<(), ExprError>> for Interpreter {
|
|
|
@ -380,9 +425,41 @@ impl StmtVisitor<Result<(), ExprError>> for Interpreter { |
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_class_stmt(&mut self, stmt: &ClassStmt) -> Result<(), ExprError> {
|
|
|
|
// Retrieve & validate the superclass
|
|
|
|
let superclass = if let Some(sc) = &stmt.superclass {
|
|
|
|
// This if statement is always entered
|
|
|
|
if let ExprContent::Variable(sc_var) = &sc.content {
|
|
|
|
let sc_val = self.evaluate(sc)?;
|
|
|
|
match &sc_val {
|
|
|
|
ExprValue::Class(class) => Some(class.deref().to_owned()),
|
|
|
|
_ => {
|
|
|
|
return Err(ExprError::Error(RealExprError {
|
|
|
|
token: sc_var.name.clone(),
|
|
|
|
msg: "Superclass must be a class.".to_string(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
self.environments
|
|
|
|
.define(self.current_env, stmt.name.lexeme.clone(), ExprValue::Nil);
|
|
|
|
|
|
|
|
if let Some(superclass) = &superclass {
|
|
|
|
self.current_env = self.environments.add_child(self.current_env);
|
|
|
|
self.environments.define(
|
|
|
|
self.current_env,
|
|
|
|
"super".to_string(),
|
|
|
|
ExprValue::Class(Box::new(superclass.clone())),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let has_superclass = superclass.is_some();
|
|
|
|
|
|
|
|
let methods: HashMap<String, LoxFunction> = stmt
|
|
|
|
.methods
|
|
|
|
.iter()
|
|
|
@ -394,11 +471,17 @@ impl StmtVisitor<Result<(), ExprError>> for Interpreter { |
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let klass = LoxClass::new(stmt.name.lexeme.clone(), methods);
|
|
|
|
let klass = LoxClass::new(stmt.name.lexeme.clone(), superclass, methods);
|
|
|
|
|
|
|
|
// TODO this leaks memory
|
|
|
|
if has_superclass {
|
|
|
|
self.current_env = self.environments.enclosing(self.current_env);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.environments.assign(
|
|
|
|
self.current_env,
|
|
|
|
&stmt.name,
|
|
|
|
&ExprValue::Callable(Box::new(klass)),
|
|
|
|
&ExprValue::Class(Box::new(klass)),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|