1
0
Fork 0

Add support for variables in interpreter

This commit is contained in:
Vili Sinervä 2025-02-03 18:01:39 +02:00
parent 520357d930
commit 1ed2ac97cc
No known key found for this signature in database
GPG key ID: DF8FEAF54EFAC996
4 changed files with 136 additions and 63 deletions

View file

@ -1,12 +1,13 @@
use std::io;
use interpreter::interpret;
use interpreter::Interpreter;
use parser::parse;
use tokenizer::tokenize;
mod ast;
mod interpreter;
mod parser;
mod symtab;
mod token;
mod tokenizer;
mod value;
@ -18,10 +19,15 @@ pub fn compile(code: &str) {
pub fn start_interpreter() {
let lines = io::stdin().lines();
#[allow(clippy::manual_flatten)]
for line in lines {
if let Ok(code) = line {
println!("{}", interpret(&parse(&tokenize(&code))));
let tokens = tokenize(&code);
let ast = parse(&tokens);
let mut interpreter = Interpreter::new();
let val = interpreter.interpret(&ast);
println!("{}", val);
}
}
}

View file

@ -1,74 +1,114 @@
use std::collections::HashMap;
use crate::compiler::{
ast::Expression::{self, *},
symtab::SymTab,
value::Value,
};
#[expect(unused_variables)]
pub fn interpret(ast: &Expression) -> Value {
match ast {
EmptyLiteral(_) => Value::None(),
IntLiteral(_, val) => Value::Int(*val),
BoolLiteral(_, val) => Value::Bool(*val),
Identifier(_, _) => todo!(), // Variables are TODO
UnaryOp(_, op, expr) => match *op {
"-" => -interpret(expr),
"not" => !interpret(expr),
_ => panic!("Unrecognized unary op {}", op),
},
BinaryOp(_, left, op, right) => match *op {
"+" => interpret(left) + interpret(right),
"*" => interpret(left) * interpret(right),
"-" => interpret(left) - interpret(right),
"/" => interpret(left) / interpret(right),
"%" => interpret(left) % interpret(right),
"==" => Value::Bool(interpret(left) == interpret(right)),
"!=" => Value::Bool(interpret(left) != interpret(right)),
"<" => Value::Bool(interpret(left) < interpret(right)),
"<=" => Value::Bool(interpret(left) <= interpret(right)),
">" => Value::Bool(interpret(left) > interpret(right)),
">=" => Value::Bool(interpret(left) >= interpret(right)),
"and" => interpret(left).and(&interpret(right)),
"or" => interpret(left).or(&interpret(right)),
"=" => todo!(), // Variables are TODO
_ => panic!("Unrecognized binary op {}", op),
},
VarDeclaration(_, name, expr) => todo!(), // Variables are TODO
Conditional(_, condition_expr, then_expr, else_expr) => {
if let Value::Bool(condition) = interpret(condition_expr) {
if condition {
interpret(then_expr)
} else if let Some(expr) = else_expr {
interpret(expr)
} else {
Value::None()
}
} else {
panic!("Non-bool as if-then-else condition!");
}
pub struct Interpreter<'source> {
symbols: SymTab<'source>,
}
impl<'source> Interpreter<'source> {
pub fn new() -> Self {
Interpreter {
symbols: SymTab::new_global(),
}
While(_, condition, do_expr) => {
let mut val = Value::None();
loop {
let condition = interpret(condition);
if let Value::Bool(cond) = condition {
if cond {
val = interpret(do_expr);
}
pub fn interpret(&mut self, ast: &Expression<'source>) -> Value {
match ast {
EmptyLiteral(_) => Value::None(),
IntLiteral(_, val) => Value::Int(*val),
BoolLiteral(_, val) => Value::Bool(*val),
Identifier(_, name) => *self.symbols.get(name),
UnaryOp(_, op, expr) => match *op {
"-" => -self.interpret(expr),
"not" => !self.interpret(expr),
_ => panic!("Unrecognized unary op {}", op),
},
BinaryOp(_, left, op, right) => match *op {
"+" => self.interpret(left) + self.interpret(right),
"*" => self.interpret(left) * self.interpret(right),
"-" => self.interpret(left) - self.interpret(right),
"/" => self.interpret(left) / self.interpret(right),
"%" => self.interpret(left) % self.interpret(right),
"==" => Value::Bool(self.interpret(left) == self.interpret(right)),
"!=" => Value::Bool(self.interpret(left) != self.interpret(right)),
"<" => Value::Bool(self.interpret(left) < self.interpret(right)),
"<=" => Value::Bool(self.interpret(left) <= self.interpret(right)),
">" => Value::Bool(self.interpret(left) > self.interpret(right)),
">=" => Value::Bool(self.interpret(left) >= self.interpret(right)),
"and" => self.interpret(left).and(&self.interpret(right)),
"or" => self.interpret(left).or(&self.interpret(right)),
"=" => {
if let Expression::Identifier(_, name) = **left {
let val = self.interpret(right);
*self.symbols.get(name) = val;
val
} else {
break;
panic!("Assignment must have identifier as left expr!");
}
}
_ => panic!("Unrecognized binary op {}", op),
},
VarDeclaration(_, name, expr) => {
let value = self.interpret(expr);
if self.symbols.locals.insert(name, value).is_some() {
panic!("Variable {} already defined in this scope!", name)
}
Value::None()
}
Conditional(_, condition_expr, then_expr, else_expr) => {
if let Value::Bool(condition) = self.interpret(condition_expr) {
if condition {
self.interpret(then_expr)
} else if let Some(expr) = else_expr {
self.interpret(expr)
} else {
Value::None()
}
} else {
panic!("Non-boon as while-do condition!");
panic!("Non-bool as if-then-else condition!");
}
}
val
}
FunCall(_, name, args) => todo!(), // Functions are TODO
Block(_, expressions) => {
let mut val = Value::None();
for expression in expressions {
val = interpret(expression);
While(_, condition, do_expr) => {
let mut val = Value::None();
loop {
let condition = self.interpret(condition);
if let Value::Bool(cond) = condition {
if cond {
val = self.interpret(do_expr);
} else {
break;
}
} else {
panic!("Non-boon as while-do condition!");
}
}
val
}
FunCall(_, name, args) => todo!(), // Functions are TODO
Block(_, expressions) => {
self.symbols = SymTab {
locals: HashMap::new(),
parent: Some(Box::new(std::mem::take(&mut self.symbols))),
};
let mut val = Value::None();
for expression in expressions {
val = self.interpret(expression);
}
if let Some(symbols) = &mut self.symbols.parent {
self.symbols = std::mem::take(symbols);
} else {
panic!("Non-global symbol table without parent!");
}
val
}
val
}
}
}

27
src/compiler/symtab.rs Normal file
View file

@ -0,0 +1,27 @@
use crate::compiler::value::Value;
use std::collections::HashMap;
#[derive(Default)]
pub struct SymTab<'source> {
pub locals: HashMap<&'source str, Value>,
pub parent: Option<Box<SymTab<'source>>>,
}
impl<'source> SymTab<'source> {
pub fn get(&mut self, symbol: &str) -> &mut Value {
if let Some(val) = self.locals.get_mut(symbol) {
val
} else if let Some(parent) = &mut self.parent {
parent.get(symbol)
} else {
panic!("No symbol {} found!", symbol);
}
}
pub fn new_global() -> SymTab<'source> {
SymTab {
locals: HashMap::new(),
parent: None,
}
}
}

View file

@ -3,7 +3,7 @@ use std::{
ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
};
#[derive(PartialEq, PartialOrd, Debug)]
#[derive(PartialEq, PartialOrd, Debug, Copy, Clone)]
pub enum Value {
Int(i64),
Bool(bool),