mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
Lox Interpreter done (Chapter 13)
This commit is contained in:
parent
10540708d4
commit
719a014977
16 changed files with 707 additions and 196 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue