From 9627f588d2c55cbb35dca37d1ce3fb9fcb15820d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sat, 14 Jan 2023 16:07:40 +0100
Subject: [PATCH] Interpreter - Getter/setter execution

---
 src/interpreter/classes.rs       | 64 ++++++++++++++++++++++++++++----
 src/interpreter/interpretable.rs | 10 ++---
 2 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/src/interpreter/classes.rs b/src/interpreter/classes.rs
index a7cf1ba..7f946e8 100644
--- a/src/interpreter/classes.rs
+++ b/src/interpreter/classes.rs
@@ -13,8 +13,8 @@ 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, name: &Token) -> SloxResult<Value>;
-    fn set(&self, name: &Token, value: Value);
+    fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value>;
+    fn set(&self, itpr_state: &mut InterpreterState, name: &Token, value: Value);
 }
 
 /// The key for the table of class members.
@@ -101,12 +101,23 @@ impl Callable for ClassRef {
 }
 
 impl PropertyCarrier for ClassRef {
-    fn get(&self, name: &Token) -> SloxResult<Value> {
+    fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> {
         let class = self.borrow();
-        let mut mb_key = (ClassMemberKind::Method, true, name.lexeme.clone());
+
+        // Check for a property getter and execute it if found.
+        let mut mb_key = (ClassMemberKind::Getter, true, name.lexeme.clone());
+        if let Some(getter) = class.members.get(&mb_key) {
+            let bound_method = bind_method(getter, Value::from(self.clone()));
+            return bound_method.call(itpr_state, vec![]);
+        }
+
+        // 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) = class.members.get(&mb_key) {
             let bound_method = bind_method(method, Value::from(self.clone()));
             return Ok(Value::from(bound_method));
@@ -119,8 +130,21 @@ impl PropertyCarrier for ClassRef {
         ))
     }
 
-    fn set(&self, name: &Token, value: Value) {
+    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, false, name.lexeme.clone());
+        if let Some(setter) = class.members.get(&mb_key) {
+            // Bind and execute the property setter
+            let bound_method = bind_method(setter, Value::from(self.clone()));
+            bound_method
+                .call(itpr_state, vec![value])
+                .expect("failed to execute setter");
+            return;
+        }
+
+        // Set the property directly
         class.fields.borrow_mut().insert(name.lexeme.clone(), value);
     }
 }
@@ -145,12 +169,23 @@ impl Display for Instance {
 }
 
 impl PropertyCarrier for InstanceRef {
-    fn get(&self, name: &Token) -> SloxResult<Value> {
+    fn get(&self, itpr_state: &mut InterpreterState, name: &Token) -> SloxResult<Value> {
         let instance = self.borrow();
-        let mut mb_key = (ClassMemberKind::Method, false, name.lexeme.clone());
+
+        // Check for a property getter and execute it if found.
+        let mut mb_key = (ClassMemberKind::Getter, false, name.lexeme.clone());
+        if let Some(getter) = instance.class.borrow().members.get(&mb_key) {
+            let bound_method = bind_method(getter, Value::from(self.clone()));
+            return bound_method.call(itpr_state, vec![]);
+        }
+
+        // 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) = instance.class.borrow().members.get(&mb_key) {
             let bound_method = bind_method(method, Value::from(self.clone()));
             return Ok(Value::from(bound_method));
@@ -163,8 +198,21 @@ impl PropertyCarrier for InstanceRef {
         ))
     }
 
-    fn set(&self, name: &Token, value: Value) {
+    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());
+        if let Some(setter) = instance.class.borrow().members.get(&mb_key) {
+            // Bind and execute the property setter
+            let bound_method = bind_method(setter, Value::from(self.clone()));
+            bound_method
+                .call(itpr_state, vec![value])
+                .expect("failed to execute setter");
+            return;
+        }
+
+        // Set the property directly
         instance
             .fields
             .borrow_mut()
diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs
index e36db33..e120078 100644
--- a/src/interpreter/interpretable.rs
+++ b/src/interpreter/interpretable.rs
@@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
 
 use crate::{
     ast::{
-        ClassDecl, ClassMemberDecl, ClassMemberKind, ExprNode, FunDecl, GetExpr, ProgramNode,
+        ClassDecl, ExprNode, FunDecl, GetExpr, ProgramNode,
         SetExpr, StmtNode, VariableExpr,
     },
     errors::{ErrorKind, SloxError, SloxResult},
@@ -556,8 +556,8 @@ impl ExprNode {
     ) -> InterpreterResult {
         let instance = get_expr.instance.interpret(itpr_state)?.result();
         instance.with_property_carrier(
-            |inst| inst.get(&get_expr.name).map(|v| v.into()),
-            || error(&get_expr.name, "only instances have properties"),
+            |inst| inst.get(itpr_state, &get_expr.name).map(|v| v.into()),
+            || error(&get_expr.name, "this object doesn't have properties"),
         )
     }
 
@@ -571,10 +571,10 @@ impl ExprNode {
         instance.with_property_carrier(
             |instance| {
                 let value = set_expr.value.interpret(itpr_state)?.result();
-                instance.set(&set_expr.name, value.clone());
+                instance.set(itpr_state, &set_expr.name, value.clone());
                 Ok(value.into())
             },
-            || error(&set_expr.name, "only instances have properties"),
+            || error(&set_expr.name, "this object doesn't have properties"),
         )
     }
 }