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) } }