From 4e866165ea28862e23b41c3e38392d7623b43988 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Tue, 17 Jan 2023 07:06:38 +0100
Subject: [PATCH] Classes - Superclass method accessor

---
 src/interpreter/classes.rs | 72 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/src/interpreter/classes.rs b/src/interpreter/classes.rs
index f8c4d51..593573b 100644
--- a/src/interpreter/classes.rs
+++ b/src/interpreter/classes.rs
@@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
 use lazy_static::lazy_static;
 
 use crate::{
-    ast::ClassMemberKind,
+    ast::{ClassMemberKind, SuperExpr},
     errors::{ErrorKind, SloxError, SloxResult},
     tokens::Token,
 };
@@ -15,6 +15,12 @@ use super::{functions::Function, Callable, InterpreterState, Value};
 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.
@@ -179,6 +185,38 @@ impl PropertyCarrier for ClassRef {
         // 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> {
+        let mb_key = (
+            ClassMemberKind::Method,
+            true,
+            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, Value::from(self.clone()));
+            Ok(Value::from(bound_method))
+        }) {
+            method
+        } else {
+            Err(SloxError::with_token(
+                ErrorKind::Runtime,
+                &super_expr.method,
+                "undefined property".to_owned(),
+            ))
+        }
+    }
 }
 
 /* ----------------------- *
@@ -255,4 +293,36 @@ impl PropertyCarrier for InstanceRef {
             .borrow_mut()
             .insert(name.lexeme.clone(), value);
     }
+
+    fn get_super(
+        &self,
+        itpr_state: &mut InterpreterState,
+        super_expr: &SuperExpr,
+        distance: usize,
+    ) -> SloxResult<Value> {
+        let mb_key = (
+            ClassMemberKind::Method,
+            false,
+            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, Value::from(self.clone()));
+            Ok(Value::from(bound_method))
+        }) {
+            method
+        } else {
+            Err(SloxError::with_token(
+                ErrorKind::Runtime,
+                &super_expr.method,
+                "undefined property".to_owned(),
+            ))
+        }
+    }
 }