From 8f176543c73ce451c929690d186850beb9258c91 Mon Sep 17 00:00:00 2001 From: greg Date: Sun, 10 Mar 2019 16:04:20 -0700 Subject: [PATCH] Nested scopes in symbol table --- schala-lang/language/src/symbol_table.rs | 113 +++++++++++++++++++---- 1 file changed, 94 insertions(+), 19 deletions(-) diff --git a/schala-lang/language/src/symbol_table.rs b/schala-lang/language/src/symbol_table.rs index 2634394..2eb881d 100644 --- a/schala-lang/language/src/symbol_table.rs +++ b/schala-lang/language/src/symbol_table.rs @@ -21,7 +21,6 @@ struct SymbolPath { //cf. p. 150 or so of Language Implementation Patterns pub struct SymbolTable { values: HashMap, - scope_name_stack: Vec, } //TODO add various types of lookups here, maybe multiple hash tables internally? also make values @@ -30,14 +29,13 @@ impl SymbolTable { pub fn new() -> SymbolTable { SymbolTable { values: HashMap::new(), - scope_name_stack: vec![], } } - pub fn add_new_symbol(&mut self, path: &Rc, symbol: Symbol) { + fn add_new_symbol(&mut self, name: &Rc, path: &Vec>, symbol: Symbol) { let symbol_path = SymbolPath { - name: path.clone(), - enclosing_scopes: vec![] + name: name.clone(), + enclosing_scopes: path.clone(), }; self.values.insert(symbol_path, symbol); } @@ -49,6 +47,11 @@ impl SymbolTable { }; self.values.get(&symbol_path) } + + pub fn lookup_by_path(&self, name: &Rc, path: &Vec>) -> Option<&Symbol> { + let symbol_path = SymbolPath { name: name.clone(), enclosing_scopes: path.clone() }; + self.values.get(&symbol_path) + } } #[derive(Debug)] @@ -93,10 +96,14 @@ impl SymbolTable { pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> { let mut seen_identifiers = HashMap::new(); - self.add_symbols_from_scope(&ast.0, &mut seen_identifiers) + let mut scope_name_stack = Vec::new(); + self.add_symbols_from_scope(&ast.0, &mut scope_name_stack, &mut seen_identifiers) } - fn add_symbols_from_scope<'a>(&'a mut self, statements: &Vec>, seen_identifiers: &mut SymbolTrackTable) -> Result<(), String> { + fn add_symbols_from_scope<'a>(&'a mut self, + statements: &Vec>, + scope_name_stack: &mut Vec>, + seen_identifiers: &mut SymbolTrackTable) -> Result<(), String> { use self::ast::Declaration::*; fn check_symbol(table: &mut SymbolTrackTable, name: &Rc) -> Result<(), String> { @@ -119,15 +126,18 @@ impl SymbolTable { match decl { FuncSig(ref signature) => { check_symbol(seen_identifiers, &signature.name)?; - self.add_function_signature(signature)? + self.add_function_signature(signature, scope_name_stack)? } FuncDecl(ref signature, ref body) => { check_symbol(seen_identifiers, &signature.name)?; - self.add_function_signature(signature)?; + self.add_function_signature(signature, scope_name_stack)?; let mut subscope_seen_identifiers = HashMap::new(); - self.add_symbols_from_scope(body, &mut subscope_seen_identifiers)? + scope_name_stack.push(signature.name.clone()); + let output = self.add_symbols_from_scope(body, scope_name_stack, &mut subscope_seen_identifiers); + let _ = scope_name_stack.pop(); + output? }, - TypeDecl { name, body, mutable } => self.add_type_decl(name, body, mutable)?, + TypeDecl { name, body, mutable } => self.add_type_decl(name, body, mutable, scope_name_stack)?, _ => () } } @@ -142,7 +152,7 @@ impl SymbolTable { output } - fn add_function_signature(&mut self, signature: &Signature) -> Result<(), String> { + fn add_function_signature(&mut self, signature: &Signature, scope_name_stack: &mut Vec>) -> Result<(), String> { let mut local_type_context = LocalTypeContext::new(); let types = signature.params.iter().map(|param| match param { (_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)), @@ -151,12 +161,13 @@ impl SymbolTable { let spec = SymbolSpec::Func(types); self.add_new_symbol( &signature.name, + scope_name_stack, Symbol { name: signature.name.clone(), spec } ); Ok(()) } - fn add_type_decl(&mut self, type_name: &TypeSingletonName, body: &TypeBody, _mutable: &bool) -> Result<(), String> { + fn add_type_decl(&mut self, type_name: &TypeSingletonName, body: &TypeBody, _mutable: &bool, scope_name_stack: &mut Vec>) -> Result<(), String> { use crate::ast::{TypeIdentifier, Variant}; let TypeBody(variants) = body; let TypeSingletonName { name, .. } = type_name; @@ -169,7 +180,7 @@ impl SymbolTable { type_name: name.clone(), type_args: vec![], }; - self.add_new_symbol(variant_name, Symbol { name: variant_name.clone(), spec }); + self.add_new_symbol(variant_name, scope_name_stack, Symbol { name: variant_name.clone(), spec }); }, Variant::TupleStruct(variant_name, tuple_members) => { let type_args = tuple_members.iter().map(|type_name| match type_name { @@ -182,7 +193,7 @@ impl SymbolTable { type_args }; let symbol = Symbol { name: variant_name.clone(), spec }; - self.add_new_symbol(variant_name, symbol); + self.add_new_symbol(variant_name, scope_name_stack, symbol); }, //TODO if there is only one variant, and it is a record, it doesn't need to have an //explicit name @@ -190,7 +201,7 @@ impl SymbolTable { let fields = HashMap::new(); let spec = SymbolSpec::RecordConstructor { fields }; let symbol = Symbol { name: name.clone(), spec }; - self.add_new_symbol(name, symbol); + self.add_new_symbol(name, scope_name_stack, symbol); }, } } @@ -217,15 +228,15 @@ impl LocalTypeContext { #[cfg(test)] mod symbol_table_tests { use super::*; + use crate::util::quick_ast; macro_rules! values_in_table { //TODO multiple values ($source:expr, $single_value:expr) => { { let mut symbol_table = SymbolTable::new(); - let ast = crate::util::quick_ast($source); + let ast = quick_ast($source); symbol_table.add_top_level_symbols(&ast).unwrap(); - println!("TAAABL: {}", symbol_table.debug_symbol_table()); match symbol_table.lookup_by_name($single_value) { Some(_spec) => (), None => panic!(), @@ -247,7 +258,71 @@ mod symbol_table_tests { fn a() { 3 } "#; let mut symbol_table = SymbolTable::new(); - let ast = crate::util::quick_ast(source); + let ast = quick_ast(source); + let output = symbol_table.add_top_level_symbols(&ast).unwrap_err(); + assert!(output.contains("Duplicate")) + } + + #[test] + fn enclosing_scopes() { + let source = r#" +fn outer_func(x) { + fn inner_func(arg) { + arg + } + x + inner_func(x) +}"#; + let mut symbol_table = SymbolTable::new(); + let ast = quick_ast(source); + symbol_table.add_top_level_symbols(&ast).unwrap(); + assert!(symbol_table.lookup_by_path(&rc!(outer_func), &vec![]).is_some()); + assert!(symbol_table.lookup_by_path(&rc!(inner_func), &vec![rc!(outer_func)]).is_some()); + } + + #[test] + fn enclosing_scopes_2() { + let source = r#" +fn outer_func(x) { + fn inner_func(arg) { + arg + } + + fn second_inner_func() { + fn another_inner_func() { + } + } + + inner_func(x) +}"#; + let mut symbol_table = SymbolTable::new(); + let ast = quick_ast(source); + symbol_table.add_top_level_symbols(&ast).unwrap(); + println!("{}", symbol_table.debug_symbol_table()); + assert!(symbol_table.lookup_by_path(&rc!(outer_func), &vec![]).is_some()); + assert!(symbol_table.lookup_by_path(&rc!(inner_func), &vec![rc!(outer_func)]).is_some()); + assert!(symbol_table.lookup_by_path(&rc!(second_inner_func), &vec![rc!(outer_func)]).is_some()); + assert!(symbol_table.lookup_by_path(&rc!(another_inner_func), &vec![rc!(outer_func), rc!(second_inner_func)]).is_some()); + } + + #[test] + fn enclosing_scopes_3() { + let source = r#" +fn outer_func(x) { + fn inner_func(arg) { + arg + } + + fn second_inner_func() { + fn another_inner_func() { + } + fn another_inner_func() { + } + } + + inner_func(x) +}"#; + let mut symbol_table = SymbolTable::new(); + let ast = quick_ast(source); let output = symbol_table.add_top_level_symbols(&ast).unwrap_err(); assert!(output.contains("Duplicate")) }