mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
chapter 8 done
This commit is contained in:
parent
f56fcc4a8b
commit
956c4d0f28
13 changed files with 570 additions and 177 deletions
|
|
@ -1,82 +1,84 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use crate::error::EvalError;
|
||||
use crate::parser::expr::{BinaryOp, Expr, Literal, UnaryOp};
|
||||
use crate::error::RuntimeError;
|
||||
use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
|
||||
|
||||
pub type EvalResult = Result<Primitive, EvalError>;
|
||||
use super::environment::Environment;
|
||||
|
||||
pub type EvalResult<T> = Result<T, RuntimeError>;
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
pub fn evaluate(expr: Expr) -> EvalResult {
|
||||
expr.eval()
|
||||
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
|
||||
statement.eval(env)
|
||||
}
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Primitive {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Bool(bool),
|
||||
Nil,
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
impl Value {
|
||||
fn is_truthy(&self) -> bool {
|
||||
match self {
|
||||
Primitive::Bool(false) | Primitive::Nil => false,
|
||||
Value::Bool(false) | Value::Nil => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Primitive {
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Primitive::String(s) => write!(f, "\"{s}\""),
|
||||
Primitive::Number(num) => write!(f, "{num}"),
|
||||
Primitive::Bool(b) => write!(f, "{b}"),
|
||||
Primitive::Nil => write!(f, "nil"),
|
||||
Value::String(s) => write!(f, "\"{s}\""),
|
||||
Value::Number(num) => write!(f, "{num}"),
|
||||
Value::Bool(b) => write!(f, "{b}"),
|
||||
Value::Nil => write!(f, "nil"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
trait Eval {
|
||||
fn eval(self) -> EvalResult;
|
||||
}
|
||||
/* trait Eval {
|
||||
fn eval(self, env: &mut Environment) -> EvalResult;
|
||||
} */
|
||||
|
||||
impl Eval for Literal {
|
||||
fn eval(self) -> EvalResult {
|
||||
impl Literal {
|
||||
fn eval(self, _env: &mut Environment) -> EvalResult<Value> {
|
||||
match self {
|
||||
Literal::String(s) => Ok(Primitive::String(s)),
|
||||
Literal::Number(num) => Ok(Primitive::Number(num)),
|
||||
Literal::Bool(b) => Ok(Primitive::Bool(b)),
|
||||
Literal::Nil => Ok(Primitive::Nil),
|
||||
Literal::String(s) => Ok(Value::String(s)),
|
||||
Literal::Number(num) => Ok(Value::Number(num)),
|
||||
Literal::Bool(b) => Ok(Value::Bool(b)),
|
||||
Literal::Nil => Ok(Value::Nil),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for Expr {
|
||||
fn eval(self) -> EvalResult {
|
||||
impl Expr {
|
||||
fn eval(self, env: &mut Environment) -> EvalResult<Value> {
|
||||
match self {
|
||||
Expr::Literal(literal) => literal.eval(),
|
||||
Expr::Unary(op, expr) => {
|
||||
let arg = expr.eval()?;
|
||||
Expr::Literal { literal } => literal.eval(env),
|
||||
Expr::Unary { op, expr } => {
|
||||
let arg = expr.eval(env)?;
|
||||
|
||||
match (op, arg) {
|
||||
(UnaryOp::Negate, Primitive::Number(num)) => Ok(Primitive::Number(-num)),
|
||||
(UnaryOp::Not, Primitive::Bool(b)) => Ok(Primitive::Bool(!b)),
|
||||
(UnaryOp::Not, primitive) => Ok(Primitive::Bool(!primitive.is_truthy())),
|
||||
(op, arg) => Err(EvalError::UnaryOpInvalidArgument { op, arg }),
|
||||
(UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)),
|
||||
(UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
|
||||
(UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())),
|
||||
(op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }),
|
||||
}
|
||||
}
|
||||
Expr::Binary(left_expr, op, right_expr) => {
|
||||
use Primitive::{Bool, Number, String};
|
||||
Expr::Binary { left, op, right } => {
|
||||
use Value::{Bool, Number, String};
|
||||
|
||||
let left = left_expr.eval()?;
|
||||
let right = right_expr.eval()?;
|
||||
let left = left.eval(env)?;
|
||||
let right = right.eval(env)?;
|
||||
|
||||
match (left, op, right) {
|
||||
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
|
||||
|
|
@ -84,7 +86,7 @@ impl Eval for Expr {
|
|||
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
|
||||
(Number(left), BinaryOp::Divide, Number(right)) => {
|
||||
if right == 0.0 {
|
||||
return Err(EvalError::DivisionByZero);
|
||||
return Err(RuntimeError::DivisionByZero);
|
||||
}
|
||||
Ok(Number(left / right))
|
||||
}
|
||||
|
|
@ -104,10 +106,52 @@ impl Eval for Expr {
|
|||
(String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)),
|
||||
(String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)),
|
||||
|
||||
(left, op, right) => Err(EvalError::BinaryOpInvalidArguments { left, op, right }),
|
||||
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
|
||||
}
|
||||
}
|
||||
Expr::Grouping(expr) => expr.eval(),
|
||||
Expr::Grouping { expr } => expr.eval(env),
|
||||
Expr::Variable { name } => env.get(&name).cloned(),
|
||||
Expr::Assignment { name, value } => {
|
||||
let value = value.eval(env)?;
|
||||
env.assign(&name, value.clone())?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
fn eval(self, env: &mut Environment) -> EvalResult<()> {
|
||||
match self {
|
||||
Stmt::ExprStmt { expr } => {
|
||||
// expr.eval(env)?;
|
||||
// Ok(Value::Nil)
|
||||
expr.eval(env)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Print { expr } => {
|
||||
match expr.eval(env)? {
|
||||
// special case: when printing a string, drop the surrounding ""
|
||||
Value::String(s) => println!("{s}"),
|
||||
val => println!("{val}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stmt::VarDecl { name, initializer } => {
|
||||
let initializer = initializer.eval(env)?;
|
||||
env.define(name, initializer);
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Block { statements } => {
|
||||
env.enter_scope();
|
||||
for statement in statements {
|
||||
// on error the current frame gets destroyed anyways, so no need to exit scope
|
||||
statement.eval(env)?;
|
||||
}
|
||||
env.exit_scope();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue