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 lazy_static::lazy_static;
|
||||
|
||||
use crate::{
|
||||
ast::ClassMemberKind,
|
||||
errors::{ErrorKind, SloxError, SloxResult},
|
||||
tokens::Token,
|
||||
};
|
||||
|
@ -14,12 +17,14 @@ pub trait PropertyCarrier {
|
|||
fn set(&self, name: &Token, value: Value);
|
||||
}
|
||||
|
||||
/// The key for the table of class members.
|
||||
pub type ClassMemberKey = (ClassMemberKind, bool, String);
|
||||
|
||||
/// A Lox class.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Class {
|
||||
name: String,
|
||||
methods: HashMap<String, Function>,
|
||||
static_methods: HashMap<String, Function>,
|
||||
members: HashMap<ClassMemberKey, Function>,
|
||||
fields: RefCell<HashMap<String, Value>>,
|
||||
}
|
||||
|
||||
|
@ -40,6 +45,11 @@ pub type InstanceRef = Rc<RefCell<Instance>>;
|
|||
* 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 {
|
||||
let bm = method.copy_with_child_env();
|
||||
bm.env().set("this", this_value);
|
||||
|
@ -48,15 +58,10 @@ fn bind_method(method: &Function, this_value: Value) -> Function {
|
|||
|
||||
impl Class {
|
||||
/// Create a new class, specifying its name.
|
||||
pub fn new(
|
||||
name: String,
|
||||
methods: HashMap<String, Function>,
|
||||
static_methods: HashMap<String, Function>,
|
||||
) -> Self {
|
||||
pub fn new(name: String, members: HashMap<ClassMemberKey, Function>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
methods,
|
||||
static_methods,
|
||||
members,
|
||||
fields: RefCell::new(HashMap::default()),
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +77,7 @@ impl Display for Class {
|
|||
|
||||
impl Callable for ClassRef {
|
||||
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()
|
||||
} else {
|
||||
0
|
||||
|
@ -81,7 +86,7 @@ impl Callable for ClassRef {
|
|||
|
||||
fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec<Value>) -> SloxResult<Value> {
|
||||
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(
|
||||
|_| {
|
||||
let bound_init = bind_method(init, inst_value.clone());
|
||||
|
@ -98,10 +103,11 @@ impl Callable for ClassRef {
|
|||
impl PropertyCarrier for ClassRef {
|
||||
fn get(&self, name: &Token) -> SloxResult<Value> {
|
||||
let class = self.borrow();
|
||||
let mut mb_key = (ClassMemberKind::Method, true, name.lexeme.clone());
|
||||
if let Some(value) = class.fields.borrow().get(&name.lexeme) {
|
||||
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()));
|
||||
return Ok(Value::from(bound_method));
|
||||
}
|
||||
|
@ -141,10 +147,11 @@ impl Display for Instance {
|
|||
impl PropertyCarrier for InstanceRef {
|
||||
fn get(&self, name: &Token) -> SloxResult<Value> {
|
||||
let instance = self.borrow();
|
||||
let mut mb_key = (ClassMemberKind::Method, false, name.lexeme.clone());
|
||||
if let Some(value) = instance.fields.borrow().get(&name.lexeme) {
|
||||
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()));
|
||||
return Ok(Value::from(bound_method));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@ use crate::{
|
|||
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.
|
||||
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 {
|
||||
/// Handle the `print` statement.
|
||||
fn on_print(&self, es: &mut InterpreterState, expr: &ExprNode) -> InterpreterResult {
|
||||
|
@ -197,53 +225,10 @@ impl StmtNode {
|
|||
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
|
||||
fn on_class_decl(&self, es: &mut InterpreterState, decl: &ClassDecl) -> InterpreterResult {
|
||||
es.environment.borrow_mut().define(&decl.name, None)?;
|
||||
let methods =
|
||||
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);
|
||||
let class = Class::new(decl.name.lexeme.clone(), extract_members(es, decl));
|
||||
es.environment
|
||||
.borrow_mut()
|
||||
.assign(&decl.name, class.into())?;
|
||||
|
|
Loading…
Reference in a new issue