190 lines
6.3 KiB
Rust
190 lines
6.3 KiB
Rust
use std::{collections::HashMap, fmt::Display, rc::Rc};
|
|
|
|
use crate::{ast::{Expression, BinOpType, UnOpType, Ast, Statement, If}, parser::parse, lexer::lex};
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
pub enum Value {
|
|
I64(i64),
|
|
String(Rc<String>),
|
|
}
|
|
|
|
pub struct Interpreter {
|
|
// Variable table stores the runtime values of variables
|
|
vartable: HashMap<String, Value>,
|
|
}
|
|
|
|
impl Interpreter {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
vartable: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
|
let tokens = lex(code).unwrap();
|
|
if print_tokens {
|
|
println!("Tokens: {:?}", tokens);
|
|
}
|
|
|
|
let ast = parse(tokens);
|
|
if print_ast {
|
|
println!("{:#?}", ast);
|
|
}
|
|
|
|
self.run(&ast);
|
|
}
|
|
|
|
pub fn run(&mut self, prog: &Ast) {
|
|
for stmt in &prog.prog {
|
|
match stmt {
|
|
Statement::Expr(expr) => {
|
|
self.resolve_expr(expr);
|
|
}
|
|
|
|
Statement::Loop(looop) => {
|
|
// loop runs as long condition != 0
|
|
loop {
|
|
if matches!(self.resolve_expr(&looop.condition), Value::I64(0)) {
|
|
break;
|
|
}
|
|
|
|
self.run(&looop.body);
|
|
|
|
if let Some(adv) = &looop.advancement {
|
|
self.resolve_expr(&adv);
|
|
}
|
|
}
|
|
}
|
|
|
|
Statement::Print(expr) => {
|
|
let result = self.resolve_expr(expr);
|
|
print!("{}", result);
|
|
}
|
|
|
|
Statement::If(If {condition, body_true, body_false}) => {
|
|
if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
|
self.run(body_false);
|
|
} else {
|
|
self.run(body_true);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
|
match expr {
|
|
Expression::I64(val) => Value::I64(*val),
|
|
Expression::String(text) => Value::String(text.clone()),
|
|
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
|
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
|
Expression::Var(name) => self.resolve_var(name),
|
|
}
|
|
}
|
|
|
|
fn resolve_var(&mut self, name: &str) -> Value {
|
|
match self.vartable.get(name) {
|
|
Some(val) => val.clone(),
|
|
None => panic!("Variable '{}' used but not declared", name),
|
|
}
|
|
}
|
|
|
|
fn resolve_unop(&mut self, uo: &UnOpType, operand: &Expression) -> Value {
|
|
let operand = self.resolve_expr(operand);
|
|
|
|
match (operand, uo) {
|
|
(Value::I64(val), UnOpType::Negate) => Value::I64(-val),
|
|
(Value::I64(val), UnOpType::BNot) => Value::I64(!val),
|
|
(Value::I64(val), UnOpType::LNot) => Value::I64(if val == 0 { 1 } else { 0 }),
|
|
_ => panic!("Value type is not compatible with unary operation"),
|
|
}
|
|
}
|
|
|
|
fn resolve_binop(&mut self, bo: &BinOpType, lhs: &Expression, rhs: &Expression) -> Value {
|
|
let rhs = self.resolve_expr(rhs);
|
|
|
|
match (&bo, &lhs) {
|
|
(BinOpType::Declare, Expression::Var(name)) => {
|
|
self.vartable.insert(name.clone(), rhs.clone());
|
|
return rhs;
|
|
}
|
|
(BinOpType::Assign, Expression::Var(name)) => {
|
|
match self.vartable.get_mut(name) {
|
|
Some(val) => *val = rhs.clone(),
|
|
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
|
}
|
|
return rhs;
|
|
}
|
|
_ => ()
|
|
}
|
|
|
|
let lhs = self.resolve_expr(lhs);
|
|
|
|
match (lhs, rhs) {
|
|
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
|
BinOpType::Add => Value::I64(lhs + rhs),
|
|
BinOpType::Mul => Value::I64(lhs * rhs),
|
|
BinOpType::Sub => Value::I64(lhs - rhs),
|
|
BinOpType::Div => Value::I64(lhs / rhs),
|
|
BinOpType::Mod => Value::I64(lhs % rhs),
|
|
BinOpType::BOr => Value::I64(lhs | rhs),
|
|
BinOpType::BAnd => Value::I64(lhs & rhs),
|
|
BinOpType::BXor => Value::I64(lhs ^ rhs),
|
|
BinOpType::LAnd => Value::I64(if (lhs != 0) && (rhs != 0) { 1 } else { 0 }),
|
|
BinOpType::LOr => Value::I64(if (lhs != 0) || (rhs != 0) { 1 } else { 0 }),
|
|
BinOpType::Shr => Value::I64(lhs >> rhs),
|
|
BinOpType::Shl => Value::I64(lhs << rhs),
|
|
BinOpType::EquEqu => Value::I64(if lhs == rhs { 1 } else { 0 }),
|
|
BinOpType::NotEqu => Value::I64(if lhs != rhs { 1 } else { 0 }),
|
|
BinOpType::Less => Value::I64(if lhs < rhs { 1 } else { 0 }),
|
|
BinOpType::LessEqu => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
|
BinOpType::Greater => Value::I64(if lhs > rhs { 1 } else { 0 }),
|
|
BinOpType::GreaterEqu => Value::I64(if lhs >= rhs { 1 } else { 0 }),
|
|
|
|
BinOpType::Declare | BinOpType::Assign => unreachable!(),
|
|
},
|
|
_ => panic!("Value types are not compatible"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Value {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Value::I64(val) => write!(f, "{}", val),
|
|
Value::String(text) => write!(f, "{}", text),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::{Interpreter, Value};
|
|
use crate::ast::{Expression, BinOpType};
|
|
|
|
#[test]
|
|
fn test_interpreter_expr() {
|
|
// Expression: 1 + 2 * 3 + 4
|
|
// With precedence: (1 + (2 * 3)) + 4
|
|
let ast = Expression::BinOp(
|
|
BinOpType::Add,
|
|
Expression::BinOp(
|
|
BinOpType::Add,
|
|
Expression::I64(1).into(),
|
|
Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
|
|
)
|
|
.into(),
|
|
Expression::I64(4).into(),
|
|
);
|
|
|
|
let expected = Value::I64(11);
|
|
|
|
let mut interpreter = Interpreter::new();
|
|
let actual = interpreter.resolve_expr(&ast);
|
|
|
|
assert_eq!(expected, actual);
|
|
}
|
|
}
|