Nested scopes in symbol table
This commit is contained in:
parent
9716b5e55b
commit
8f176543c7
@ -21,7 +21,6 @@ struct SymbolPath {
|
|||||||
//cf. p. 150 or so of Language Implementation Patterns
|
//cf. p. 150 or so of Language Implementation Patterns
|
||||||
pub struct SymbolTable {
|
pub struct SymbolTable {
|
||||||
values: HashMap<SymbolPath, Symbol>,
|
values: HashMap<SymbolPath, Symbol>,
|
||||||
scope_name_stack: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add various types of lookups here, maybe multiple hash tables internally? also make values
|
//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 {
|
pub fn new() -> SymbolTable {
|
||||||
SymbolTable {
|
SymbolTable {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
scope_name_stack: vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_new_symbol(&mut self, path: &Rc<String>, symbol: Symbol) {
|
fn add_new_symbol(&mut self, name: &Rc<String>, path: &Vec<Rc<String>>, symbol: Symbol) {
|
||||||
let symbol_path = SymbolPath {
|
let symbol_path = SymbolPath {
|
||||||
name: path.clone(),
|
name: name.clone(),
|
||||||
enclosing_scopes: vec![]
|
enclosing_scopes: path.clone(),
|
||||||
};
|
};
|
||||||
self.values.insert(symbol_path, symbol);
|
self.values.insert(symbol_path, symbol);
|
||||||
}
|
}
|
||||||
@ -49,6 +47,11 @@ impl SymbolTable {
|
|||||||
};
|
};
|
||||||
self.values.get(&symbol_path)
|
self.values.get(&symbol_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lookup_by_path(&self, name: &Rc<String>, path: &Vec<Rc<String>>) -> Option<&Symbol> {
|
||||||
|
let symbol_path = SymbolPath { name: name.clone(), enclosing_scopes: path.clone() };
|
||||||
|
self.values.get(&symbol_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -93,10 +96,14 @@ impl SymbolTable {
|
|||||||
|
|
||||||
pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> {
|
pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> {
|
||||||
let mut seen_identifiers = HashMap::new();
|
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<Meta<Statement>>, seen_identifiers: &mut SymbolTrackTable) -> Result<(), String> {
|
fn add_symbols_from_scope<'a>(&'a mut self,
|
||||||
|
statements: &Vec<Meta<Statement>>,
|
||||||
|
scope_name_stack: &mut Vec<Rc<String>>,
|
||||||
|
seen_identifiers: &mut SymbolTrackTable) -> Result<(), String> {
|
||||||
use self::ast::Declaration::*;
|
use self::ast::Declaration::*;
|
||||||
|
|
||||||
fn check_symbol(table: &mut SymbolTrackTable, name: &Rc<String>) -> Result<(), String> {
|
fn check_symbol(table: &mut SymbolTrackTable, name: &Rc<String>) -> Result<(), String> {
|
||||||
@ -119,15 +126,18 @@ impl SymbolTable {
|
|||||||
match decl {
|
match decl {
|
||||||
FuncSig(ref signature) => {
|
FuncSig(ref signature) => {
|
||||||
check_symbol(seen_identifiers, &signature.name)?;
|
check_symbol(seen_identifiers, &signature.name)?;
|
||||||
self.add_function_signature(signature)?
|
self.add_function_signature(signature, scope_name_stack)?
|
||||||
}
|
}
|
||||||
FuncDecl(ref signature, ref body) => {
|
FuncDecl(ref signature, ref body) => {
|
||||||
check_symbol(seen_identifiers, &signature.name)?;
|
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();
|
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
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function_signature(&mut self, signature: &Signature) -> Result<(), String> {
|
fn add_function_signature(&mut self, signature: &Signature, scope_name_stack: &mut Vec<Rc<String>>) -> Result<(), String> {
|
||||||
let mut local_type_context = LocalTypeContext::new();
|
let mut local_type_context = LocalTypeContext::new();
|
||||||
let types = signature.params.iter().map(|param| match param {
|
let types = signature.params.iter().map(|param| match param {
|
||||||
(_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)),
|
(_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)),
|
||||||
@ -151,12 +161,13 @@ impl SymbolTable {
|
|||||||
let spec = SymbolSpec::Func(types);
|
let spec = SymbolSpec::Func(types);
|
||||||
self.add_new_symbol(
|
self.add_new_symbol(
|
||||||
&signature.name,
|
&signature.name,
|
||||||
|
scope_name_stack,
|
||||||
Symbol { name: signature.name.clone(), spec }
|
Symbol { name: signature.name.clone(), spec }
|
||||||
);
|
);
|
||||||
Ok(())
|
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<Rc<String>>) -> Result<(), String> {
|
||||||
use crate::ast::{TypeIdentifier, Variant};
|
use crate::ast::{TypeIdentifier, Variant};
|
||||||
let TypeBody(variants) = body;
|
let TypeBody(variants) = body;
|
||||||
let TypeSingletonName { name, .. } = type_name;
|
let TypeSingletonName { name, .. } = type_name;
|
||||||
@ -169,7 +180,7 @@ impl SymbolTable {
|
|||||||
type_name: name.clone(),
|
type_name: name.clone(),
|
||||||
type_args: vec![],
|
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) => {
|
Variant::TupleStruct(variant_name, tuple_members) => {
|
||||||
let type_args = tuple_members.iter().map(|type_name| match type_name {
|
let type_args = tuple_members.iter().map(|type_name| match type_name {
|
||||||
@ -182,7 +193,7 @@ impl SymbolTable {
|
|||||||
type_args
|
type_args
|
||||||
};
|
};
|
||||||
let symbol = Symbol { name: variant_name.clone(), spec };
|
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
|
//TODO if there is only one variant, and it is a record, it doesn't need to have an
|
||||||
//explicit name
|
//explicit name
|
||||||
@ -190,7 +201,7 @@ impl SymbolTable {
|
|||||||
let fields = HashMap::new();
|
let fields = HashMap::new();
|
||||||
let spec = SymbolSpec::RecordConstructor { fields };
|
let spec = SymbolSpec::RecordConstructor { fields };
|
||||||
let symbol = Symbol { name: name.clone(), spec };
|
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)]
|
#[cfg(test)]
|
||||||
mod symbol_table_tests {
|
mod symbol_table_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::util::quick_ast;
|
||||||
|
|
||||||
macro_rules! values_in_table {
|
macro_rules! values_in_table {
|
||||||
//TODO multiple values
|
//TODO multiple values
|
||||||
($source:expr, $single_value:expr) => {
|
($source:expr, $single_value:expr) => {
|
||||||
{
|
{
|
||||||
let mut symbol_table = SymbolTable::new();
|
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();
|
symbol_table.add_top_level_symbols(&ast).unwrap();
|
||||||
println!("TAAABL: {}", symbol_table.debug_symbol_table());
|
|
||||||
match symbol_table.lookup_by_name($single_value) {
|
match symbol_table.lookup_by_name($single_value) {
|
||||||
Some(_spec) => (),
|
Some(_spec) => (),
|
||||||
None => panic!(),
|
None => panic!(),
|
||||||
@ -247,7 +258,71 @@ mod symbol_table_tests {
|
|||||||
fn a() { 3 }
|
fn a() { 3 }
|
||||||
"#;
|
"#;
|
||||||
let mut symbol_table = SymbolTable::new();
|
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();
|
let output = symbol_table.add_top_level_symbols(&ast).unwrap_err();
|
||||||
assert!(output.contains("Duplicate"))
|
assert!(output.contains("Duplicate"))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user