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,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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue