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 std::io;
use interpreter::interpret; use interpreter::Interpreter;
use parser::parse; use parser::parse;
use tokenizer::tokenize; use tokenizer::tokenize;
mod ast; mod ast;
mod interpreter; mod interpreter;
mod parser; mod parser;
mod symtab;
mod token; mod token;
mod tokenizer; mod tokenizer;
mod value; mod value;
@ -18,10 +19,15 @@ pub fn compile(code: &str) {
pub fn start_interpreter() { pub fn start_interpreter() {
let lines = io::stdin().lines(); let lines = io::stdin().lines();
#[allow(clippy::manual_flatten)]
for line in lines { for line in lines {
if let Ok(code) = line { 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,44 +1,71 @@
use std::collections::HashMap;
use crate::compiler::{ use crate::compiler::{
ast::Expression::{self, *}, ast::Expression::{self, *},
symtab::SymTab,
value::Value, value::Value,
}; };
#[expect(unused_variables)] pub struct Interpreter<'source> {
pub fn interpret(ast: &Expression) -> Value { symbols: SymTab<'source>,
}
impl<'source> Interpreter<'source> {
pub fn new() -> Self {
Interpreter {
symbols: SymTab::new_global(),
}
}
pub fn interpret(&mut self, ast: &Expression<'source>) -> Value {
match ast { match ast {
EmptyLiteral(_) => Value::None(), EmptyLiteral(_) => Value::None(),
IntLiteral(_, val) => Value::Int(*val), IntLiteral(_, val) => Value::Int(*val),
BoolLiteral(_, val) => Value::Bool(*val), BoolLiteral(_, val) => Value::Bool(*val),
Identifier(_, _) => todo!(), // Variables are TODO Identifier(_, name) => *self.symbols.get(name),
UnaryOp(_, op, expr) => match *op { UnaryOp(_, op, expr) => match *op {
"-" => -interpret(expr), "-" => -self.interpret(expr),
"not" => !interpret(expr), "not" => !self.interpret(expr),
_ => panic!("Unrecognized unary op {}", op), _ => panic!("Unrecognized unary op {}", op),
}, },
BinaryOp(_, left, op, right) => match *op { BinaryOp(_, left, op, right) => match *op {
"+" => interpret(left) + interpret(right), "+" => self.interpret(left) + self.interpret(right),
"*" => interpret(left) * interpret(right), "*" => self.interpret(left) * self.interpret(right),
"-" => interpret(left) - interpret(right), "-" => self.interpret(left) - self.interpret(right),
"/" => interpret(left) / interpret(right), "/" => self.interpret(left) / self.interpret(right),
"%" => interpret(left) % interpret(right), "%" => self.interpret(left) % self.interpret(right),
"==" => Value::Bool(interpret(left) == interpret(right)), "==" => Value::Bool(self.interpret(left) == self.interpret(right)),
"!=" => Value::Bool(interpret(left) != interpret(right)), "!=" => Value::Bool(self.interpret(left) != self.interpret(right)),
"<" => Value::Bool(interpret(left) < interpret(right)), "<" => Value::Bool(self.interpret(left) < self.interpret(right)),
"<=" => Value::Bool(interpret(left) <= interpret(right)), "<=" => Value::Bool(self.interpret(left) <= self.interpret(right)),
">" => Value::Bool(interpret(left) > interpret(right)), ">" => Value::Bool(self.interpret(left) > self.interpret(right)),
">=" => Value::Bool(interpret(left) >= interpret(right)), ">=" => Value::Bool(self.interpret(left) >= self.interpret(right)),
"and" => interpret(left).and(&interpret(right)), "and" => self.interpret(left).and(&self.interpret(right)),
"or" => interpret(left).or(&interpret(right)), "or" => self.interpret(left).or(&self.interpret(right)),
"=" => todo!(), // Variables are TODO "=" => {
if let Expression::Identifier(_, name) = **left {
let val = self.interpret(right);
*self.symbols.get(name) = val;
val
} else {
panic!("Assignment must have identifier as left expr!");
}
}
_ => panic!("Unrecognized binary op {}", op), _ => panic!("Unrecognized binary op {}", op),
}, },
VarDeclaration(_, name, expr) => todo!(), // Variables are TODO 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) => { Conditional(_, condition_expr, then_expr, else_expr) => {
if let Value::Bool(condition) = interpret(condition_expr) { if let Value::Bool(condition) = self.interpret(condition_expr) {
if condition { if condition {
interpret(then_expr) self.interpret(then_expr)
} else if let Some(expr) = else_expr { } else if let Some(expr) = else_expr {
interpret(expr) self.interpret(expr)
} else { } else {
Value::None() Value::None()
} }
@ -49,10 +76,10 @@ pub fn interpret(ast: &Expression) -> Value {
While(_, condition, do_expr) => { While(_, condition, do_expr) => {
let mut val = Value::None(); let mut val = Value::None();
loop { loop {
let condition = interpret(condition); let condition = self.interpret(condition);
if let Value::Bool(cond) = condition { if let Value::Bool(cond) = condition {
if cond { if cond {
val = interpret(do_expr); val = self.interpret(do_expr);
} else { } else {
break; break;
} }
@ -64,11 +91,24 @@ pub fn interpret(ast: &Expression) -> Value {
} }
FunCall(_, name, args) => todo!(), // Functions are TODO FunCall(_, name, args) => todo!(), // Functions are TODO
Block(_, expressions) => { Block(_, expressions) => {
self.symbols = SymTab {
locals: HashMap::new(),
parent: Some(Box::new(std::mem::take(&mut self.symbols))),
};
let mut val = Value::None(); let mut val = Value::None();
for expression in expressions { for expression in expressions {
val = interpret(expression); 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}, ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
}; };
#[derive(PartialEq, PartialOrd, Debug)] #[derive(PartialEq, PartialOrd, Debug, Copy, Clone)]
pub enum Value { pub enum Value {
Int(i64), Int(i64),
Bool(bool), Bool(bool),