Add support for variables in interpreter
This commit is contained in:
parent
520357d930
commit
1ed2ac97cc
4 changed files with 136 additions and 63 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
27
src/compiler/symtab.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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),
|
||||||
|
|
Reference in a new issue