From 0ebe2e9f2244c8fe9854f51f39c6ba1185825508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Thu, 12 Jan 2023 07:39:12 +0100 Subject: [PATCH] Interpreter - Class properties --- src/interpreter/classes.rs | 35 ++++++++++++++++++++++++++++++-- src/interpreter/interpretable.rs | 4 ++-- src/interpreter/value.rs | 21 ++++++++++++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/interpreter/classes.rs b/src/interpreter/classes.rs index d521f17..4a23f90 100644 --- a/src/interpreter/classes.rs +++ b/src/interpreter/classes.rs @@ -19,6 +19,7 @@ pub trait PropertyCarrier { pub struct Class { name: String, methods: HashMap, + fields: RefCell>, } /// Classes are mostly used through references. @@ -47,7 +48,11 @@ 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) -> Self { - Self { name, methods } + Self { + name, + methods, + fields: RefCell::new(HashMap::default()), + } } } @@ -72,7 +77,7 @@ impl Callable for ClassRef { let inst_value = Value::from(Instance::new(self.clone())); if let Some(init) = self.borrow().methods.get("init") { inst_value.with_instance( - |instance| { + |_| { let bound_init = bind_method(init, inst_value.clone()); bound_init.call(itpr_state, arguments) }, @@ -84,6 +89,32 @@ impl Callable for ClassRef { } } +impl PropertyCarrier for ClassRef { + fn get(&self, name: &Token) -> SloxResult { + let class = self.borrow(); + if let Some(value) = class.fields.borrow().get(&name.lexeme) { + return Ok(value.clone()); + } + /* + if let Some(method) = class.methods.get(&name.lexeme) { + let bound_method = bind_method(method, Value::from(self.clone())); + return Ok(Value::from(bound_method)); + } + */ + + Err(SloxError::with_token( + ErrorKind::Runtime, + name, + "undefined property".to_owned(), + )) + } + + fn set(&self, name: &Token, value: Value) { + let class = self.borrow(); + class.fields.borrow_mut().insert(name.lexeme.clone(), value); + } +} + /* ----------------------- * * Instance implementation * * ----------------------- */ diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs index 5ec6ff1..a6016b3 100644 --- a/src/interpreter/interpretable.rs +++ b/src/interpreter/interpretable.rs @@ -540,7 +540,7 @@ impl ExprNode { get_expr: &GetExpr, ) -> InterpreterResult { let instance = get_expr.instance.interpret(itpr_state)?.result(); - instance.with_instance( + instance.with_property_carrier( |inst| inst.get(&get_expr.name).map(|v| v.into()), || error(&get_expr.name, "only instances have properties"), ) @@ -553,7 +553,7 @@ impl ExprNode { set_expr: &SetExpr, ) -> InterpreterResult { let instance = set_expr.instance.interpret(itpr_state)?.result(); - instance.with_instance( + instance.with_property_carrier( |instance| { let value = set_expr.value.interpret(itpr_state)?.result(); instance.set(&set_expr.name, value.clone()); diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 2d97812..a76336e 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, fmt::Display, rc::Rc}; use super::{ - classes::{Class, ClassRef, Instance, InstanceRef}, + classes::{Class, ClassRef, Instance, InstanceRef, PropertyCarrier}, functions::Function, native_fn::NativeFunction, Callable, @@ -75,6 +75,25 @@ impl Value { _ => ferr(), } } + + /// Run some code against a property carrier value (either an instance + /// or a class). If the value does not contain such an object, an error + /// function will be called instead. + pub fn with_property_carrier(&self, fok: Fok, ferr: Ferr) -> Rt + where + Fok: FnOnce(&dyn PropertyCarrier) -> Rt, + Ferr: FnOnce() -> Rt, + { + let obj = match self { + Value::Object(obj_ref) => obj_ref.borrow(), + _ => return ferr(), + }; + match &*obj { + Object::Class(cls) => fok(cls), + Object::Instance(inst) => fok(inst), + _ => ferr(), + } + } } impl PartialEq for Value {