Add first pass at interpreter (everything before the error checking section)
parent
3100b77003
commit
d80650ddab
|
@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue