rust-crafting-interpreters-.../src/interpreter/classes.rs

322 lines
9.3 KiB
Rust

use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
use lazy_static::lazy_static;
use crate::{
ast::{ClassMemberKind, SuperExpr},
errors::{ErrorKind, SloxError, SloxResult},
tokens::Token,
};
use super::{functions::Function, Callable, InterpreterState, Value};
/// This trait represents an object on which getters and setters
/// must be supported.
pub trait PropertyCarrier {
fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value>;
fn set(&self, itpr_state: &mut InterpreterState, name: &Token, value: Value);
fn get_super(
&self,
itpr_state: &mut InterpreterState,
super_expr: &SuperExpr,
distance: usize,
) -> SloxResult<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,
superclass: Option<ClassRef>,
members: HashMap<ClassMemberKey, Function>,
fields: RefCell<HashMap<String, Value>>,
}
/// Classes are mostly used through references.
pub type ClassRef = Rc<RefCell<Class>>;
/// An instance of a Lox class
#[derive(Debug, Clone)]
pub struct Instance {
class: ClassRef,
fields: RefCell<HashMap<String, Value>>,
}
/// Helper type used to refer to instances.
pub type InstanceRef = Rc<RefCell<Instance>>;
/* --------------- *
* Various helpers *
* --------------- */
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);
bm
}
fn get_super<T>(
on_ref: &T,
is_static: bool,
itpr_state: &mut InterpreterState,
super_expr: &SuperExpr,
distance: usize,
) -> SloxResult<Value>
where
T: Clone + Into<Value>,
{
let mb_key = (
ClassMemberKind::Method,
is_static,
super_expr.method.lexeme.clone(),
);
let sc_value = itpr_state
.environment
.borrow()
.get_at(distance, &super_expr.keyword.token)?;
let class = sc_value.as_class_ref().expect("class reference expected");
if let Some(method) = with_class_member(&class, &mb_key, |method| {
let bound_method = bind_method(method, on_ref.clone().into());
Ok(Value::from(bound_method))
}) {
method
} else {
Err(SloxError::with_token(
ErrorKind::Runtime,
&super_expr.method,
"undefined property".to_owned(),
))
}
}
/* -------------------- *
* Class implementation *
* -------------------- */
impl Class {
/// Create a new class, specifying its name.
pub fn new(
name: String,
superclass: Option<ClassRef>,
members: HashMap<ClassMemberKey, Function>,
) -> Self {
Self {
name,
superclass,
members,
fields: RefCell::new(HashMap::default()),
}
}
}
impl Display for Class {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("<class ")?;
f.write_str(&self.name)?;
f.write_str(">")
}
}
impl Callable for ClassRef {
fn arity(&self) -> usize {
if let Some(init) = self.borrow().members.get(&INIT_METHOD_KEY) {
init.arity()
} else {
0
}
}
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().members.get(&INIT_METHOD_KEY) {
inst_value.with_instance(
|_| {
let bound_init = bind_method(init, inst_value.clone());
bound_init.call(itpr_state, arguments)
},
|| panic!("Instance is not an instance, wtf"),
)
} else {
Ok(inst_value)
}
}
}
/// Attempt to find some member, identified by a key, inside a class hierarchy. If the member is
/// found, the specified function is exectued with the member as its argument, and its return value
/// is passed back to the caller. If the member is not found, None is returned.
fn with_class_member<F, Rt>(class: &ClassRef, mb_key: &ClassMemberKey, f: F) -> Option<Rt>
where
F: FnOnce(&Function) -> Rt,
{
let mut cls = class.clone();
loop {
if let Some(member) = cls.borrow().members.get(mb_key) {
return Some(f(member));
}
let nclr = if let Some(sc) = &cls.borrow().superclass {
sc.clone()
} else {
return None;
};
cls = nclr;
}
}
impl PropertyCarrier for ClassRef {
fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> {
let class = self.borrow();
// Check for a property getter and execute it if found.
let mut mb_key = (ClassMemberKind::Getter, true, name.lexeme.clone());
if let Some(value) = with_class_member(self, &mb_key, |getter| {
let bound_method = bind_method(getter, Value::from(self.clone()));
bound_method.call(itpr_state, vec![])
}) {
return value;
}
// Check for an actual field.
if let Some(value) = class.fields.borrow().get(&name.lexeme) {
return Ok(value.clone());
}
// Check for a method.
mb_key.0 = ClassMemberKind::Method;
if let Some(method) = with_class_member(self, &mb_key, |method| {
let bound_method = bind_method(method, Value::from(self.clone()));
Ok(Value::from(bound_method))
}) {
return method;
}
Err(SloxError::with_token(
ErrorKind::Runtime,
name,
"undefined property".to_owned(),
))
}
fn set(&self, itpr_state: &mut InterpreterState, name: &Token, value: Value) {
let class = self.borrow();
// Check for a property setter.
let mb_key = (ClassMemberKind::Setter, true, name.lexeme.clone());
let lookup = with_class_member(self, &mb_key, |setter| {
let bound_method = bind_method(setter, Value::from(self.clone()));
bound_method
.call(itpr_state, vec![value.clone()])
.expect("failed to execute setter");
});
if lookup.is_some() {
return;
}
// Set the property directly
class.fields.borrow_mut().insert(name.lexeme.clone(), value);
}
fn get_super(
&self,
itpr_state: &mut InterpreterState,
super_expr: &SuperExpr,
distance: usize,
) -> SloxResult<Value> {
get_super(self, true, itpr_state, super_expr, distance)
}
}
/* ----------------------- *
* Instance implementation *
* ----------------------- */
impl Instance {
fn new(class: ClassRef) -> Self {
Self {
class,
fields: RefCell::new(HashMap::default()),
}
}
}
impl Display for Instance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("<Instance of {}>", self.class.borrow(),))
}
}
impl PropertyCarrier for InstanceRef {
fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> {
let instance = self.borrow();
// Check for a property getter and execute it if found.
let mut mb_key = (ClassMemberKind::Getter, false, name.lexeme.clone());
if let Some(value) = with_class_member(&instance.class, &mb_key, |getter| {
let bound_method = bind_method(getter, Value::from(self.clone()));
bound_method.call(itpr_state, vec![])
}) {
return value;
}
// Check for an actual field.
if let Some(value) = instance.fields.borrow().get(&name.lexeme) {
return Ok(value.clone());
}
// Check for a method.
mb_key.0 = ClassMemberKind::Method;
if let Some(method) = with_class_member(&instance.class, &mb_key, |method| {
let bound_method = bind_method(method, Value::from(self.clone()));
Ok(Value::from(bound_method))
}) {
return method;
}
Err(SloxError::with_token(
ErrorKind::Runtime,
name,
"undefined property".to_owned(),
))
}
fn set(&self, itpr_state: &mut InterpreterState, name: &Token, value: Value) {
let instance = self.borrow();
// Check for a property setter.
let mb_key = (ClassMemberKind::Setter, false, name.lexeme.clone());
let lookup = with_class_member(&instance.class, &mb_key, |setter| {
let bound_method = bind_method(setter, Value::from(self.clone()));
bound_method
.call(itpr_state, vec![value.clone()])
.expect("failed to execute setter");
});
if lookup.is_some() {
return;
}
// Set the property directly
instance
.fields
.borrow_mut()
.insert(name.lexeme.clone(), value);
}
fn get_super(
&self,
itpr_state: &mut InterpreterState,
super_expr: &SuperExpr,
distance: usize,
) -> SloxResult<Value> {
get_super(self, false, itpr_state, super_expr, distance)
}
}