From 1ed2ac97cc0e52a24b5f4015d814e449179d1689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vili=20Sinerv=C3=A4?= Date: Mon, 3 Feb 2025 18:01:39 +0200 Subject: [PATCH] Add support for variables in interpreter --- src/compiler.rs | 12 ++- src/compiler/interpreter.rs | 158 ++++++++++++++++++++++-------------- src/compiler/symtab.rs | 27 ++++++ src/compiler/value.rs | 2 +- 4 files changed, 136 insertions(+), 63 deletions(-) create mode 100644 src/compiler/symtab.rs diff --git a/src/compiler.rs b/src/compiler.rs index e1c84c1..9a6e499 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -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); } } } diff --git a/src/compiler/interpreter.rs b/src/compiler/interpreter.rs index 23e9750..e6303c9 100644 --- a/src/compiler/interpreter.rs +++ b/src/compiler/interpreter.rs @@ -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 } } } diff --git a/src/compiler/symtab.rs b/src/compiler/symtab.rs new file mode 100644 index 0000000..f712178 --- /dev/null +++ b/src/compiler/symtab.rs @@ -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>>, +} + +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, + } + } +} diff --git a/src/compiler/value.rs b/src/compiler/value.rs index f1269da..3b5bde5 100644 --- a/src/compiler/value.rs +++ b/src/compiler/value.rs @@ -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),