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,9 +1,11 @@
use std::collections::HashMap;
use std::rc::Rc;
use rlox2_frontend::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
use crate::error::RuntimeError;
use crate::LoxClass;
use crate::function::LoxExternFunction;
use crate::{LoxClass, LoxReference};
use super::environment::Environment;
use super::{LoxFunction, Runtime, Value};
@ -133,20 +135,89 @@ impl Eval for Expr {
match callee {
Value::Function(fun) => fun.call(args, env),
Value::ExternFunction(ext_fun) => ext_fun.call(args, env),
Value::Class(class) => LoxClass::call(class, args, env),
_ => Err(RuntimeError::NotCallable { callee }),
}
}
Expr::Get { target, name } => {
let target = target.eval(env)?;
if let Value::Object(object) = target {
object.get(name).ok_or_else(|| {
let class = object.class();
let name = name.to_owned();
RuntimeError::UndefinedAttribute { class, name }
})
} else {
Err(RuntimeError::InvalidGetTarget)
}
}
Expr::Set { target, name, value } => {
let target = target.eval(env)?;
if let Value::Object(mut object) = target {
let value = value.eval(env)?;
object.set(name, value);
Ok(Value::Nil)
} else {
Err(RuntimeError::InvalidSetTarget)
}
}
Expr::Function {
name,
param_names,
closure_vars,
body,
} => Ok(Value::function(LoxFunction::new(
name,
name.as_ref(),
env.collect_closure(closure_vars),
param_names.clone(),
param_names.as_ref().clone(),
body.as_ref().clone(),
))),
Expr::Class {
superclass,
name,
methods: method_exprs,
} => {
let superclass = match superclass.as_ref().map(|expr| expr.eval(env)) {
Some(Ok(Value::Class(superclass))) => Some(superclass),
Some(Ok(value)) => return Err(RuntimeError::InvalidSuperclass { value }),
Some(Err(err)) => return Err(err),
None => None,
};
let mut methods: HashMap<String, Value> = HashMap::new();
// this is the scope "this" will get injected in
env.enter_scope();
for method_expr in method_exprs.iter() {
let method = method_expr.eval(env)?;
if let Value::Function(ref fun) = method {
let name = fun.name().to_owned();
methods.insert(name, method);
}
}
env.exit_scope();
Ok(Value::class(LoxClass::new(name, methods, superclass)))
}
Expr::This => panic!("Unresolved this"),
Expr::Super {
super_var,
this_var,
method,
} => match (super_var.eval(env)?, this_var.eval(env)?) {
(Value::Class(superclass), Value::Object(this)) => {
if let Some(method) = superclass.get_method(method, this) {
Ok(method)
} else {
Err(RuntimeError::InvalidGetTarget)
}
}
(super_val, this_val) => panic!("super evaluated to {super_val} and this evaluated to {this_val}"),
},
}
}
}
@ -196,13 +267,6 @@ impl Eval for Stmt {
}
env.exit_scope();
}
Stmt::Class { name, methods: _ } => {
env.define(name, Value::Nil);
let class = Value::class(LoxClass::new(name));
env.assign(name, class, 0)?;
}
Stmt::ExprStmt { expr } => {
// expr.eval(env)?;
// Ok(Value::Nil)
@ -248,3 +312,40 @@ impl LoxFunction {
ret_val
}
}
impl LoxExternFunction {
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
if args.len() != self.arity() {
return Err(RuntimeError::WrongArity {
name: self.name().to_owned(),
arity: self.arity(),
given: args.len(),
});
}
(self.closure())(args, env)
}
}
impl LoxClass {
// has to take class as an argument instead of as self to leave it behind its Rc
pub fn call(class: Rc<LoxClass>, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
if args.len() != class.arity() {
return Err(RuntimeError::WrongArity {
name: class.name().to_owned(),
arity: class.arity(),
given: args.len(),
});
}
let object = LoxReference::new(class);
// object.init(args, env)?;
if let Some(Value::Function(method)) = object.get("init") {
method.call(args, env)?;
}
Ok(Value::Object(object))
}
}