diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index d3b6b65..01c0957 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -4,6 +4,8 @@ use anyhow::Error; use opcode::Opcode; use parser::ast::{BooleanLiteral, Expression, IntegerLiteral, Literal, Node, Statement, BlockStatement}; +pub mod symbol_table; + #[derive(Clone, PartialEq)] pub struct Bytecode { pub instructions: opcode::Instructions, @@ -136,6 +138,11 @@ impl Compiler { fn compile_statement(&mut self, s: &Statement) -> Result<(), Error> { match s { + Statement::Assign(a) => { + self.compile_expression(&a.value)?; + + return Ok(()); + } Statement::Return(r) => { self.compile_expression(&r.return_value)?; diff --git a/compiler/src/symbol_table.rs b/compiler/src/symbol_table.rs new file mode 100644 index 0000000..3e23e2c --- /dev/null +++ b/compiler/src/symbol_table.rs @@ -0,0 +1,55 @@ +use std::{collections::HashMap, rc::Rc}; + +#[derive(Clone, Debug)] +pub enum SymbolScope { + Global, + Local, + Builtin, + Free, + Function, +} + +#[derive(Clone, Debug)] +pub struct Symbol { + pub name: String, + pub scope: SymbolScope, + pub index: usize, +} + +#[derive(Clone, Debug)] +pub struct SymbolTable { + pub store: HashMap>, + pub num_definitions: usize, +} + +impl SymbolTable { + pub fn define(&mut self, name: &str) -> Rc { + let symbol = Rc::new( + Symbol { + name: name.to_string(), + scope: SymbolScope::Global, + index: self.num_definitions, + } + ); + + self.store.insert(name.to_string(), Rc::clone(&symbol)); + + self.num_definitions += 1; + + symbol + } + + pub fn new() -> Self { + Self { + store: HashMap::new(), + num_definitions: 0, + } + } + + pub fn resolve(&self, name: &str) -> Option> { + match self.store.get(name) { + Some(symbol) => Some(Rc::clone(symbol)), + None => None, + } + } +} \ No newline at end of file diff --git a/compiler/tests/symbol_table_tests.rs b/compiler/tests/symbol_table_tests.rs new file mode 100644 index 0000000..90a59c7 --- /dev/null +++ b/compiler/tests/symbol_table_tests.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use anyhow::Error; +use compiler::symbol_table::{Symbol, SymbolScope, SymbolTable}; + +#[test] +fn test_define() -> Result<(), Error> { + let expected = HashMap::from([ + ("a".to_string(), Symbol {name: "a".to_string(), scope: SymbolScope::Global, index: 0}), + ("b".to_string(), Symbol {name: "b".to_string(), scope: SymbolScope::Global, index: 1}), + ("c".to_string(), Symbol {name: "c".to_string(), scope: SymbolScope::Global, index: 2}), + ("d".to_string(), Symbol {name: "d".to_string(), scope: SymbolScope::Global, index: 3}), + ]); + + let mut global = SymbolTable::new(); + + let a = global.define("a"); + + if a.name != "a" { + panic!("a.name not 'a'. got={}", a.name); + } + + let b = global.define("b"); + + if b.name != "b" { + panic!("b.name not 'b'. got={}", b.name); + } + + Ok(()) +} + +#[test] +fn test_resolve_global() -> Result<(), Error> { + let mut global = SymbolTable::new(); + + global.define("a"); + global.define("b"); + + let expected = vec![ + ("a".to_string(), Symbol {name: "a".to_string(), scope: SymbolScope::Global, index: 0}), + ("b".to_string(), Symbol {name: "b".to_string(), scope: SymbolScope::Global, index: 1}), + ]; + + for (name, expected_symbol) in expected { + let symbol = global.resolve(&name); + + if symbol.is_none() { + panic!("symbol for {} is None", name); + } + + let symbol = symbol.unwrap(); + + if symbol.name != expected_symbol.name { + panic!("symbol.name not {}. got={}", expected_symbol.name, symbol.name); + } + } + + Ok(()) +} \ No newline at end of file diff --git a/vm/tests/vm_tests.rs b/vm/tests/vm_tests.rs index 616e5b8..d6d5d33 100644 --- a/vm/tests/vm_tests.rs +++ b/vm/tests/vm_tests.rs @@ -227,3 +227,25 @@ fn test_integer_arithmetic() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_global_dollar_statements() -> Result<(), Error> { + let tests = vec![ + VmTestCase { + input: "$one = 1; $one".to_string(), + expected: "1".to_string(), + }, + VmTestCase { + input: "$one = 1; $two = 2; $one + $two".to_string(), + expected: "3".to_string(), + }, + VmTestCase { + input: "$one = 1; $two = $one + $one; $one + $two".to_string(), + expected: "3".to_string(), + }, + ]; + + run_vm_tests(tests)?; + + Ok(()) +}