Lox Interpreter done (Chapter 13)

This commit is contained in:
Moritz Gmeiner 2023-01-28 14:19:12 +01:00
commit 719a014977
16 changed files with 707 additions and 196 deletions

View file

@ -1,19 +1,141 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::rc::Rc;
#[derive(Debug)]
use rlox2_frontend::parser::{Expr, Stmt};
use crate::{LoxFunction, LoxReference, Value};
#[derive(Debug, Clone)]
pub struct LoxClass {
superclass: Option<Rc<LoxClass>>,
name: String,
methods: HashMap<String, Value>,
}
/// Representation of a class in Lox. Always behind an Rc to ensure uniqueness. Should never be handled raw.
impl LoxClass {
pub fn new(name: impl Into<String>) -> Self {
pub fn new(name: impl Into<String>, methods: HashMap<String, Value>, superclass: Option<Rc<LoxClass>>) -> Rc<Self> {
let name = name.into();
LoxClass { name }
let mut methods = methods;
// if class has an init method: insert an implicit "return this;" at its end
if let Some(Value::Function(init)) = methods.get("init") {
let name = init.name().to_owned();
let closure = init.closure().clone();
let param_names = init.param_names().to_vec();
let mut body = init.body().clone();
if let Stmt::Block { ref mut statements } = body {
statements.push(Stmt::return_stmt(Expr::LocalVariable {
name: "this".to_owned(),
level: 1,
}));
} else {
panic!("Body of init method of class {name} wasn't a block");
}
let new_init = Value::function(LoxFunction::new(name, closure, param_names, body));
methods.insert("init".to_owned(), new_init);
}
if let Some(ref superclass) = superclass {
let mut new_methods: HashMap<String, Value> = HashMap::new();
// Rc<LoxFunction> is immutable, so we need to drain, change, and replace
for (name, value) in methods {
if let Value::Function(method_ref) = value {
let superclass = superclass.clone();
let mut method = method_ref.as_ref().clone();
method.inject_closed_var("super", Value::Class(superclass));
new_methods.insert(name, Value::function(method));
}
}
methods = new_methods;
}
Rc::new(LoxClass {
superclass,
name,
methods,
})
}
pub fn superclass(&self) -> Option<Rc<LoxClass>> {
self.superclass.clone()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn arity(&self) -> usize {
if let Some(Value::Function(method)) = self.methods.get("init") {
return method.arity();
}
/* if let Some(superclass) = self.superclass {
return superclass.arity();
} */
0
}
pub fn get_method(&self, name: &str, this: LoxReference) -> Option<Value> {
/* self.methods
.get(name)
.cloned()
.or_else(|| self.superclass.as_ref().and_then(|cls| cls.get_method(name))) */
if let Some(method) = self.methods.get(name) {
match method {
Value::Function(method) => {
let mut method = method.as_ref().clone();
method.inject_closed_var("this", Value::Object(this));
return Some(Value::function(method));
}
method => panic!("method {name} on class {self} is {method}"),
}
}
if let Some(ref superclass) = self.superclass {
return superclass.get_method(name, this);
}
None
}
/* pub fn init(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<()> {
if let Some(ref superclass) = self.superclass {
let args = args.clone();
superclass.init(args, env)?;
}
if let Some(Value::Function(method)) = self.get_method("init") {
method.call(args, env)?;
}
Ok(())
} */
}
impl Display for LoxClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<class {}>", self.name)
write!(f, "<class {} at {:#X}>", self.name, (self as *const LoxClass) as usize)
}
}
// slightly hacky: two classes are equal if they are stored at the same location
// since a LoxClass is always (supposed to be) behind an Rc anyways, we might as well compare their pointers
impl PartialEq for LoxClass {
fn eq(&self, other: &Self) -> bool {
let self_ptr = self as *const LoxClass;
let other_ptr = other as *const LoxClass;
self_ptr == other_ptr
}
}