From 542bc56aad9b50cd2a768449475c30ae4eefa389 Mon Sep 17 00:00:00 2001 From: Emmanuel Benoit Date: Mon, 9 Jan 2023 10:50:04 +0100 Subject: [PATCH] Interpreter - Initial implementation of bound methods --- src/interpreter/classes.rs | 60 +++++++++++++++++++++++++++++++++++- src/interpreter/functions.rs | 4 +++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/interpreter/classes.rs b/src/interpreter/classes.rs index a782023..494d0a8 100644 --- a/src/interpreter/classes.rs +++ b/src/interpreter/classes.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc}; use crate::{ errors::{ErrorKind, SloxError, SloxResult}, - tokens::Token, + tokens::{Token, TokenType}, }; use super::{functions::Function, Callable, InterpreterState, Value}; @@ -24,6 +24,13 @@ pub struct Instance { fields: HashMap, } +/// A method bound to an instance. +#[derive(Debug, Clone)] +pub struct BoundMethod { + instance: Value, + method: String, +} + /* -------------------- * * Class implementation * * -------------------- */ @@ -88,6 +95,18 @@ impl Instance { pub(super) fn set(&mut self, name: &Token, value: Value) { self.fields.insert(name.lexeme.clone(), value); } + + fn with_method(&self, name: &str, f: F) -> Rt + where + F: FnOnce(&Function) -> Rt, + { + let cls = self.class.borrow(); + let method = cls + .methods + .get(name) + .expect(&format!("Method {} not found", name)); + f(method) + } } impl Display for Instance { @@ -95,3 +114,42 @@ impl Display for Instance { f.write_fmt(format_args!("", self.class.borrow(),)) } } + +/* --------------------------- * + * Bound method implementation * + * --------------------------- */ + +impl BoundMethod { + fn with_method(&self, f: F) -> Rt + where + F: FnOnce(&Function) -> Rt, + { + self.instance.with_instance( + |instance| instance.with_method(&self.method, f), + || panic!("Instance value does not contain an instance"), + ) + } + + fn this_token(&self) -> Token { + Token { + token_type: TokenType::This, + lexeme: "this".to_owned(), + line: self.with_method(|method| method.name().expect("Method has no name").line), + } + } +} + +impl Callable for BoundMethod { + fn arity(&self) -> usize { + self.with_method(|m| m.arity()) + } + + fn call(&self, itpr_state: &mut InterpreterState, arguments: Vec) -> SloxResult { + let mut this_env = InterpreterState::create_child(itpr_state); + this_env + .environment + .borrow_mut() + .define(&self.this_token(), Some(self.instance.clone()))?; + self.with_method(|m| m.call(&mut this_env, arguments)) + } +} diff --git a/src/interpreter/functions.rs b/src/interpreter/functions.rs index cac76bd..1813719 100644 --- a/src/interpreter/functions.rs +++ b/src/interpreter/functions.rs @@ -31,6 +31,10 @@ impl Function { env: environment, } } + + pub(super) fn name(&self) -> Option<&Token> { + self.name.as_ref() + } } impl Callable for Function {