diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..000f850 --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,139 @@ +use std::fmt::{self, Display, Formatter}; + +use crate::{ + ast::{Expr, LiteralValue, Visitor}, + lex::{Token, TokenKind}, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum EvaluatedValue { + Number(f64), + String(String), + Boolean(bool), + Nil, +} + +impl EvaluatedValue { + fn is_truthy(&self) -> bool { + match self { + EvaluatedValue::Nil => false, + EvaluatedValue::Boolean(value) => *value, + _ => true, + } + } +} + +impl From<&LiteralValue> for EvaluatedValue { + fn from(value: &LiteralValue) -> Self { + match value { + LiteralValue::True => EvaluatedValue::Boolean(true), + LiteralValue::False => EvaluatedValue::Boolean(false), + LiteralValue::Nil => EvaluatedValue::Nil, + LiteralValue::Number(number) => EvaluatedValue::Number(*number), + LiteralValue::String(string) => EvaluatedValue::String(string.clone()), + } + } +} + +impl Display for EvaluatedValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + EvaluatedValue::Nil => write!(f, "nil"), + EvaluatedValue::Boolean(true) => write!(f, "true"), + EvaluatedValue::Boolean(false) => write!(f, "false"), + EvaluatedValue::Number(val) => write!(f, "{val}"), + EvaluatedValue::String(val) => write!(f, "{val}"), + } + } +} + +struct Interpreter; + +pub fn evaluate(expr: &Expr) -> EvaluatedValue { + Interpreter.visit_expr(expr) +} + +macro_rules! assume_number { + ($value: expr) => { + match $value { + EvaluatedValue::Number(n) => n, + _ => panic!("value was not a number"), + } + }; +} + +impl Visitor for Interpreter { + fn visit_literal(&mut self, value: &LiteralValue) -> EvaluatedValue { + value.into() + } + + fn visit_grouping(&mut self, expr: &Expr) -> EvaluatedValue { + self.visit_expr(expr) + } + + fn visit_unary(&mut self, expr: &Expr, operator: &Token) -> EvaluatedValue { + let operand = self.visit_expr(expr); + match operator.kind() { + TokenKind::Minus => EvaluatedValue::Number(-assume_number!(operand)), + TokenKind::Bang => EvaluatedValue::Boolean(operand.is_truthy()), + _ => unreachable!( + "attempted to evaluate non-unary operator as unary operator: {:?}", + operator + ), + } + } + + fn visit_binary(&mut self, left: &Expr, operator: &Token, right: &Expr) -> EvaluatedValue { + let left_operand = self.visit_expr(left); + let right_operand = self.visit_expr(right); + match operator.kind() { + TokenKind::Minus => { + EvaluatedValue::Number(assume_number!(left_operand) - assume_number!(right_operand)) + } + + TokenKind::Slash => { + EvaluatedValue::Number(assume_number!(left_operand) / assume_number!(right_operand)) + } + + TokenKind::Star => { + EvaluatedValue::Number(assume_number!(left_operand) * assume_number!(right_operand)) + } + + TokenKind::Plus => match (left_operand, right_operand) { + (EvaluatedValue::Number(left_num), EvaluatedValue::Number(right_num)) => { + EvaluatedValue::Number(left_num + right_num) + } + + (EvaluatedValue::String(left_str), EvaluatedValue::String(right_str)) => { + EvaluatedValue::String(left_str + right_str.as_ref()) + } + + _ => panic!("incompatible addition types"), // TODO: handle this better + }, + + TokenKind::Greater => EvaluatedValue::Boolean( + assume_number!(left_operand) > assume_number!(right_operand), + ), + + TokenKind::GreaterEqual => EvaluatedValue::Boolean( + assume_number!(left_operand) >= assume_number!(right_operand), + ), + + TokenKind::Less => EvaluatedValue::Boolean( + assume_number!(left_operand) < assume_number!(right_operand), + ), + + TokenKind::LessEqual => EvaluatedValue::Boolean( + assume_number!(left_operand) <= assume_number!(right_operand), + ), + + TokenKind::EqualEqual => EvaluatedValue::Boolean(left_operand == right_operand), + TokenKind::BangEqual => EvaluatedValue::Boolean(left_operand != right_operand), + + _ => unreachable!( + "attempted to evaluate non-binary operator as binary operator: {:?}", + operator + ), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f99d0f9..52874c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use crate::ast::{Expr, Visitor}; use std::fmt::{self, Display, Formatter}; mod ast; +mod eval; mod lex; mod parse; @@ -46,7 +47,11 @@ pub fn run(script: &str) -> Result<(), ScriptErrors> { return Err(errors.into()); } - println!("{}", ASTPrinter.visit_expr(&parsed)); + println!( + "{} => {}", + ASTPrinter.visit_expr(&parsed), + eval::evaluate(&parsed) + ); Ok(()) }