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};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
mod eval;
|
||||||
mod lex;
|
mod lex;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
|
@ -46,7 +47,11 @@ pub fn run(script: &str) -> Result<(), ScriptErrors> {
|
||||||
return Err(errors.into());
|
return Err(errors.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", ASTPrinter.visit_expr(&parsed));
|
println!(
|
||||||
|
"{} => {}",
|
||||||
|
ASTPrinter.visit_expr(&parsed),
|
||||||
|
eval::evaluate(&parsed)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue