diff --git a/schala-lang/language/src/schala.rs b/schala-lang/language/src/schala.rs index 8b3c706..2ebc441 100644 --- a/schala-lang/language/src/schala.rs +++ b/schala-lang/language/src/schala.rs @@ -89,6 +89,11 @@ impl Schala { self.resolver.resolve(&mut ast) .map_err(|err| SchalaError::from_string(err, Stage::ScopeResolution))?; + + //WIP - new symbol table + self.symbol_table.borrow_mut().process_ast(&ast) + .map_err(|err| SchalaError::from_string(err, Stage::Symbols))?; + // Typechecking // TODO typechecking not working let _overall_type = self.type_context.typecheck(&ast) diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index 90d83a9..75a3b56 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -4,9 +4,9 @@ use std::rc::Rc; use std::fmt; use std::fmt::Write; -use crate::tokenizing::LineNumber; +use crate::tokenizing::{Location, LineNumber}; use crate::ast; -use crate::ast::{ItemId, TypeBody, TypeSingletonName, Signature, Statement, StatementKind, ModuleSpecifier}; +use crate::ast::{ItemId, TypeBody, Variant, TypeSingletonName, Signature, Declaration, Statement, StatementKind, ModuleSpecifier}; use crate::typechecking::TypeName; @@ -29,6 +29,71 @@ mod symbol_trie; use symbol_trie::SymbolTrie; mod test; + + +/// Fully-qualified symbol name +#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +struct FQSN { + scopes: Vec, +} + +impl FQSN { + fn from_scope_stack(scopes: &[Scope], new_name: String) -> Self { + let mut v = Vec::new(); + for s in scopes { + v.push(s.clone()); + } + v.push(Scope::Name(new_name)); + FQSN { scopes: v } + } + + fn extend(&self, new_name: String) -> Self { + let mut existing = self.scopes.clone(); + existing.push(Scope::Name(new_name)); + FQSN { scopes: existing } + } +} + +//TODO eventually this should use ItemId's to avoid String-cloning +/// One segment within a scope. +#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +enum Scope { + Top, + Name(String) +} + +#[derive(Debug, Clone)] +struct DuplicateName { + prev_name: FQSN, + location: Location +} + +//TODO should map to a Spec* type that has Location and Kind of namespace entry +//tht way I don't need as many tables +/// Keeps track of what names were used in a given namespace. +struct NameTable { + table: HashMap +} + +impl NameTable { + fn new() -> Self { + Self { table: HashMap::new() } + } + + fn register(&mut self, name: FQSN, location: Location) -> Result<(), DuplicateName> { + match self.table.entry(name.clone()) { + Entry::Occupied(o) => { + Err(DuplicateName { prev_name: name, location: *o.get() }) + }, + Entry::Vacant(v) => { + v.insert(location); + Ok(()) + } + } + } +} + + /// Keeps track of what names were used in a given namespace. Call try_register to add a name to /// the table, or report an error if a name already exists. struct DuplicateNameTrackTable { @@ -91,13 +156,15 @@ impl ScopeSegment { } } - //cf. p. 150 or so of Language Implementation Patterns pub struct SymbolTable { decl_locations: DeclLocations, symbol_path_to_symbol: HashMap, id_to_fqsn: HashMap, symbol_trie: SymbolTrie, + functions: NameTable, //TODO maybe bindings and functions should be the same table? + types: NameTable, + bindings: NameTable, //TODO NameTable should be a trie to facilitate quick lookups } impl SymbolTable { @@ -106,7 +173,10 @@ impl SymbolTable { decl_locations: DeclLocations::new(), symbol_path_to_symbol: HashMap::new(), id_to_fqsn: HashMap::new(), - symbol_trie: SymbolTrie::new() + symbol_trie: SymbolTrie::new(), + functions: NameTable::new(), + types: NameTable::new(), + bindings: NameTable::new(), } } @@ -182,10 +252,114 @@ impl fmt::Display for SymbolSpec { } } + impl SymbolTable { /* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem * later */ + + /// This function traverses the AST and adds symbol table entries for + /// constants, functions, types, and modules defined within. This simultaneously + /// checks for dupicate definitions (and returns errors if discovered), and sets + /// up name tables that will be used by further parts of the compiler + pub fn process_ast(&mut self, ast: &ast::AST) -> Result<(), String> { + + let mut scope_stack = vec![Scope::Top]; + self.add_from_scope(ast.statements.as_ref(), &mut scope_stack) + .map_err(|err| format!("{:?}", err))?; + Ok(()) + } + + //TODO this should probably return a vector of duplicate name errors + fn add_from_scope<'a>(&'a mut self, statements: &[Statement], scope_stack: &mut Vec) -> Result<(), DuplicateName> { + for statement in statements { + let Statement { id: _, kind, location } = statement; + let location = *location; + match kind { + StatementKind::Declaration(Declaration::FuncSig(signature)) => { + let fn_name: String = signature.name.as_str().to_owned(); + let fq_function = FQSN::from_scope_stack(scope_stack.as_ref(), fn_name); + self.functions.register(fq_function.clone(), location)?; + self.types.register(fq_function, location)?; + } + StatementKind::Declaration(Declaration::FuncDecl(signature, body)) => { + let fn_name: String = signature.name.as_str().to_owned(); + let new_scope = Scope::Name(fn_name.clone()); + let fq_function = FQSN::from_scope_stack(scope_stack.as_ref(), fn_name); + self.functions.register(fq_function.clone(), location)?; + self.types.register(fq_function, location)?; + scope_stack.push(new_scope); + let output = self.add_from_scope(body.as_ref(), scope_stack); + scope_stack.pop(); + output? + }, + StatementKind::Declaration(Declaration::TypeDecl { name, body, mutable }) => { + let fq_type = FQSN::from_scope_stack(scope_stack.as_ref(), name.name.as_ref().to_owned()); + self.types.register(fq_type, location)?; + if let Err(errors) = self.add_type_members(name, body, mutable, location, scope_stack) { + return Err(errors[0].clone()); + } + }, + StatementKind::Declaration(Declaration::Binding { name, .. }) => { + let fq_binding = FQSN::from_scope_stack(scope_stack.as_ref(), name.as_str().to_owned()); + self.bindings.register(fq_binding, location)?; + } + StatementKind::Module(ModuleSpecifier { name, contents }) => { + let mod_name = name.as_str().to_owned(); + let fq_module = FQSN::from_scope_stack(scope_stack.as_ref(), mod_name.clone()); + let new_scope = Scope::Name(mod_name); + self.bindings.register(fq_module, location)?; + scope_stack.push(new_scope); + let output = self.add_from_scope(contents.as_ref(), scope_stack); + scope_stack.pop(); + output? + }, + _ => (), + } + } + Ok(()) + } + + fn add_type_members(&mut self, type_name: &TypeSingletonName, type_body: &TypeBody, _mutable: &bool, location: Location, scope_stack: &mut Vec) -> Result<(), Vec> { + let mut errors = vec![]; + + let mut register = |fqsn: FQSN| { + if let Err(err) = self.types.register(fqsn, location) { + errors.push(err); + } + }; + + let TypeBody(variants) = type_body; + let new_scope = Scope::Name(type_name.name.as_ref().to_owned()); + scope_stack.push(new_scope); + + for variant in variants { + match variant { + Variant::UnitStruct(name) | Variant::TupleStruct(name, _) => { + let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.as_ref().to_owned()); + register(fq_name); + }, + Variant::Record { name, members } => { + let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.as_ref().to_owned()); + register(fq_name.clone()); + for (field_name, _) in members { + let fq_field_name = fq_name.extend(field_name.as_str().to_owned()); + register(fq_field_name); + } + } + } + } + + scope_stack.pop(); + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } + + pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> { let mut scope_name_stack = Vec::new(); self.add_symbols_from_scope(&ast.statements, &mut scope_name_stack) @@ -269,7 +443,7 @@ impl SymbolTable { //TODO handle type mutability 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}; + use crate::ast::{TypeIdentifier}; let TypeBody(variants) = body; let ref type_name = type_name.name;