Finished up to and including chapter 16

This commit is contained in:
Moritz Gmeiner 2023-01-30 17:41:48 +01:00
commit b86985deaf
24 changed files with 1051 additions and 198 deletions

View file

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::rc::Rc;
use rlox2_frontend::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
use rustc_hash::FxHashMap;
use crate::error::RuntimeError;
use crate::function::LoxExternFunction;
@ -29,8 +29,7 @@ trait Eval {
}
impl Eval for Literal {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
let _ = env;
fn eval(&self, _env: &mut Environment) -> EvalResult<Value> {
match self {
Literal::String(s) => Ok(Value::String(Rc::clone(s))),
Literal::Number(num) => Ok(Value::Number(*num)),
@ -57,57 +56,60 @@ impl Eval for Expr {
Expr::Binary { left, op, right } => {
use Value::{Bool, Number, String};
let op = *op;
let left = left.eval(env)?;
let right = right.eval(env)?;
match (left, *op, right) {
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
(Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)),
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
(Number(left), BinaryOp::Divide, Number(right)) => {
if right == 0.0 {
return Err(RuntimeError::DivisionByZero);
if op == BinaryOp::Equal {
return Ok(Bool(left == right));
}
if op == BinaryOp::NotEqual {
return Ok(Bool(left != right));
}
match (left, right) {
(Number(left), Number(right)) => match op {
BinaryOp::Add => Ok(Number(left + right)),
BinaryOp::Subtract => Ok(Number(left - right)),
BinaryOp::Multiply => Ok(Number(left * right)),
BinaryOp::Divide => Ok(Number(left / right)),
BinaryOp::Less => Ok(Bool(left < right)),
BinaryOp::LessEqual => Ok(Bool(left <= right)),
BinaryOp::Greater => Ok(Bool(left > right)),
BinaryOp::GreaterEqual => Ok(Bool(left >= right)),
_ => unreachable!(),
},
(String(left), String(right)) => match op {
BinaryOp::Add => {
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
s += &left;
s += &right;
Ok(String(Rc::new(s)))
}
Ok(Number(left / right))
}
(String(left), BinaryOp::Add, String(right)) => {
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
s += &left;
s += &right;
Ok(String(Rc::new(s)))
}
(left, BinaryOp::Equal, right) => Ok(Bool(left == right)),
(left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)),
(Number(left), BinaryOp::Less, Number(right)) => Ok(Bool(left < right)),
(Number(left), BinaryOp::LessEqual, Number(right)) => Ok(Bool(left <= right)),
(Number(left), BinaryOp::Greater, Number(right)) => Ok(Bool(left > right)),
(Number(left), BinaryOp::GreaterEqual, Number(right)) => Ok(Bool(left >= right)),
(String(left), BinaryOp::Less, String(right)) => Ok(Bool(left < right)),
(String(left), BinaryOp::LessEqual, String(right)) => Ok(Bool(left <= right)),
(String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)),
(String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)),
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
BinaryOp::Less => Ok(Bool(left < right)),
BinaryOp::LessEqual => Ok(Bool(left <= right)),
BinaryOp::Greater => Ok(Bool(left > right)),
BinaryOp::GreaterEqual => Ok(Bool(left >= right)),
BinaryOp::Equal | BinaryOp::NotEqual => unreachable!(),
_ => Err(RuntimeError::BinaryOpInvalidArguments {
left: String(left),
op,
right: String(right),
}),
},
(left, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
}
}
Expr::Logical { left, op, right } => {
let left = left.eval(env)?;
match op {
LogicalOp::Or => {
if left.is_truthy() {
return Ok(left);
}
}
LogicalOp::And => {
if !left.is_truthy() {
return Ok(left);
}
}
// shortcircuit
if *op == LogicalOp::Or && left.is_truthy() || *op == LogicalOp::And && !left.is_truthy() {
return Ok(left);
}
let right = right.eval(env)?;
Ok(right)
}
@ -127,15 +129,20 @@ impl Eval for Expr {
}
Expr::Call { callee, args } => {
let callee = callee.eval(env)?;
let args = args
.iter()
.map(|arg| arg.eval(env))
.collect::<EvalResult<Vec<Value>>>()?;
/* let args = args
.iter()
.map(|arg| arg.eval(env))
.collect::<EvalResult<Vec<Value>>>()?; */
for arg in args {
let arg = arg.eval(env)?;
env.push_arg(arg);
}
match callee {
Value::Function(fun) => LoxFunction::call(fun, args, env),
Value::ExternFunction(ext_fun) => ext_fun.call(args, env),
Value::Class(class) => LoxClass::call(class, args, env),
Value::Function(fun) => call_fun(fun, env),
Value::ExternFunction(ext_fun) => call_extfun(ext_fun, env),
Value::Class(class) => call_class(class, env),
_ => Err(RuntimeError::NotCallable { callee }),
}
}
@ -169,9 +176,9 @@ impl Eval for Expr {
closure_vars,
body,
} => Ok(Value::function(LoxFunction::new(
name.as_ref(),
name,
env.collect_closure(closure_vars),
param_names.as_ref().clone(),
param_names.clone(),
body.as_ref().clone(),
))),
Expr::Class {
@ -186,7 +193,7 @@ impl Eval for Expr {
None => None,
};
let mut methods: HashMap<String, Value> = HashMap::new();
let mut methods: FxHashMap<String, Value> = FxHashMap::default();
// this is the scope "this" will get injected in
env.enter_scope();
@ -285,71 +292,71 @@ impl Eval for Stmt {
/*====================================================================================================================*/
impl LoxFunction {
pub fn call(fun: Rc<LoxFunction>, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
if args.len() != fun.arity() {
return Err(RuntimeError::WrongArity {
name: fun.name().to_owned(),
arity: fun.arity(),
given: args.len(),
});
}
pub fn call_fun(fun: Rc<LoxFunction>, env: &mut Environment) -> EvalResult<Value> {
let args = env.collect_args();
env.enter_scope();
env.define(fun.name(), Value::Function(fun.clone()));
env.insert_closure(fun.closure().clone());
for (name, value) in std::iter::zip(fun.param_names(), args) {
env.define(name, value);
}
let ret_val = match fun.body().eval(env) {
Ok(_) => Ok(Value::Nil),
Err(RuntimeError::Return { value }) => Ok(value),
Err(err) => Err(err),
};
env.exit_scope();
ret_val
if args.len() != fun.arity() {
return Err(RuntimeError::WrongArity {
name: fun.name().to_owned(),
arity: fun.arity(),
given: args.len(),
});
}
env.enter_scope();
env.define(fun.name(), Value::Function(fun.clone()));
env.insert_closure(fun.closure().clone());
for (name, value) in std::iter::zip(fun.param_names(), args) {
env.define(name, value);
}
let ret_val = match fun.body().eval(env) {
Ok(_) => Ok(Value::Nil),
Err(RuntimeError::Return { value }) => Ok(value),
Err(err) => Err(err),
};
env.exit_scope();
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(),
});
}
pub fn call_extfun(ext_fun: Rc<LoxExternFunction>, env: &mut Environment) -> EvalResult<Value> {
let args = env.collect_args();
(self.closure())(args, env)
if args.len() != ext_fun.arity() {
return Err(RuntimeError::WrongArity {
name: ext_fun.name().to_owned(),
arity: ext_fun.arity(),
given: args.len(),
});
}
(ext_fun.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(),
});
}
// has to take class as an argument instead of as self to leave it behind its Rc
pub fn call_class(class: Rc<LoxClass>, env: &mut Environment) -> EvalResult<Value> {
// let args = env.collect_args();
let object = LoxReference::new(class);
// object.init(args, env)?;
if let Some(Value::Function(method)) = object.get("init") {
LoxFunction::call(method, args, env)?;
}
Ok(Value::Object(object))
if env.num_args() != class.arity() {
return Err(RuntimeError::WrongArity {
name: class.name().to_owned(),
arity: class.arity(),
given: env.num_args(),
});
}
let object = LoxReference::new(class);
// object.init(args, env)?;
if let Some(Value::Function(method)) = object.get("init") {
call_fun(method, env)?;
}
Ok(Value::Object(object))
}