Compare commits

..

No commits in common. "209b6bba48bef616aea55794129ec6c3af9b545b" and "30fbc9a721d48376d7aa7111486b1a86dc6bc1d0" have entirely different histories.

7 changed files with 317 additions and 304 deletions

View File

@ -1,24 +1,20 @@
use std::{ use std::collections::{hash_map::Entry, HashMap};
collections::{hash_map::Entry, HashMap, HashSet}, use std::fmt;
fmt, use std::rc::Rc;
rc::Rc,
};
use crate::{ use crate::ast;
ast, use crate::ast::{
ast::{ Declaration, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, TypeSingletonName,
Declaration, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, TypeSingletonName, Variant, Variant, VariantKind,
VariantKind,
},
tokenizing::Location,
type_inference::{PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder},
}; };
use crate::tokenizing::Location;
use crate::type_inference::{TypeContext, TypeId};
mod resolver; mod resolver;
mod symbol_trie; mod symbol_trie;
use symbol_trie::SymbolTrie; use symbol_trie::SymbolTrie;
mod test; mod test;
use crate::identifier::{define_id_kind, Id, IdStore}; use crate::identifier::{Id, IdStore, define_id_kind};
define_id_kind!(DefItem); define_id_kind!(DefItem);
pub type DefId = Id<DefItem>; pub type DefId = Id<DefItem>;
@ -67,6 +63,7 @@ impl fmt::Display for Fqsn {
} }
} }
//TODO eventually this should use ItemId's to avoid String-cloning //TODO eventually this should use ItemId's to avoid String-cloning
/// One segment within a scope. /// One segment within a scope.
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
@ -84,9 +81,15 @@ impl fmt::Display for Scope {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SymbolError { pub enum SymbolError {
DuplicateName { prev_name: Fqsn, location: Location }, DuplicateName {
DuplicateVariant { type_fqsn: Fqsn, name: String }, prev_name: Fqsn,
DuplicateRecord { type_name: Fqsn, location: Location, member: String }, location: Location,
},
DuplicateRecord {
type_name: Fqsn,
location: Location,
member: String,
},
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -113,13 +116,17 @@ struct NameTable<K> {
impl<K> NameTable<K> { impl<K> NameTable<K> {
fn new() -> Self { fn new() -> Self {
Self { table: HashMap::new() } Self {
table: HashMap::new(),
}
} }
fn register(&mut self, name: Fqsn, spec: NameSpec<K>) -> Result<(), SymbolError> { fn register(&mut self, name: Fqsn, spec: NameSpec<K>) -> Result<(), SymbolError> {
match self.table.entry(name.clone()) { match self.table.entry(name.clone()) {
Entry::Occupied(o) => Entry::Occupied(o) => Err(SymbolError::DuplicateName {
Err(SymbolError::DuplicateName { prev_name: name, location: o.get().location }), prev_name: name,
location: o.get().location,
}),
Entry::Vacant(v) => { Entry::Vacant(v) => {
v.insert(spec); v.insert(spec);
Ok(()) Ok(())
@ -130,6 +137,7 @@ impl<K> NameTable<K> {
//cf. p. 150 or so of Language Implementation Patterns //cf. p. 150 or so of Language Implementation Patterns
pub struct SymbolTable { pub struct SymbolTable {
def_id_store: IdStore<DefItem>, def_id_store: IdStore<DefItem>,
/// Used for import resolution. /// Used for import resolution.
@ -163,11 +171,8 @@ impl SymbolTable {
/// The main entry point into the symbol table. This will traverse the AST in several /// The main entry point into the symbol table. This will traverse the AST in several
/// different ways and populate subtables with information that will be used further in the /// different ways and populate subtables with information that will be used further in the
/// compilation process. /// compilation process.
pub fn process_ast( pub fn process_ast(&mut self, ast: &ast::AST, type_context: &mut TypeContext) -> Result<(), Vec<SymbolError>> {
&mut self,
ast: &ast::AST,
type_context: &mut TypeContext,
) -> Result<(), Vec<SymbolError>> {
let mut runner = SymbolTableRunner { type_context, table: self }; let mut runner = SymbolTableRunner { type_context, table: self };
let errs = runner.populate_name_tables(ast); let errs = runner.populate_name_tables(ast);
@ -184,7 +189,8 @@ impl SymbolTable {
//TODO optimize this //TODO optimize this
pub fn lookup_symbol_by_def(&self, def: &DefId) -> Option<&Symbol> { pub fn lookup_symbol_by_def(&self, def: &DefId) -> Option<&Symbol> {
self.id_to_symbol.iter().find(|(_, sym)| sym.def_id == *def).map(|(_, sym)| sym.as_ref()) self.id_to_symbol.iter().find(|(_, sym)| sym.def_id == *def)
.map(|(_, sym)| sym.as_ref())
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -200,7 +206,11 @@ impl SymbolTable {
/// to a Symbol, a descriptor of what that name refers to. /// to a Symbol, a descriptor of what that name refers to.
fn add_symbol(&mut self, id: &ItemId, fqsn: Fqsn, spec: SymbolSpec) { fn add_symbol(&mut self, id: &ItemId, fqsn: Fqsn, spec: SymbolSpec) {
let def_id = self.def_id_store.fresh(); let def_id = self.def_id_store.fresh();
let symbol = Rc::new(Symbol { fully_qualified_name: fqsn.clone(), spec, def_id }); let symbol = Rc::new(Symbol {
fully_qualified_name: fqsn.clone(),
spec,
def_id,
});
self.symbol_trie.insert(&fqsn); self.symbol_trie.insert(&fqsn);
self.fqsn_to_symbol.insert(fqsn, symbol.clone()); self.fqsn_to_symbol.insert(fqsn, symbol.clone());
self.id_to_symbol.insert(*id, symbol); self.id_to_symbol.insert(*id, symbol);
@ -209,7 +219,7 @@ impl SymbolTable {
struct SymbolTableRunner<'a> { struct SymbolTableRunner<'a> {
type_context: &'a mut TypeContext, type_context: &'a mut TypeContext,
table: &'a mut SymbolTable, table: &'a mut SymbolTable
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -255,10 +265,16 @@ impl fmt::Display for Symbol {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SymbolSpec { pub enum SymbolSpec {
Func, Func,
// The tag and arity here are *surface* tags, computed from the order in which they were DataConstructor {
// defined. The type context may create a different ordering. tag: u32,
DataConstructor { tag: u32, arity: usize, type_id: TypeId }, arity: usize,
RecordConstructor { tag: u32, members: HashMap<Rc<String>, TypeId>, type_id: TypeId }, type_id: TypeId,
},
RecordConstructor {
tag: u32,
members: HashMap<Rc<String>, TypeId>,
type_id: TypeId,
},
GlobalBinding, //Only for global variables, not for function-local ones or ones within a `let` scope context GlobalBinding, //Only for global variables, not for function-local ones or ones within a `let` scope context
LocalVariable, LocalVariable,
FunctionParam(u8), FunctionParam(u8),
@ -269,10 +285,22 @@ impl fmt::Display for SymbolSpec {
use self::SymbolSpec::*; use self::SymbolSpec::*;
match self { match self {
Func => write!(f, "Func"), Func => write!(f, "Func"),
DataConstructor { tag, type_id, arity } => DataConstructor {
write!(f, "DataConstructor(tag: {}, arity: {}, type: {})", tag, arity, type_id), tag,
RecordConstructor { type_id, tag, .. } => type_id,
write!(f, "RecordConstructor(tag: {})(<members> -> {})", tag, type_id), arity,
} => write!(
f,
"DataConstructor(tag: {}, arity: {}, type: {})",
tag, arity, type_id
),
RecordConstructor {
type_id, tag, ..
} => write!(
f,
"RecordConstructor(tag: {})(<members> -> {})",
tag, type_id
),
GlobalBinding => write!(f, "GlobalBinding"), GlobalBinding => write!(f, "GlobalBinding"),
LocalVariable => write!(f, "Local variable"), LocalVariable => write!(f, "Local variable"),
FunctionParam(n) => write!(f, "Function param: {}", n), FunctionParam(n) => write!(f, "Function param: {}", n),
@ -313,7 +341,11 @@ impl<'a> SymbolTableRunner<'a> {
let mut errors = vec![]; let mut errors = vec![];
for statement in statements { for statement in statements {
let Statement { id, kind, location } = statement; //TODO I'm not sure if I need to do anything with this ID let Statement {
id,
kind,
location,
} = statement; //TODO I'm not sure if I need to do anything with this ID
let location = *location; let location = *location;
if let Err(err) = self.add_single_statement(id, kind, location, scope_stack, function_scope) { if let Err(err) = self.add_single_statement(id, kind, location, scope_stack, function_scope) {
errors.push(err); errors.push(err);
@ -334,8 +366,11 @@ impl<'a> SymbolTableRunner<'a> {
scope_stack.pop(); scope_stack.pop();
output output
} }
StatementKind::Declaration(Declaration::TypeDecl { name, body, mutable }) => StatementKind::Declaration(Declaration::TypeDecl {
self.add_type_members(name, body, mutable, location, scope_stack), name,
body,
mutable,
}) => self.add_type_members(name, body, mutable, location, scope_stack),
_ => vec![], _ => vec![],
}; };
errors.extend(recursive_errs.into_iter()); errors.extend(recursive_errs.into_iter());
@ -356,39 +391,87 @@ impl<'a> SymbolTableRunner<'a> {
match kind { match kind {
StatementKind::Declaration(Declaration::FuncSig(signature)) => { StatementKind::Declaration(Declaration::FuncSig(signature)) => {
let fq_function = Fqsn::from_scope_stack(scope_stack, signature.name.clone()); let fq_function = Fqsn::from_scope_stack(scope_stack, signature.name.clone());
self.table self.table.fq_names.register(
.fq_names fq_function.clone(),
.register(fq_function.clone(), NameSpec { location, kind: NameKind::Function })?; NameSpec {
self.table.types.register(fq_function.clone(), NameSpec { location, kind: TypeKind })?; location,
kind: NameKind::Function,
},
)?;
self.table.types.register(
fq_function.clone(),
NameSpec {
location,
kind: TypeKind,
},
)?;
self.add_symbol(id, fq_function, SymbolSpec::Func); self.add_symbol(
id,
fq_function,
SymbolSpec::Func,
);
} }
StatementKind::Declaration(Declaration::FuncDecl(signature, ..)) => { StatementKind::Declaration(Declaration::FuncDecl(signature, ..)) => {
let fn_name = &signature.name; let fn_name = &signature.name;
let fq_function = Fqsn::from_scope_stack(scope_stack, fn_name.clone()); let fq_function = Fqsn::from_scope_stack(scope_stack, fn_name.clone());
self.table self.table.fq_names.register(
.fq_names fq_function.clone(),
.register(fq_function.clone(), NameSpec { location, kind: NameKind::Function })?; NameSpec {
self.table.types.register(fq_function.clone(), NameSpec { location, kind: TypeKind })?; location,
kind: NameKind::Function,
},
)?;
self.table.types.register(
fq_function.clone(),
NameSpec {
location,
kind: TypeKind,
},
)?;
self.add_symbol(id, fq_function, SymbolSpec::Func); self.add_symbol(
id,
fq_function,
SymbolSpec::Func,
);
} }
StatementKind::Declaration(Declaration::TypeDecl { name, .. }) => { StatementKind::Declaration(Declaration::TypeDecl { name, .. }) => {
let fq_type = Fqsn::from_scope_stack(scope_stack, name.name.clone()); let fq_type = Fqsn::from_scope_stack(scope_stack, name.name.clone());
self.table.types.register(fq_type, NameSpec { location, kind: TypeKind })?; self.table.types.register(
fq_type,
NameSpec {
location,
kind: TypeKind,
},
)?;
} }
StatementKind::Declaration(Declaration::Binding { name, .. }) => { StatementKind::Declaration(Declaration::Binding { name, .. }) => {
let fq_binding = Fqsn::from_scope_stack(scope_stack, name.clone()); let fq_binding = Fqsn::from_scope_stack(scope_stack, name.clone());
self.table self.table.fq_names.register(
.fq_names fq_binding.clone(),
.register(fq_binding.clone(), NameSpec { location, kind: NameKind::Binding })?; NameSpec {
location,
kind: NameKind::Binding,
},
)?;
if !function_scope { if !function_scope {
self.add_symbol(id, fq_binding, SymbolSpec::GlobalBinding); self.add_symbol(
id,
fq_binding,
SymbolSpec::GlobalBinding,
);
} }
} }
StatementKind::Module(ModuleSpecifier { name, .. }) => { StatementKind::Module(ModuleSpecifier { name, .. }) => {
let fq_module = Fqsn::from_scope_stack(scope_stack, name.clone()); let fq_module = Fqsn::from_scope_stack(scope_stack, name.clone());
self.table.fq_names.register(fq_module, NameSpec { location, kind: NameKind::Module })?; self.table.fq_names.register(
fq_module,
NameSpec {
location,
kind: NameKind::Module,
},
)?;
} }
_ => (), _ => (),
} }
@ -403,93 +486,90 @@ impl<'a> SymbolTableRunner<'a> {
location: Location, location: Location,
scope_stack: &mut Vec<Scope>, scope_stack: &mut Vec<Scope>,
) -> Vec<SymbolError> { ) -> Vec<SymbolError> {
let TypeBody(variants) = type_body; let mut member_errors = vec![];
let type_fqsn = Fqsn::from_scope_stack(scope_stack, type_name.name.clone()); let mut errors = vec![];
let mut register = |id: &ItemId, fqsn: Fqsn, spec: SymbolSpec| {
let name_spec = NameSpec {
location,
kind: TypeKind,
};
if let Err(err) = self.table.types.register(fqsn.clone(), name_spec) {
errors.push(err);
} else {
self.table.add_symbol(id, fqsn, spec);
};
};
let TypeBody(variants) = type_body;
let new_scope = Scope::Name(type_name.name.clone()); let new_scope = Scope::Name(type_name.name.clone());
scope_stack.push(new_scope); scope_stack.push(new_scope);
// Check for duplicates before registering any types with the TypeContext for (index, variant) in variants.iter().enumerate() {
let mut seen_variants = HashSet::new(); let tag = index as u32;
let mut errors = vec![]; let Variant { name, kind, id } = variant;
let type_id = self.type_context.id_from_name(name.as_ref());
for variant in variants { match kind {
if seen_variants.contains(&variant.name) { VariantKind::UnitStruct => {
errors.push(SymbolError::DuplicateVariant { let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
type_fqsn: type_fqsn.clone(), let spec = SymbolSpec::DataConstructor {
name: variant.name.as_ref().to_string(), tag,
}) arity: 0,
type_id,
};
register(id, fq_name, spec);
} }
seen_variants.insert(variant.name.clone()); VariantKind::TupleStruct(items) => {
let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
let spec = SymbolSpec::DataConstructor {
tag,
arity: items.len(),
type_id,
};
register(id, fq_name, spec);
}
VariantKind::Record(members) => {
let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
if let VariantKind::Record(ref members) = variant.kind {
let variant_name = Fqsn::from_scope_stack(scope_stack.as_ref(), variant.name.clone());
let mut seen_members = HashMap::new(); let mut seen_members = HashMap::new();
for (member_name, _) in members.iter() { for (member_name, _) in members.iter() {
match seen_members.entry(member_name.as_ref()) { match seen_members.entry(member_name.as_ref()) {
Entry::Occupied(o) => { Entry::Occupied(o) => {
let location = *o.get(); let location = *o.get();
errors.push(SymbolError::DuplicateRecord { member_errors.push(SymbolError::DuplicateRecord {
type_name: variant_name.clone(), type_name: fq_name.clone(),
location, location,
member: member_name.as_ref().to_string(), member: member_name.as_ref().to_string(),
}); });
} }
//TODO eventually this should track meaningful locations //TODO eventually this should track meaningful locations
Entry::Vacant(v) => { Entry::Vacant(v) => {
v.insert(location); v.insert(Location::default());
}
}
} }
} }
} }
if !errors.is_empty() { let spec = SymbolSpec::RecordConstructor {
return errors; tag,
} type_id,
members: members
let mut type_builder = TypeBuilder::new(type_name.name.as_ref()); .iter()
.map(|(member_name, _type_identifier)| {
for variant in variants.iter() { (
let Variant { name, kind, id: _ } = variant; member_name.clone(),
self.type_context.id_from_name("DUMMY_TYPE_ID")
//TODO the order in which things get added to variant_builder determines the sematnics )
//of `tag` later })
let mut variant_builder = VariantBuilder::new(name.as_ref()); .collect(),
match kind {
VariantKind::UnitStruct => (),
VariantKind::TupleStruct(items) =>
for type_identifier in items {
let pending: PendingType = type_identifier.into();
variant_builder.add_member(pending);
},
VariantKind::Record(members) =>
for (field_name, type_identifier) in members.iter() {
let pending: PendingType = type_identifier.into();
variant_builder.add_record_member(field_name.as_ref(), pending);
},
}
type_builder.add_variant(variant_builder);
}
let type_id = self.type_context.register_type(type_builder);
for (index, variant) in variants.iter().enumerate() {
let Variant { name, kind, id } = variant;
let fqsn = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
let spec = match kind {
VariantKind::UnitStruct =>
SymbolSpec::DataConstructor { tag: index as u32, arity: 0, type_id },
VariantKind::TupleStruct(items) =>
SymbolSpec::DataConstructor { tag: index as u32, arity: items.len(), type_id },
VariantKind::Record(..) =>
SymbolSpec::RecordConstructor { tag: index as u32, members: HashMap::new(), type_id },
}; };
println!("Adding symbol {}", fqsn); register(id, fq_name, spec);
self.table.add_symbol(id, fqsn, spec); }
}
} }
scope_stack.pop(); scope_stack.pop();
vec![] errors.extend(member_errors.into_iter());
errors
} }
} }

View File

@ -1,10 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use crate::{ use crate::ast::*;
ast::*, use crate::symbol_table::{Fqsn, Scope, SymbolTable, SymbolSpec};
symbol_table::{Fqsn, Scope, SymbolSpec, SymbolTable}, use crate::util::ScopeStack;
util::ScopeStack,
};
#[derive(Debug)] #[derive(Debug)]
enum NameType { enum NameType {
@ -16,7 +14,9 @@ enum NameType {
#[derive(Debug)] #[derive(Debug)]
enum ScopeType { enum ScopeType {
Function { name: Rc<String> }, Function {
name: Rc<String>
},
Lambda, Lambda,
PatternMatch, PatternMatch,
//TODO add some notion of a let-like scope? //TODO add some notion of a let-like scope?
@ -32,13 +32,17 @@ pub struct ScopeResolver<'a> {
impl<'a> ScopeResolver<'a> { impl<'a> ScopeResolver<'a> {
pub fn new(symbol_table: &'a mut SymbolTable) -> Self { pub fn new(symbol_table: &'a mut SymbolTable) -> Self {
let lexical_scopes = ScopeStack::new(None); let lexical_scopes = ScopeStack::new(None);
Self { symbol_table, lexical_scopes } Self {
symbol_table,
lexical_scopes,
}
} }
pub fn resolve(&mut self, ast: &AST) { pub fn resolve(&mut self, ast: &AST) {
walk_ast(self, ast); walk_ast(self, ast);
} }
/* /*
fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> Fqsn { fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> Fqsn {
let QualifiedName { components, .. } = sym_name; let QualifiedName { components, .. } = sym_name;
@ -81,7 +85,7 @@ impl<'a> ScopeResolver<'a> {
if let Some(symbol) = symbol { if let Some(symbol) = symbol {
self.symbol_table.id_to_symbol.insert(*id, symbol.clone()); self.symbol_table.id_to_symbol.insert(*id, symbol.clone());
} }
} },
Some(NameType::Param(n)) => { Some(NameType::Param(n)) => {
let spec = SymbolSpec::FunctionParam(*n); let spec = SymbolSpec::FunctionParam(*n);
//TODO need to come up with a better solution for local variable FQSNs //TODO need to come up with a better solution for local variable FQSNs
@ -94,7 +98,7 @@ impl<'a> ScopeResolver<'a> {
if let Some(symbol) = symbol { if let Some(symbol) = symbol {
self.symbol_table.id_to_symbol.insert(*id, symbol); self.symbol_table.id_to_symbol.insert(*id, symbol);
} }
} },
None => { None => {
//TODO see if I can reduce this duplicate code //TODO see if I can reduce this duplicate code
let fqsn = Fqsn { scopes: vec![Scope::Name(local_name.clone())] }; let fqsn = Fqsn { scopes: vec![Scope::Name(local_name.clone())] };
@ -119,23 +123,38 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
// FQSNs map to a Symbol (or this is an error), Symbols have a DefId. So for every // FQSNs map to a Symbol (or this is an error), Symbols have a DefId. So for every
// name we import, we map a local name (a string) to a NameType::ImportedDefinition(DefId). // name we import, we map a local name (a string) to a NameType::ImportedDefinition(DefId).
fn import(&mut self, import_spec: &ImportSpecifier) -> Recursion { fn import(&mut self, import_spec: &ImportSpecifier) -> Recursion {
let ImportSpecifier { ref path_components, ref imported_names, .. } = &import_spec; let ImportSpecifier {
ref path_components,
ref imported_names,
..
} = &import_spec;
match imported_names { match imported_names {
ImportedNames::All => { ImportedNames::All => {
let prefix = let prefix = Fqsn {
Fqsn { scopes: path_components.iter().map(|c| Scope::Name(c.clone())).collect() }; scopes: path_components
.iter()
.map(|c| Scope::Name(c.clone()))
.collect(),
};
let members = self.symbol_table.symbol_trie.get_children(&prefix); let members = self.symbol_table.symbol_trie.get_children(&prefix);
for fqsn in members.into_iter() { for fqsn in members.into_iter() {
self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn)); self.lexical_scopes.insert( fqsn.local_name(), NameType::Import(fqsn));
} }
} }
ImportedNames::LastOfPath => { ImportedNames::LastOfPath => {
let fqsn = Fqsn { scopes: path_components.iter().map(|c| Scope::Name(c.clone())).collect() }; let fqsn = Fqsn {
scopes: path_components
.iter()
.map(|c| Scope::Name(c.clone()))
.collect()
};
self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn)); self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn));
} }
ImportedNames::List(ref names) => { ImportedNames::List(ref names) => {
let fqsn_prefix: Vec<Scope> = let fqsn_prefix: Vec<Scope> = path_components
path_components.iter().map(|c| Scope::Name(c.clone())).collect(); .iter()
.map(|c| Scope::Name(c.clone()))
.collect();
for name in names.iter() { for name in names.iter() {
let mut scopes = fqsn_prefix.clone(); let mut scopes = fqsn_prefix.clone();
scopes.push(Scope::Name(name.clone())); scopes.push(Scope::Name(name.clone()));
@ -151,22 +170,23 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
let cur_function_name = match self.lexical_scopes.get_name() { let cur_function_name = match self.lexical_scopes.get_name() {
//TODO this needs to be a fqsn //TODO this needs to be a fqsn
Some(ScopeType::Function { name }) => Some(name.clone()), Some(ScopeType::Function { name }) => Some(name.clone()),
_ => None, _ => None
}; };
match declaration { match declaration {
Declaration::FuncDecl(signature, block) => { Declaration::FuncDecl(signature, block) => {
let param_names = signature.params.iter().map(|param| param.name.clone()); let param_names = signature.params.iter().map(|param| param.name.clone());
//TODO I'm 90% sure this is right, until I get to closures //TODO I'm 90% sure this is right, until I get to closures
//let mut new_scope = self.lexical_scopes.new_scope(Some(ScopeType::Function { name: signature.name.clone() })); //let mut new_scope = self.lexical_scopes.new_scope(Some(ScopeType::Function { name: signature.name.clone() }));
let mut new_scope = let mut new_scope = ScopeStack::new(Some(ScopeType::Function { name: signature.name.clone() }));
ScopeStack::new(Some(ScopeType::Function { name: signature.name.clone() }));
for (n, param) in param_names.enumerate().into_iter() { for (n, param) in param_names.enumerate().into_iter() {
new_scope.insert(param, NameType::Param(n as u8)); new_scope.insert(param, NameType::Param(n as u8));
} }
let mut new_resolver = let mut new_resolver = ScopeResolver {
ScopeResolver { symbol_table: self.symbol_table, lexical_scopes: new_scope }; symbol_table: self.symbol_table,
lexical_scopes: new_scope,
};
walk_block(&mut new_resolver, block); walk_block(&mut new_resolver, block);
Recursion::Stop Recursion::Stop
} }
@ -179,7 +199,7 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
} }
Recursion::Continue Recursion::Continue
} }
_ => Recursion::Continue, _ => Recursion::Continue
} }
} }
@ -188,10 +208,10 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
match &expression.kind { match &expression.kind {
Value(name) => { Value(name) => {
self.lookup_name_in_scope(name); self.lookup_name_in_scope(name);
} },
NamedStruct { name, fields: _ } => { NamedStruct { name, fields: _ } => {
self.lookup_name_in_scope(name); self.lookup_name_in_scope(name);
} },
Lambda { params, body, .. } => { Lambda { params, body, .. } => {
let param_names = params.iter().map(|param| param.name.clone()); let param_names = params.iter().map(|param| param.name.clone());
//TODO need to properly handle closure scope, this is currently broken //TODO need to properly handle closure scope, this is currently broken
@ -202,8 +222,10 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
new_scope.insert(param, NameType::Param(n as u8)); new_scope.insert(param, NameType::Param(n as u8));
} }
let mut new_resolver = let mut new_resolver = ScopeResolver {
ScopeResolver { symbol_table: self.symbol_table, lexical_scopes: new_scope }; symbol_table: self.symbol_table,
lexical_scopes: new_scope,
};
walk_block(&mut new_resolver, body); walk_block(&mut new_resolver, body);
return Recursion::Stop; return Recursion::Stop;
} }
@ -213,25 +235,32 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
} }
let mut resolver = ScopeResolver { let mut resolver = ScopeResolver {
lexical_scopes: self.lexical_scopes.new_scope(Some(ScopeType::PatternMatch)), lexical_scopes: self.lexical_scopes.new_scope(Some(ScopeType::PatternMatch)),
symbol_table: self.symbol_table, symbol_table: self.symbol_table
}; };
let new_resolver = &mut resolver; let new_resolver = &mut resolver;
match body.as_ref() { match body.as_ref() {
IfExpressionBody::SimpleConditional { then_case, else_case } => { IfExpressionBody::SimpleConditional {
then_case,
else_case,
} => {
walk_block(new_resolver, then_case); walk_block(new_resolver, then_case);
if let Some(block) = else_case.as_ref() { if let Some(block) = else_case.as_ref() {
walk_block(new_resolver, block) walk_block(new_resolver, block)
} }
} }
IfExpressionBody::SimplePatternMatch { pattern, then_case, else_case } => { IfExpressionBody::SimplePatternMatch {
pattern,
then_case,
else_case,
} => {
walk_pattern(new_resolver, pattern); walk_pattern(new_resolver, pattern);
walk_block(new_resolver, then_case); walk_block(new_resolver, then_case);
if let Some(block) = else_case.as_ref() { if let Some(block) = else_case.as_ref() {
walk_block(new_resolver, block) walk_block(new_resolver, block)
} }
} }
IfExpressionBody::CondList(arms) => IfExpressionBody::CondList(arms) => {
for arm in arms { for arm in arms {
match arm.condition { match arm.condition {
Condition::Pattern(ref pat) => { Condition::Pattern(ref pat) => {
@ -249,11 +278,12 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
walk_expression(new_resolver, guard); walk_expression(new_resolver, guard);
} }
walk_block(new_resolver, &arm.body); walk_block(new_resolver, &arm.body);
}, }
}
}; };
return Recursion::Stop; return Recursion::Stop;
} },
_ => (), _ => (),
} }
Recursion::Continue Recursion::Continue
@ -278,14 +308,13 @@ impl<'a> ASTVisitor for ScopeResolver<'a> {
self.symbol_table.add_symbol(id, fqsn, SymbolSpec::LocalVariable); self.symbol_table.add_symbol(id, fqsn, SymbolSpec::LocalVariable);
self.lexical_scopes.insert(local_name, NameType::LocalVariable(*id)); self.lexical_scopes.insert(local_name, NameType::LocalVariable(*id));
} else { } else {
let fqsn = let fqsn = Fqsn { scopes: components.iter().map(|name| Scope::Name(name.clone())).collect() };
Fqsn { scopes: components.iter().map(|name| Scope::Name(name.clone())).collect() };
let symbol = self.symbol_table.fqsn_to_symbol.get(&fqsn); let symbol = self.symbol_table.fqsn_to_symbol.get(&fqsn);
if let Some(symbol) = symbol { if let Some(symbol) = symbol {
self.symbol_table.id_to_symbol.insert(*id, symbol.clone()); self.symbol_table.id_to_symbol.insert(*id, symbol.clone());
} }
} }
} },
}; };
Recursion::Continue Recursion::Continue
} }

View File

@ -1,11 +1,7 @@
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
use radix_trie::{Trie, TrieCommon, TrieKey};
use super::{Fqsn, Scope}; use super::{Fqsn, Scope};
use radix_trie::{Trie, TrieCommon, TrieKey};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[derive(Debug)] #[derive(Debug)]
pub struct SymbolTrie(Trie<Fqsn, ()>); pub struct SymbolTrie(Trie<Fqsn, ()>);
@ -37,7 +33,11 @@ impl SymbolTrie {
Some(s) => s, Some(s) => s,
None => return vec![], None => return vec![],
}; };
let output: Vec<Fqsn> = subtrie.keys().filter(|cur_key| **cur_key != *fqsn).cloned().collect(); let output: Vec<Fqsn> = subtrie
.keys()
.filter(|cur_key| **cur_key != *fqsn)
.cloned()
.collect();
output output
} }
} }

View File

@ -31,6 +31,8 @@ fn basic_symbol_table() {
let (symbols, _) = add_symbols(src); let (symbols, _) = add_symbols(src);
symbols.types.table.get(&make_fqsn(&["Option"])).unwrap(); symbols.types.table.get(&make_fqsn(&["Option"])).unwrap();
symbols.types.table.get(&make_fqsn(&["Option", "Some"])).unwrap();
symbols.types.table.get(&make_fqsn(&["Option", "None"])).unwrap();
} }
#[test] #[test]
@ -87,23 +89,6 @@ fn no_type_definition_duplicates() {
} }
} }
#[test]
fn no_variant_duplicates() {
let source = r#"
type Panda = FoolsGold | Kappa(i32) | Remix | Kappa | Thursday | Remix
"#;
let (_, output) = add_symbols(source);
let errs = output.unwrap_err();
assert_eq!(errs.len(), 2);
assert_matches!(&errs[0], SymbolError::DuplicateVariant {
type_fqsn, name } if *type_fqsn == Fqsn::from_strs(&["Panda"]) &&
name == "Kappa");
assert_matches!(&errs[1], SymbolError::DuplicateVariant {
type_fqsn, name } if *type_fqsn == Fqsn::from_strs(&["Panda"]) &&
name == "Remix");
}
#[test] #[test]
fn no_variable_definition_duplicates_in_function() { fn no_variable_definition_duplicates_in_function() {
let source = r#" let source = r#"

View File

@ -25,7 +25,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
for statement in reduced.entrypoint.into_iter() { for statement in reduced.entrypoint.into_iter() {
match self.statement(statement) { match self.statement(statement) {
Ok(Some(output)) if repl => acc.push(Ok(output.to_repl(&self.type_context))), Ok(Some(output)) if repl => acc.push(Ok(output.to_repl())),
Ok(_) => (), Ok(_) => (),
Err(error) => { Err(error) => {
acc.push(Err(error.msg)); acc.push(Err(error.msg));
@ -242,11 +242,11 @@ impl<'a, 'b> Evaluator<'a, 'b> {
} }
/* builtin functions */ /* builtin functions */
(IOPrint, &[ref anything]) => { (IOPrint, &[ref anything]) => {
print!("{}", anything.to_repl(self.type_context)); print!("{}", anything.to_repl());
Primitive::Tuple(vec![]) Primitive::Tuple(vec![])
} }
(IOPrintLn, &[ref anything]) => { (IOPrintLn, &[ref anything]) => {
print!("{}", anything.to_repl(self.type_context)); print!("{}", anything.to_repl());
Primitive::Tuple(vec![]) Primitive::Tuple(vec![])
} }
(IOGetLine, &[]) => { (IOGetLine, &[]) => {

View File

@ -89,9 +89,9 @@ impl From<Primitive> for MemoryValue {
} }
impl MemoryValue { impl MemoryValue {
fn to_repl(&self, type_context: &TypeContext) -> String { fn to_repl(&self) -> String {
match self { match self {
MemoryValue::Primitive(ref prim) => prim.to_repl(type_context), MemoryValue::Primitive(ref prim) => prim.to_repl(),
MemoryValue::Function(..) => "<function>".to_string(), MemoryValue::Function(..) => "<function>".to_string(),
} }
} }
@ -125,16 +125,11 @@ enum Primitive {
} }
impl Primitive { impl Primitive {
fn to_repl(&self, type_context: &TypeContext) -> String { fn to_repl(&self) -> String {
match self { match self {
Primitive::Object { type_id, items, tag } if items.is_empty() => Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(),
type_context.variant_local_name(type_id, *tag).unwrap().to_string(), Primitive::Object { type_id, items, .. } => {
Primitive::Object { type_id, items, tag } => { format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl())))
format!(
"{}{}",
type_context.variant_local_name(type_id, *tag).unwrap(),
paren_wrapped(items.iter().map(|item| item.to_repl(type_context)))
)
} }
Primitive::Literal(lit) => match lit { Primitive::Literal(lit) => match lit {
Literal::Nat(n) => format!("{}", n), Literal::Nat(n) => format!("{}", n),
@ -143,7 +138,7 @@ impl Primitive {
Literal::Bool(b) => format!("{}", b), Literal::Bool(b) => format!("{}", b),
Literal::StringLit(s) => format!("\"{}\"", s), Literal::StringLit(s) => format!("\"{}\"", s),
}, },
Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl(type_context))), Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())),
Primitive::Callable(..) => "<some-callable>".to_string(), Primitive::Callable(..) => "<some-callable>".to_string(),
} }
} }

View File

@ -1,110 +1,34 @@
use std::{collections::HashMap, convert::From}; use std::{fmt, rc::Rc};
//use crate::symbol_table::Fqsn; //TODO need to hook this into the actual typechecking system somehow
use crate::{ #[derive(Debug, Clone)]
ast::TypeIdentifier, pub struct TypeId {
identifier::{define_id_kind, Id, IdStore}, local_name: Rc<String>,
};
define_id_kind!(TypeItem);
pub type TypeId = Id<TypeItem>;
pub struct TypeContext {
defined_types: HashMap<TypeId, DefinedType>,
type_id_store: IdStore<TypeItem>,
} }
impl fmt::Display for TypeId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TypeId:{}", self.local_name())
}
}
impl TypeId {
pub fn local_name(&self) -> &str {
self.local_name.as_ref()
}
}
pub struct TypeContext;
impl TypeContext { impl TypeContext {
pub fn new() -> Self { pub fn new() -> Self {
Self { defined_types: HashMap::new(), type_id_store: IdStore::new() } Self
} }
pub fn register_type(&mut self, builder: TypeBuilder) -> TypeId { //TODO flesh this out...
let type_id = self.type_id_store.fresh(); pub fn id_from_name(&self, name: &str) -> TypeId {
TypeId { local_name: Rc::new(name.to_string()) }
let defined = DefinedType {
name: builder.name,
//TODO come up with a canonical tag order
variants: builder.variants.into_iter().map(|builder| Variant { name: builder.name }).collect(),
};
self.defined_types.insert(type_id, defined);
type_id
} }
pub fn variant_local_name(&self, type_id: &TypeId, tag: u32) -> Option<&str> {
self.defined_types
.get(type_id)
.and_then(|defined| defined.variants.get(tag as usize))
.map(|variant| variant.name.as_ref())
}
}
/// A type defined in program source code, as opposed to a builtin.
#[allow(dead_code)]
struct DefinedType {
name: String,
//fqsn: Fqsn,
// the variants are in this list according to tag order
variants: Vec<Variant>,
}
struct Variant {
name: String,
}
/// Represents a type mentioned as a member of another type during the type registration process.
/// It may not have been registered itself in the relevant context.
#[allow(dead_code)]
pub struct PendingType {
inner: TypeIdentifier,
}
impl From<&TypeIdentifier> for PendingType {
fn from(type_identifier: &TypeIdentifier) -> Self {
Self { inner: type_identifier.clone() }
}
}
pub struct TypeBuilder {
name: String,
variants: Vec<VariantBuilder>,
}
impl TypeBuilder {
pub fn new(name: &str) -> Self {
Self { name: name.to_string(), variants: vec![] }
}
pub fn add_variant(&mut self, vb: VariantBuilder) {
self.variants.push(vb);
}
}
pub struct VariantBuilder {
name: String,
members: Vec<VariantMember>,
}
impl VariantBuilder {
pub fn new(name: &str) -> Self {
Self { name: name.to_string(), members: vec![] }
}
pub fn add_member(&mut self, member_ty: PendingType) {
self.members.push(VariantMember::Pending(member_ty));
}
// You can't call this and `add_member` on the same fn, there should be a runtime error when
// that's detected.
pub fn add_record_member(&mut self, name: &str, ty: PendingType) {
self.members.push(VariantMember::KeyVal(name.to_string(), ty));
}
}
enum VariantMember {
Pending(PendingType),
KeyVal(String, PendingType),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]