rlox/src/interpreter/eval.rs

157 lines
5.7 KiB
Rust
Raw Normal View History

2023-01-20 21:44:27 +01:00
use std::fmt::Display;
2023-01-22 23:33:57 +01:00
use crate::error::RuntimeError;
use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
2023-01-20 21:44:27 +01:00
2023-01-22 23:33:57 +01:00
use super::environment::Environment;
pub type EvalResult<T> = Result<T, RuntimeError>;
2023-01-20 21:44:27 +01:00
/*====================================================================================================================*/
2023-01-22 23:33:57 +01:00
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
statement.eval(env)
2023-01-20 21:44:27 +01:00
}
/*====================================================================================================================*/
2023-01-22 23:33:57 +01:00
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
2023-01-20 21:44:27 +01:00
String(String),
Number(f64),
Bool(bool),
Nil,
}
2023-01-22 23:33:57 +01:00
impl Value {
2023-01-20 21:44:27 +01:00
fn is_truthy(&self) -> bool {
match self {
2023-01-22 23:33:57 +01:00
Value::Bool(false) | Value::Nil => false,
2023-01-20 21:44:27 +01:00
_ => true,
}
}
}
2023-01-22 23:33:57 +01:00
impl Display for Value {
2023-01-20 21:44:27 +01:00
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
2023-01-22 23:33:57 +01:00
Value::String(s) => write!(f, "\"{s}\""),
Value::Number(num) => write!(f, "{num}"),
Value::Bool(b) => write!(f, "{b}"),
Value::Nil => write!(f, "nil"),
2023-01-20 21:44:27 +01:00
}
}
}
/*====================================================================================================================*/
2023-01-22 23:33:57 +01:00
/* trait Eval {
fn eval(self, env: &mut Environment) -> EvalResult;
} */
2023-01-20 21:44:27 +01:00
2023-01-22 23:33:57 +01:00
impl Literal {
fn eval(self, _env: &mut Environment) -> EvalResult<Value> {
2023-01-20 21:44:27 +01:00
match self {
2023-01-22 23:33:57 +01:00
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),
2023-01-20 21:44:27 +01:00
}
}
}
2023-01-22 23:33:57 +01:00
impl Expr {
fn eval(self, env: &mut Environment) -> EvalResult<Value> {
2023-01-20 21:44:27 +01:00
match self {
2023-01-22 23:33:57 +01:00
Expr::Literal { literal } => literal.eval(env),
Expr::Unary { op, expr } => {
let arg = expr.eval(env)?;
2023-01-20 21:44:27 +01:00
match (op, arg) {
2023-01-22 23:33:57 +01:00
(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 }),
2023-01-20 21:44:27 +01:00
}
}
2023-01-22 23:33:57 +01:00
Expr::Binary { left, op, right } => {
use Value::{Bool, Number, String};
2023-01-20 21:44:27 +01:00
2023-01-22 23:33:57 +01:00
let left = left.eval(env)?;
let right = right.eval(env)?;
2023-01-20 21:44:27 +01:00
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 {
2023-01-22 23:33:57 +01:00
return Err(RuntimeError::DivisionByZero);
2023-01-20 21:44:27 +01:00
}
Ok(Number(left / right))
}
(String(left), BinaryOp::Add, String(right)) => Ok(String(left + &right)),
(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)),
2023-01-22 23:33:57 +01:00
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
}
}
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)?;
2023-01-20 21:44:27 +01:00
}
2023-01-22 23:33:57 +01:00
env.exit_scope();
Ok(())
2023-01-20 21:44:27 +01:00
}
}
}
}