schala/schala-lang/language/src/symbol_table.rs

209 lines
6.0 KiB
Rust
Raw Normal View History

use std::collections::HashMap;
use std::rc::Rc;
use std::fmt;
use std::fmt::Write;
2019-01-07 13:00:37 -08:00
use crate::ast;
2019-01-20 22:13:05 -08:00
use crate::ast::{TypeBody, TypeSingletonName, Signature};
2019-01-07 13:00:37 -08:00
use crate::typechecking::TypeName;
#[derive(PartialEq, Eq, Hash, Debug)]
struct SymbolPath {
name: Rc<String>,
enclosing_scopes: Vec<Rc<String>>
}
//cf. p. 150 or so of Language Implementation Patterns
pub struct SymbolTable {
2019-03-07 23:51:31 -08:00
values: HashMap<SymbolPath, Symbol>,
scope_name_stack: Vec<String>,
}
2018-08-05 18:01:42 -07:00
//TODO add various types of lookups here, maybe multiple hash tables internally? also make values
//non-public
impl SymbolTable {
pub fn new() -> SymbolTable {
2019-03-07 23:51:31 -08:00
SymbolTable {
values: HashMap::new(),
scope_name_stack: vec![],
}
}
2018-08-05 18:19:48 -07:00
pub fn add_new_symbol(&mut self, path: &Rc<String>, symbol: Symbol) {
let symbol_path = SymbolPath {
name: path.clone(),
enclosing_scopes: vec![]
};
self.values.insert(symbol_path, symbol);
}
2018-08-05 18:19:48 -07:00
pub fn lookup_by_name(&self, name: &Rc<String>) -> Option<&Symbol> {
let symbol_path = SymbolPath {
name: name.clone(),
enclosing_scopes: vec![]
};
self.values.get(&symbol_path)
2018-08-05 18:19:48 -07:00
}
}
#[derive(Debug)]
pub struct Symbol {
pub name: Rc<String>,
pub spec: SymbolSpec,
}
2018-06-03 23:04:07 -07:00
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Name: {}, Spec: {}>", self.name, self.spec)
}
}
#[derive(Debug)]
pub enum SymbolSpec {
2018-06-03 02:39:49 -07:00
Func(Vec<TypeName>),
2018-05-30 23:54:24 -07:00
DataConstructor {
index: usize,
2018-05-30 23:54:24 -07:00
type_name: Rc<String>,
type_args: Vec<Rc<String>>,
},
2019-01-25 00:57:01 -08:00
RecordConstructor {
fields: HashMap<Rc<String>, Rc<String>>
}
}
2018-06-03 23:04:07 -07:00
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),
2019-02-21 18:39:41 -08:00
RecordConstructor { fields: _fields } => write!(f, "RecordConstructor( <fields> )"),
2018-06-03 23:04:07 -07:00
}
}
}
impl SymbolTable {
/* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem
* later */
2018-06-04 19:25:40 -07:00
pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> {
2019-01-20 22:13:05 -08:00
use self::ast::Statement;
2018-06-04 19:25:40 -07:00
use self::ast::Declaration::*;
for statement in ast.0.iter() {
2019-01-04 22:58:25 -08:00
let statement = statement.node();
if let Statement::Declaration(decl) = statement {
match decl {
2019-01-20 22:13:05 -08:00
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");
2018-06-03 23:04:07 -07:00
for (name, sym) in &self.values {
write!(output, "{:?} -> {}\n", name, sym).unwrap();
}
output
}
2019-01-20 00:22:35 -08:00
2019-01-20 22:13:05 -08:00
fn add_function_signature(&mut self, signature: &Signature) -> Result<(), String> {
2019-01-20 00:22:35 -08:00
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,
2019-01-20 00:22:35 -08:00
Symbol { name: signature.name.clone(), spec }
);
2019-01-20 22:13:05 -08:00
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 });
2019-01-20 22:13:05 -08:00
},
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);
2019-01-20 22:13:05 -08:00
},
2019-01-25 00:57:01 -08:00
//TODO if there is only one variant, and it is a record, it doesn't need to have an
//explicit name
2019-02-21 18:39:41 -08:00
Variant::Record { name, members: _members } => {
2019-01-25 00:57:01 -08:00
let fields = HashMap::new();
let spec = SymbolSpec::RecordConstructor { fields };
let symbol = Symbol { name: name.clone(), spec };
self.add_new_symbol(name, symbol);
2019-01-25 00:57:01 -08:00
},
2019-01-20 22:13:05 -08:00
}
}
Ok(())
2019-01-20 00:22:35 -08:00
}
}
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))
}
}
2019-03-07 23:51:31 -08:00
#[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()) };
}
}