Add first pass at interpreter (everything before the error checking section)

master
Nick Krichevsky 2024-05-15 16:43:57 -04:00
parent 3100b77003
commit d80650ddab
2 changed files with 145 additions and 1 deletions

139
src/eval.rs Normal file
View File

@ -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<EvaluatedValue> 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
),
}
}
}

View File

@ -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(())
}