From 209b6bba48bef616aea55794129ec6c3af9b545b Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Fri, 29 Oct 2021 04:01:38 -0700 Subject: [PATCH] Start adding infrastructure for defining new types --- schala-lang/language/src/symbol_table/mod.rs | 367 +++++++----------- .../language/src/symbol_table/resolver.rs | 99 ++--- .../language/src/symbol_table/symbol_trie.rs | 16 +- schala-lang/language/src/symbol_table/test.rs | 2 - .../language/src/tree_walk_eval/evaluator.rs | 6 +- .../language/src/tree_walk_eval/mod.rs | 19 +- .../language/src/type_inference/mod.rs | 122 ++++-- 7 files changed, 287 insertions(+), 344 deletions(-) diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index c801c17..0d1d3b7 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -1,20 +1,24 @@ -use std::collections::{hash_map::Entry, HashMap, HashSet}; -use std::fmt; -use std::rc::Rc; - -use crate::ast; -use crate::ast::{ - Declaration, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, TypeSingletonName, - Variant, VariantKind, +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + fmt, + rc::Rc, +}; + +use crate::{ + ast, + ast::{ + Declaration, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, TypeSingletonName, Variant, + VariantKind, + }, + tokenizing::Location, + type_inference::{PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder}, }; -use crate::tokenizing::Location; -use crate::type_inference::{TypeContext, TypeId}; mod resolver; mod symbol_trie; use symbol_trie::SymbolTrie; mod test; -use crate::identifier::{Id, IdStore, define_id_kind}; +use crate::identifier::{define_id_kind, Id, IdStore}; define_id_kind!(DefItem); pub type DefId = Id; @@ -63,7 +67,6 @@ impl fmt::Display for Fqsn { } } - //TODO eventually this should use ItemId's to avoid String-cloning /// One segment within a scope. #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] @@ -81,19 +84,9 @@ impl fmt::Display for Scope { #[allow(dead_code)] #[derive(Debug, Clone)] pub enum SymbolError { - DuplicateName { - prev_name: Fqsn, - location: Location, - }, - DuplicateVariant { - type_fqsn: Fqsn, - name: String, - }, - DuplicateRecord { - type_name: Fqsn, - location: Location, - member: String, - }, + DuplicateName { prev_name: Fqsn, location: Location }, + DuplicateVariant { type_fqsn: Fqsn, name: String }, + DuplicateRecord { type_name: Fqsn, location: Location, member: String }, } #[allow(dead_code)] @@ -120,17 +113,13 @@ struct NameTable { impl NameTable { fn new() -> Self { - Self { - table: HashMap::new(), - } + Self { table: HashMap::new() } } fn register(&mut self, name: Fqsn, spec: NameSpec) -> Result<(), SymbolError> { match self.table.entry(name.clone()) { - Entry::Occupied(o) => Err(SymbolError::DuplicateName { - prev_name: name, - location: o.get().location, - }), + Entry::Occupied(o) => + Err(SymbolError::DuplicateName { prev_name: name, location: o.get().location }), Entry::Vacant(v) => { v.insert(spec); Ok(()) @@ -141,7 +130,6 @@ impl NameTable { //cf. p. 150 or so of Language Implementation Patterns pub struct SymbolTable { - def_id_store: IdStore, /// Used for import resolution. @@ -175,8 +163,11 @@ impl SymbolTable { /// 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 /// compilation process. - pub fn process_ast(&mut self, ast: &ast::AST, type_context: &mut TypeContext) -> Result<(), Vec> { - + pub fn process_ast( + &mut self, + ast: &ast::AST, + type_context: &mut TypeContext, + ) -> Result<(), Vec> { let mut runner = SymbolTableRunner { type_context, table: self }; let errs = runner.populate_name_tables(ast); @@ -193,8 +184,7 @@ impl SymbolTable { //TODO optimize this 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)] @@ -210,11 +200,7 @@ impl SymbolTable { /// to a Symbol, a descriptor of what that name refers to. fn add_symbol(&mut self, id: &ItemId, fqsn: Fqsn, spec: SymbolSpec) { 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.fqsn_to_symbol.insert(fqsn, symbol.clone()); self.id_to_symbol.insert(*id, symbol); @@ -223,7 +209,7 @@ impl SymbolTable { struct SymbolTableRunner<'a> { type_context: &'a mut TypeContext, - table: &'a mut SymbolTable + table: &'a mut SymbolTable, } #[allow(dead_code)] @@ -269,16 +255,10 @@ impl fmt::Display for Symbol { #[derive(Debug, Clone)] pub enum SymbolSpec { Func, - DataConstructor { - tag: u32, - arity: usize, - type_id: TypeId, - }, - RecordConstructor { - tag: u32, - members: HashMap, TypeId>, - type_id: TypeId, - }, + // The tag and arity here are *surface* tags, computed from the order in which they were + // defined. The type context may create a different ordering. + DataConstructor { tag: u32, arity: usize, type_id: TypeId }, + RecordConstructor { tag: u32, members: HashMap, TypeId>, type_id: TypeId }, GlobalBinding, //Only for global variables, not for function-local ones or ones within a `let` scope context LocalVariable, FunctionParam(u8), @@ -289,22 +269,10 @@ impl fmt::Display for SymbolSpec { use self::SymbolSpec::*; match self { Func => write!(f, "Func"), - DataConstructor { - tag, - type_id, - arity, - } => write!( - f, - "DataConstructor(tag: {}, arity: {}, type: {})", - tag, arity, type_id - ), - RecordConstructor { - type_id, tag, .. - } => write!( - f, - "RecordConstructor(tag: {})( -> {})", - tag, type_id - ), + DataConstructor { tag, type_id, arity } => + write!(f, "DataConstructor(tag: {}, arity: {}, type: {})", tag, arity, type_id), + RecordConstructor { type_id, tag, .. } => + write!(f, "RecordConstructor(tag: {})( -> {})", tag, type_id), GlobalBinding => write!(f, "GlobalBinding"), LocalVariable => write!(f, "Local variable"), FunctionParam(n) => write!(f, "Function param: {}", n), @@ -345,11 +313,7 @@ impl<'a> SymbolTableRunner<'a> { let mut errors = vec![]; 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; if let Err(err) = self.add_single_statement(id, kind, location, scope_stack, function_scope) { errors.push(err); @@ -370,11 +334,8 @@ impl<'a> SymbolTableRunner<'a> { scope_stack.pop(); output } - StatementKind::Declaration(Declaration::TypeDecl { - name, - body, - mutable, - }) => self.add_type_members(name, body, mutable, location, scope_stack), + StatementKind::Declaration(Declaration::TypeDecl { name, body, mutable }) => + self.add_type_members(name, body, mutable, location, scope_stack), _ => vec![], }; errors.extend(recursive_errs.into_iter()); @@ -395,87 +356,39 @@ impl<'a> SymbolTableRunner<'a> { match kind { StatementKind::Declaration(Declaration::FuncSig(signature)) => { let fq_function = Fqsn::from_scope_stack(scope_stack, signature.name.clone()); - self.table.fq_names.register( - fq_function.clone(), - NameSpec { - location, - kind: NameKind::Function, - }, - )?; - self.table.types.register( - fq_function.clone(), - NameSpec { - location, - kind: TypeKind, - }, - )?; + self.table + .fq_names + .register(fq_function.clone(), NameSpec { 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, ..)) => { let fn_name = &signature.name; let fq_function = Fqsn::from_scope_stack(scope_stack, fn_name.clone()); - self.table.fq_names.register( - fq_function.clone(), - NameSpec { - location, - kind: NameKind::Function, - }, - )?; - self.table.types.register( - fq_function.clone(), - NameSpec { - location, - kind: TypeKind, - }, - )?; + self.table + .fq_names + .register(fq_function.clone(), NameSpec { 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, .. }) => { 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, .. }) => { let fq_binding = Fqsn::from_scope_stack(scope_stack, name.clone()); - self.table.fq_names.register( - fq_binding.clone(), - NameSpec { - location, - kind: NameKind::Binding, - }, - )?; + self.table + .fq_names + .register(fq_binding.clone(), NameSpec { location, kind: NameKind::Binding })?; if !function_scope { - self.add_symbol( - id, - fq_binding, - SymbolSpec::GlobalBinding, - ); + self.add_symbol(id, fq_binding, SymbolSpec::GlobalBinding); } } StatementKind::Module(ModuleSpecifier { name, .. }) => { 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 })?; } _ => (), } @@ -490,113 +403,93 @@ impl<'a> SymbolTableRunner<'a> { location: Location, scope_stack: &mut Vec, ) -> Vec { - - let type_fqsn = Fqsn::from_scope_stack(scope_stack, type_name.name.clone()); - let TypeBody(variants) = type_body; - let mut duplicates = HashSet::new(); - let mut dup_errors = vec![]; - - for variant in variants { - if duplicates.contains(&variant.name) { - dup_errors.push(SymbolError::DuplicateVariant { - type_fqsn: type_fqsn.clone(), - name: variant.name.as_ref().to_string() - }) - } - duplicates.insert(variant.name.clone()); - } - - if !dup_errors.is_empty() { - return dup_errors; - } - - - - - let mut member_errors = vec![]; - 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 type_fqsn = Fqsn::from_scope_stack(scope_stack, type_name.name.clone()); let new_scope = Scope::Name(type_name.name.clone()); scope_stack.push(new_scope); - for (index, variant) in variants.iter().enumerate() { - let tag = index as u32; - let Variant { name, kind, id } = variant; - let type_id = self.type_context.id_from_name(name.as_ref()); + // Check for duplicates before registering any types with the TypeContext + let mut seen_variants = HashSet::new(); + let mut errors = vec![]; - match kind { - VariantKind::UnitStruct => { - let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone()); - let spec = SymbolSpec::DataConstructor { - tag, - arity: 0, - type_id, - }; - register(id, fq_name, spec); - } - 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()); + for variant in variants { + if seen_variants.contains(&variant.name) { + errors.push(SymbolError::DuplicateVariant { + type_fqsn: type_fqsn.clone(), + name: variant.name.as_ref().to_string(), + }) + } + seen_variants.insert(variant.name.clone()); - let mut seen_members = HashMap::new(); - for (member_name, _) in members.iter() { - match seen_members.entry(member_name.as_ref()) { - Entry::Occupied(o) => { - let location = *o.get(); - member_errors.push(SymbolError::DuplicateRecord { - type_name: fq_name.clone(), - location, - member: member_name.as_ref().to_string(), - }); - } - //TODO eventually this should track meaningful locations - Entry::Vacant(v) => { - v.insert(Location::default()); - } + 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(); + for (member_name, _) in members.iter() { + match seen_members.entry(member_name.as_ref()) { + Entry::Occupied(o) => { + let location = *o.get(); + errors.push(SymbolError::DuplicateRecord { + type_name: variant_name.clone(), + location, + member: member_name.as_ref().to_string(), + }); + } + //TODO eventually this should track meaningful locations + Entry::Vacant(v) => { + v.insert(location); } } - - let spec = SymbolSpec::RecordConstructor { - tag, - type_id, - members: members - .iter() - .map(|(member_name, _type_identifier)| { - ( - member_name.clone(), - self.type_context.id_from_name("DUMMY_TYPE_ID") - ) - }) - .collect(), - }; - register(id, fq_name, spec); } } } + if !errors.is_empty() { + return errors; + } + + let mut type_builder = TypeBuilder::new(type_name.name.as_ref()); + + for variant in variants.iter() { + let Variant { name, kind, id: _ } = variant; + + //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()); + 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); + self.table.add_symbol(id, fqsn, spec); + } + scope_stack.pop(); - errors.extend(member_errors.into_iter()); - errors + vec![] } } diff --git a/schala-lang/language/src/symbol_table/resolver.rs b/schala-lang/language/src/symbol_table/resolver.rs index caaa67c..b589399 100644 --- a/schala-lang/language/src/symbol_table/resolver.rs +++ b/schala-lang/language/src/symbol_table/resolver.rs @@ -1,8 +1,10 @@ use std::rc::Rc; -use crate::ast::*; -use crate::symbol_table::{Fqsn, Scope, SymbolTable, SymbolSpec}; -use crate::util::ScopeStack; +use crate::{ + ast::*, + symbol_table::{Fqsn, Scope, SymbolSpec, SymbolTable}, + util::ScopeStack, +}; #[derive(Debug)] enum NameType { @@ -14,9 +16,7 @@ enum NameType { #[derive(Debug)] enum ScopeType { - Function { - name: Rc - }, + Function { name: Rc }, Lambda, PatternMatch, //TODO add some notion of a let-like scope? @@ -32,17 +32,13 @@ pub struct ScopeResolver<'a> { impl<'a> ScopeResolver<'a> { pub fn new(symbol_table: &'a mut SymbolTable) -> Self { let lexical_scopes = ScopeStack::new(None); - Self { - symbol_table, - lexical_scopes, - } + Self { symbol_table, lexical_scopes } } pub fn resolve(&mut self, ast: &AST) { walk_ast(self, ast); } - /* fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> Fqsn { let QualifiedName { components, .. } = sym_name; @@ -85,7 +81,7 @@ impl<'a> ScopeResolver<'a> { if let Some(symbol) = symbol { self.symbol_table.id_to_symbol.insert(*id, symbol.clone()); } - }, + } Some(NameType::Param(n)) => { let spec = SymbolSpec::FunctionParam(*n); //TODO need to come up with a better solution for local variable FQSNs @@ -98,7 +94,7 @@ impl<'a> ScopeResolver<'a> { if let Some(symbol) = symbol { self.symbol_table.id_to_symbol.insert(*id, symbol); } - }, + } None => { //TODO see if I can reduce this duplicate code let fqsn = Fqsn { scopes: vec![Scope::Name(local_name.clone())] }; @@ -123,38 +119,23 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { // 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). 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 { ImportedNames::All => { - let prefix = Fqsn { - scopes: path_components - .iter() - .map(|c| Scope::Name(c.clone())) - .collect(), - }; + let prefix = + Fqsn { scopes: path_components.iter().map(|c| Scope::Name(c.clone())).collect() }; let members = self.symbol_table.symbol_trie.get_children(&prefix); 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 => { - 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)); } ImportedNames::List(ref names) => { - let fqsn_prefix: Vec = path_components - .iter() - .map(|c| Scope::Name(c.clone())) - .collect(); + let fqsn_prefix: Vec = + path_components.iter().map(|c| Scope::Name(c.clone())).collect(); for name in names.iter() { let mut scopes = fqsn_prefix.clone(); scopes.push(Scope::Name(name.clone())); @@ -170,23 +151,22 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { let cur_function_name = match self.lexical_scopes.get_name() { //TODO this needs to be a fqsn Some(ScopeType::Function { name }) => Some(name.clone()), - _ => None + _ => None, }; match declaration { Declaration::FuncDecl(signature, block) => { let param_names = signature.params.iter().map(|param| param.name.clone()); //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 = ScopeStack::new(Some(ScopeType::Function { name: signature.name.clone() })); + let mut new_scope = + ScopeStack::new(Some(ScopeType::Function { name: signature.name.clone() })); for (n, param) in param_names.enumerate().into_iter() { new_scope.insert(param, NameType::Param(n as u8)); } - let mut new_resolver = ScopeResolver { - symbol_table: self.symbol_table, - lexical_scopes: new_scope, - }; + let mut new_resolver = + ScopeResolver { symbol_table: self.symbol_table, lexical_scopes: new_scope }; walk_block(&mut new_resolver, block); Recursion::Stop } @@ -199,7 +179,7 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { } Recursion::Continue } - _ => Recursion::Continue + _ => Recursion::Continue, } } @@ -208,10 +188,10 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { match &expression.kind { Value(name) => { self.lookup_name_in_scope(name); - }, + } NamedStruct { name, fields: _ } => { self.lookup_name_in_scope(name); - }, + } Lambda { params, body, .. } => { let param_names = params.iter().map(|param| param.name.clone()); //TODO need to properly handle closure scope, this is currently broken @@ -222,10 +202,8 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { new_scope.insert(param, NameType::Param(n as u8)); } - let mut new_resolver = ScopeResolver { - symbol_table: self.symbol_table, - lexical_scopes: new_scope, - }; + let mut new_resolver = + ScopeResolver { symbol_table: self.symbol_table, lexical_scopes: new_scope }; walk_block(&mut new_resolver, body); return Recursion::Stop; } @@ -235,32 +213,25 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { } let mut resolver = ScopeResolver { 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; match body.as_ref() { - IfExpressionBody::SimpleConditional { - then_case, - else_case, - } => { + IfExpressionBody::SimpleConditional { then_case, else_case } => { walk_block(new_resolver, then_case); if let Some(block) = else_case.as_ref() { 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_block(new_resolver, then_case); if let Some(block) = else_case.as_ref() { walk_block(new_resolver, block) } } - IfExpressionBody::CondList(arms) => { + IfExpressionBody::CondList(arms) => for arm in arms { match arm.condition { Condition::Pattern(ref pat) => { @@ -278,12 +249,11 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { walk_expression(new_resolver, guard); } walk_block(new_resolver, &arm.body); - } - } + }, }; return Recursion::Stop; - }, + } _ => (), } Recursion::Continue @@ -308,13 +278,14 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { self.symbol_table.add_symbol(id, fqsn, SymbolSpec::LocalVariable); self.lexical_scopes.insert(local_name, NameType::LocalVariable(*id)); } else { - let fqsn = Fqsn { scopes: components.iter().map(|name| Scope::Name(name.clone())).collect() }; + let fqsn = + Fqsn { scopes: components.iter().map(|name| Scope::Name(name.clone())).collect() }; let symbol = self.symbol_table.fqsn_to_symbol.get(&fqsn); if let Some(symbol) = symbol { self.symbol_table.id_to_symbol.insert(*id, symbol.clone()); } } - }, + } }; Recursion::Continue } diff --git a/schala-lang/language/src/symbol_table/symbol_trie.rs b/schala-lang/language/src/symbol_table/symbol_trie.rs index 013df06..2e02c66 100644 --- a/schala-lang/language/src/symbol_table/symbol_trie.rs +++ b/schala-lang/language/src/symbol_table/symbol_trie.rs @@ -1,7 +1,11 @@ -use super::{Fqsn, Scope}; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; + use radix_trie::{Trie, TrieCommon, TrieKey}; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; + +use super::{Fqsn, Scope}; #[derive(Debug)] pub struct SymbolTrie(Trie); @@ -33,11 +37,7 @@ impl SymbolTrie { Some(s) => s, None => return vec![], }; - let output: Vec = subtrie - .keys() - .filter(|cur_key| **cur_key != *fqsn) - .cloned() - .collect(); + let output: Vec = subtrie.keys().filter(|cur_key| **cur_key != *fqsn).cloned().collect(); output } } diff --git a/schala-lang/language/src/symbol_table/test.rs b/schala-lang/language/src/symbol_table/test.rs index 63aed0b..3f2c195 100644 --- a/schala-lang/language/src/symbol_table/test.rs +++ b/schala-lang/language/src/symbol_table/test.rs @@ -31,8 +31,6 @@ fn basic_symbol_table() { let (symbols, _) = add_symbols(src); 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] diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs index 3aa35a4..20c5a15 100644 --- a/schala-lang/language/src/tree_walk_eval/evaluator.rs +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -25,7 +25,7 @@ impl<'a, 'b> Evaluator<'a, 'b> { for statement in reduced.entrypoint.into_iter() { match self.statement(statement) { - Ok(Some(output)) if repl => acc.push(Ok(output.to_repl())), + Ok(Some(output)) if repl => acc.push(Ok(output.to_repl(&self.type_context))), Ok(_) => (), Err(error) => { acc.push(Err(error.msg)); @@ -242,11 +242,11 @@ impl<'a, 'b> Evaluator<'a, 'b> { } /* builtin functions */ (IOPrint, &[ref anything]) => { - print!("{}", anything.to_repl()); + print!("{}", anything.to_repl(self.type_context)); Primitive::Tuple(vec![]) } (IOPrintLn, &[ref anything]) => { - print!("{}", anything.to_repl()); + print!("{}", anything.to_repl(self.type_context)); Primitive::Tuple(vec![]) } (IOGetLine, &[]) => { diff --git a/schala-lang/language/src/tree_walk_eval/mod.rs b/schala-lang/language/src/tree_walk_eval/mod.rs index 72709ef..568622b 100644 --- a/schala-lang/language/src/tree_walk_eval/mod.rs +++ b/schala-lang/language/src/tree_walk_eval/mod.rs @@ -89,9 +89,9 @@ impl From for MemoryValue { } impl MemoryValue { - fn to_repl(&self) -> String { + fn to_repl(&self, type_context: &TypeContext) -> String { match self { - MemoryValue::Primitive(ref prim) => prim.to_repl(), + MemoryValue::Primitive(ref prim) => prim.to_repl(type_context), MemoryValue::Function(..) => "".to_string(), } } @@ -125,11 +125,16 @@ enum Primitive { } impl Primitive { - fn to_repl(&self) -> String { + fn to_repl(&self, type_context: &TypeContext) -> String { match self { - Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(), - Primitive::Object { type_id, items, .. } => { - format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl()))) + Primitive::Object { type_id, items, tag } if items.is_empty() => + type_context.variant_local_name(type_id, *tag).unwrap().to_string(), + Primitive::Object { type_id, items, tag } => { + 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 { Literal::Nat(n) => format!("{}", n), @@ -138,7 +143,7 @@ impl Primitive { Literal::Bool(b) => format!("{}", b), Literal::StringLit(s) => format!("\"{}\"", s), }, - Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())), + Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl(type_context))), Primitive::Callable(..) => "".to_string(), } } diff --git a/schala-lang/language/src/type_inference/mod.rs b/schala-lang/language/src/type_inference/mod.rs index 062e113..a4a8761 100644 --- a/schala-lang/language/src/type_inference/mod.rs +++ b/schala-lang/language/src/type_inference/mod.rs @@ -1,34 +1,110 @@ -use std::{fmt, rc::Rc}; +use std::{collections::HashMap, convert::From}; -//TODO need to hook this into the actual typechecking system somehow -#[derive(Debug, Clone)] -pub struct TypeId { - local_name: Rc, +//use crate::symbol_table::Fqsn; +use crate::{ + ast::TypeIdentifier, + identifier::{define_id_kind, Id, IdStore}, +}; + +define_id_kind!(TypeItem); +pub type TypeId = Id; + +pub struct TypeContext { + defined_types: HashMap, + type_id_store: IdStore, } -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 { pub fn new() -> Self { - Self + Self { defined_types: HashMap::new(), type_id_store: IdStore::new() } } - //TODO flesh this out... - pub fn id_from_name(&self, name: &str) -> TypeId { - TypeId { local_name: Rc::new(name.to_string()) } + pub fn register_type(&mut self, builder: TypeBuilder) -> TypeId { + let type_id = self.type_id_store.fresh(); + + 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, +} + +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, +} + +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, +} + +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)]