Interpreter - Refactored method handling to prepare for getters/setters
This commit is contained in:
parent
1272b5ec89
commit
c372979009
2 changed files with 50 additions and 58 deletions
|
@ -1,6 +1,9 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
ast::ClassMemberKind,
|
||||||
errors::{ErrorKind, SloxError, SloxResult},
|
errors::{ErrorKind, SloxError, SloxResult},
|
||||||
tokens::Token,
|
tokens::Token,
|
||||||
};
|
};
|
||||||
|
@ -14,12 +17,14 @@ pub trait PropertyCarrier {
|
||||||
fn set(&self, name: &Token, value: Value);
|
fn set(&self, name: &Token, value: Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The key for the table of class members.
|
||||||
|
pub type ClassMemberKey = (ClassMemberKind, bool, String);
|
||||||
|
|
||||||
/// A Lox class.
|
/// A Lox class.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
name: String,
|
name: String,
|
||||||
methods: HashMap<String, Function>,
|
members: HashMap<ClassMemberKey, Function>,
|
||||||
static_methods: HashMap<String, Function>,
|
|
||||||
fields: RefCell<HashMap<String, Value>>,
|
fields: RefCell<HashMap<String, Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +45,11 @@ pub type InstanceRef = Rc<RefCell<Instance>>;
|
||||||
* Class implementation *
|
* Class implementation *
|
||||||
* -------------------- */
|
* -------------------- */
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref INIT_METHOD_KEY: ClassMemberKey =
|
||||||
|
(ClassMemberKind::Method, false, String::from("init"));
|
||||||
|
}
|
||||||
|
|
||||||
fn bind_method(method: &Function, this_value: Value) -> Function {
|
fn bind_method(method: &Function, this_value: Value) -> Function {
|
||||||
let bm = method.copy_with_child_env();
|
let bm = method.copy_with_child_env();
|
||||||
bm.env().set("this", this_value);
|
bm.env().set("this", this_value);
|
||||||
|
@ -48,15 +58,10 @@ fn bind_method(method: &Function, this_value: Value) -> Function {
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
/// Create a new class, specifying its name.
|
/// Create a new class, specifying its name.
|
||||||
pub fn new(
|
pub fn new(name: String, members: HashMap<ClassMemberKey, Function>) -> Self {
|
||||||
name: String,
|
|
||||||
methods: HashMap<String, Function>,
|
|
||||||
static_methods: HashMap<String, Function>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
methods,
|
members,
|
||||||
static_methods,
|
|
||||||
fields: RefCell::new(HashMap::default()),
|
fields: RefCell::new(HashMap::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +77,7 @@ impl Display for Class {
|
||||||
|
|
||||||
impl Callable for ClassRef {
|
impl Callable for ClassRef {
|
||||||
fn arity(&self) -> usize {
|
fn arity(&self) -> usize {
|
||||||
if let Some(init) = self.borrow().methods.get("init") {
|
if let Some(init) = self.borrow().members.get(&INIT_METHOD_KEY) {
|
||||||
init.arity()
|
init.arity()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
@ -81,7 +86,7 @@ impl Callable for ClassRef {
|
||||||
|
|
||||||
fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
|
fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
|
||||||
let inst_value = Value::from(Instance::new(self.clone()));
|
let inst_value = Value::from(Instance::new(self.clone()));
|
||||||
if let Some(init) = self.borrow().methods.get("init") {
|
if let Some(init) = self.borrow().members.get(&INIT_METHOD_KEY) {
|
||||||
inst_value.with_instance(
|
inst_value.with_instance(
|
||||||
|_| {
|
|_| {
|
||||||
let bound_init = bind_method(init, inst_value.clone());
|
let bound_init = bind_method(init, inst_value.clone());
|
||||||
|
@ -98,10 +103,11 @@ impl Callable for ClassRef {
|
||||||
impl PropertyCarrier for ClassRef {
|
impl PropertyCarrier for ClassRef {
|
||||||
fn get(&self, name: &Token) -> SloxResult<Value> {
|
fn get(&self, name: &Token) -> SloxResult<Value> {
|
||||||
let class = self.borrow();
|
let class = self.borrow();
|
||||||
|
let mut mb_key = (ClassMemberKind::Method, true, name.lexeme.clone());
|
||||||
if let Some(value) = class.fields.borrow().get(&name.lexeme) {
|
if let Some(value) = class.fields.borrow().get(&name.lexeme) {
|
||||||
return Ok(value.clone());
|
return Ok(value.clone());
|
||||||
}
|
}
|
||||||
if let Some(method) = class.static_methods.get(&name.lexeme) {
|
if let Some(method) = class.members.get(&mb_key) {
|
||||||
let bound_method = bind_method(method, Value::from(self.clone()));
|
let bound_method = bind_method(method, Value::from(self.clone()));
|
||||||
return Ok(Value::from(bound_method));
|
return Ok(Value::from(bound_method));
|
||||||
}
|
}
|
||||||
|
@ -141,10 +147,11 @@ impl Display for Instance {
|
||||||
impl PropertyCarrier for InstanceRef {
|
impl PropertyCarrier for InstanceRef {
|
||||||
fn get(&self, name: &Token) -> SloxResult<Value> {
|
fn get(&self, name: &Token) -> SloxResult<Value> {
|
||||||
let instance = self.borrow();
|
let instance = self.borrow();
|
||||||
|
let mut mb_key = (ClassMemberKind::Method, false, name.lexeme.clone());
|
||||||
if let Some(value) = instance.fields.borrow().get(&name.lexeme) {
|
if let Some(value) = instance.fields.borrow().get(&name.lexeme) {
|
||||||
return Ok(value.clone());
|
return Ok(value.clone());
|
||||||
}
|
}
|
||||||
if let Some(method) = instance.class.borrow().methods.get(&name.lexeme) {
|
if let Some(method) = instance.class.borrow().members.get(&mb_key) {
|
||||||
let bound_method = bind_method(method, Value::from(self.clone()));
|
let bound_method = bind_method(method, Value::from(self.clone()));
|
||||||
return Ok(Value::from(bound_method));
|
return Ok(Value::from(bound_method));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ use crate::{
|
||||||
tokens::{Token, TokenType},
|
tokens::{Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{classes::Class, functions::Function, Environment, EnvironmentRef, Value};
|
use super::{
|
||||||
|
classes::{Class, ClassMemberKey},
|
||||||
|
functions::Function,
|
||||||
|
Environment, EnvironmentRef, Value,
|
||||||
|
};
|
||||||
|
|
||||||
/// Evaluate an interpretable, returning its value.
|
/// Evaluate an interpretable, returning its value.
|
||||||
pub fn evaluate(ast: &ProgramNode, vars: ResolvedVariables) -> SloxResult<Value> {
|
pub fn evaluate(ast: &ProgramNode, vars: ResolvedVariables) -> SloxResult<Value> {
|
||||||
|
@ -173,6 +177,30 @@ impl Interpretable for StmtNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract members from a class declaration, generating a map of
|
||||||
|
/// functions.
|
||||||
|
fn extract_members(
|
||||||
|
es: &mut InterpreterState,
|
||||||
|
decl: &ClassDecl,
|
||||||
|
) -> HashMap<ClassMemberKey, Function> {
|
||||||
|
decl.members
|
||||||
|
.iter()
|
||||||
|
.map(|member| {
|
||||||
|
let fnd = &member.fun_decl;
|
||||||
|
(
|
||||||
|
(member.kind, member.is_static, fnd.name.lexeme.clone()),
|
||||||
|
Function::new(
|
||||||
|
Some(&fnd.name),
|
||||||
|
&fnd.params,
|
||||||
|
&fnd.body,
|
||||||
|
es.environment.clone(),
|
||||||
|
fnd.name.lexeme == "init",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
impl StmtNode {
|
impl StmtNode {
|
||||||
/// Handle the `print` statement.
|
/// Handle the `print` statement.
|
||||||
fn on_print(&self, es: &mut InterpreterState, expr: &ExprNode) -> InterpreterResult {
|
fn on_print(&self, es: &mut InterpreterState, expr: &ExprNode) -> InterpreterResult {
|
||||||
|
@ -197,53 +225,10 @@ impl StmtNode {
|
||||||
Ok(InterpreterFlowControl::default())
|
Ok(InterpreterFlowControl::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract members from a class declaration, generating a map of
|
|
||||||
/// functions.
|
|
||||||
fn extract_members<F>(
|
|
||||||
&self,
|
|
||||||
es: &mut InterpreterState,
|
|
||||||
decl: &ClassDecl,
|
|
||||||
filter: F,
|
|
||||||
) -> HashMap<String, Function>
|
|
||||||
where
|
|
||||||
F: FnMut(&ClassMemberDecl) -> Option<(&FunDecl, &[Token])>,
|
|
||||||
{
|
|
||||||
decl.members
|
|
||||||
.iter()
|
|
||||||
.filter_map(filter)
|
|
||||||
.map(|(fdecl, params)| {
|
|
||||||
(
|
|
||||||
fdecl.name.lexeme.clone(),
|
|
||||||
Function::new(
|
|
||||||
Some(&fdecl.name),
|
|
||||||
¶ms,
|
|
||||||
&fdecl.body,
|
|
||||||
es.environment.clone(),
|
|
||||||
fdecl.name.lexeme == "init",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<String, Function>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle a class declaration
|
/// Handle a class declaration
|
||||||
fn on_class_decl(&self, es: &mut InterpreterState, decl: &ClassDecl) -> InterpreterResult {
|
fn on_class_decl(&self, es: &mut InterpreterState, decl: &ClassDecl) -> InterpreterResult {
|
||||||
es.environment.borrow_mut().define(&decl.name, None)?;
|
es.environment.borrow_mut().define(&decl.name, None)?;
|
||||||
let methods =
|
let class = Class::new(decl.name.lexeme.clone(), extract_members(es, decl));
|
||||||
self.extract_members(es, decl, |member| match (&member.kind, member.is_static) {
|
|
||||||
(ClassMemberKind::Method, false) => {
|
|
||||||
Some((&member.fun_decl, &member.fun_decl.params))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
let static_methods =
|
|
||||||
self.extract_members(es, decl, |member| match (&member.kind, member.is_static) {
|
|
||||||
(ClassMemberKind::Method, true) => {
|
|
||||||
Some((&member.fun_decl, &member.fun_decl.params))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
});
|
|
||||||
let class = Class::new(decl.name.lexeme.clone(), methods, static_methods);
|
|
||||||
es.environment
|
es.environment
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.assign(&decl.name, class.into())?;
|
.assign(&decl.name, class.into())?;
|
||||||
|
|
Loading…
Reference in a new issue