use std::collections::HashMap; use std::rc::Rc; use std::fmt; use std::fmt::Write; use crate::ast; use crate::ast::{TypeBody, TypeSingletonName, Signature}; use crate::typechecking::TypeName; #[derive(PartialEq, Eq, Hash, Debug)] struct SymbolPath { name: Rc, enclosing_scopes: Vec> } //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 //non-public 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) { let symbol_path = SymbolPath { name: path.clone(), enclosing_scopes: vec![] }; self.values.insert(symbol_path, symbol); } pub fn lookup_by_name(&self, name: &Rc) -> Option<&Symbol> { let symbol_path = SymbolPath { name: name.clone(), enclosing_scopes: vec![] }; self.values.get(&symbol_path) } } #[derive(Debug)] pub struct Symbol { pub name: Rc, pub spec: SymbolSpec, } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "", self.name, self.spec) } } #[derive(Debug)] pub enum SymbolSpec { Func(Vec), DataConstructor { index: usize, type_name: Rc, type_args: Vec>, }, RecordConstructor { fields: HashMap, Rc> } } impl fmt::Display for SymbolSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::SymbolSpec::*; match self { Func(type_names) => write!(f, "Func({:?})", type_names), DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor(idx: {})({:?} -> {})", index, type_args, type_name), RecordConstructor { fields: _fields } => write!(f, "RecordConstructor( )"), } } } impl SymbolTable { /* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem * later */ pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> { use self::ast::Statement; use self::ast::Declaration::*; for statement in ast.0.iter() { let statement = statement.node(); if let Statement::Declaration(decl) = statement { match decl { FuncSig(signature) | FuncDecl(signature, _) => self.add_function_signature(signature)?, TypeDecl { name, body, mutable } => self.add_type_decl(name, body, mutable)?, _ => () } } } Ok(()) } pub fn debug_symbol_table(&self) -> String { let mut output = format!("Symbol table\n"); for (name, sym) in &self.values { write!(output, "{:?} -> {}\n", name, sym).unwrap(); } output } fn add_function_signature(&mut self, signature: &Signature) -> 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)), (_, None) => local_type_context.new_universal_type() }).collect(); let spec = SymbolSpec::Func(types); self.add_new_symbol( &signature.name, Symbol { name: signature.name.clone(), spec } ); Ok(()) } fn add_type_decl(&mut self, type_name: &TypeSingletonName, body: &TypeBody, _mutable: &bool) -> Result<(), String> { use crate::ast::{TypeIdentifier, Variant}; let TypeBody(variants) = body; let TypeSingletonName { name, .. } = type_name; //TODO figure out why _params isn't being used here for (index, var) in variants.iter().enumerate() { match var { Variant::UnitStruct(variant_name) => { let spec = SymbolSpec::DataConstructor { index, type_name: name.clone(), type_args: vec![], }; self.add_new_symbol(variant_name, Symbol { name: variant_name.clone(), spec }); }, Variant::TupleStruct(variant_name, tuple_members) => { let type_args = tuple_members.iter().map(|type_name| match type_name { TypeIdentifier::Singleton(TypeSingletonName { name, ..}) => name.clone(), TypeIdentifier::Tuple(_) => unimplemented!(), }).collect(); let spec = SymbolSpec::DataConstructor { index, type_name: name.clone(), type_args }; let symbol = Symbol { name: variant_name.clone(), spec }; self.add_new_symbol(variant_name, symbol); }, //TODO if there is only one variant, and it is a record, it doesn't need to have an //explicit name Variant::Record { name, members: _members } => { let fields = HashMap::new(); let spec = SymbolSpec::RecordConstructor { fields }; let symbol = Symbol { name: name.clone(), spec }; self.add_new_symbol(name, symbol); }, } } Ok(()) } } struct LocalTypeContext { state: u8 } impl LocalTypeContext { fn new() -> LocalTypeContext { LocalTypeContext { state: 0 } } fn new_universal_type(&mut self) -> TypeName { let n = self.state; self.state += 1; Rc::new(format!("{}", (('a' as u8) + n) as char)) } } #[cfg(test)] mod symbol_table_tests { use super::*; 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); 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!(), }; } } } #[test] fn basic_symbol_table() { values_in_table! { "let a = 10; fn b() { 20 }", &Rc::new("b".to_string()) }; } }