From 6b9ca92e009dedb9fb8a14e4ee1bafebbd176fe6 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Fri, 29 Oct 2021 17:27:21 -0700 Subject: [PATCH] Have TypeContext calculate tag numbers --- schala-lang/language/src/symbol_table/mod.rs | 35 ++++---- .../language/src/type_inference/mod.rs | 85 +++++++++++++++---- 2 files changed, 89 insertions(+), 31 deletions(-) diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index 0d1d3b7..9089840 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::enum_variant_names)] + use std::{ collections::{hash_map::Entry, HashMap, HashSet}, fmt, @@ -11,7 +13,7 @@ use crate::{ VariantKind, }, tokenizing::Location, - type_inference::{PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder}, + type_inference::{self, PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder}, }; mod resolver; @@ -450,11 +452,12 @@ impl<'a> SymbolTableRunner<'a> { let mut type_builder = TypeBuilder::new(type_name.name.as_ref()); + let mut fqsn_id_map = HashMap::new(); for variant in variants.iter() { - let Variant { name, kind, id: _ } = variant; + let Variant { name, kind, id } = variant; + + fqsn_id_map.insert(Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone()), 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()); match kind { VariantKind::UnitStruct => (), @@ -473,19 +476,21 @@ impl<'a> SymbolTableRunner<'a> { } let type_id = self.type_context.register_type(type_builder); + let type_definition = self.type_context.lookup_type(&type_id).unwrap(); - 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 }, + // This index is guaranteed to be the correct tag + for (index, variant) in type_definition.variants.iter().enumerate() { + let fqsn = Fqsn::from_scope_stack(scope_stack.as_ref(), Rc::new(variant.name.to_string())); + let id = fqsn_id_map.get(&fqsn).unwrap(); + let tag = index as u32; + let spec = match &variant.members { + type_inference::VariantMembers::Unit => + SymbolSpec::DataConstructor { tag, arity: 0, type_id }, + type_inference::VariantMembers::Tuple(items) => + SymbolSpec::DataConstructor { tag, arity: items.len(), type_id }, + type_inference::VariantMembers::Record(..) => + SymbolSpec::RecordConstructor { tag, members: HashMap::new(), type_id }, }; - println!("Adding symbol {}", fqsn); self.table.add_symbol(id, fqsn, spec); } diff --git a/schala-lang/language/src/type_inference/mod.rs b/schala-lang/language/src/type_inference/mod.rs index a4a8761..ad04b82 100644 --- a/schala-lang/language/src/type_inference/mod.rs +++ b/schala-lang/language/src/type_inference/mod.rs @@ -1,6 +1,5 @@ use std::{collections::HashMap, convert::From}; -//use crate::symbol_table::Fqsn; use crate::{ ast::TypeIdentifier, identifier::{define_id_kind, Id, IdStore}, @@ -22,11 +21,51 @@ impl TypeContext { 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(), - }; + let mut pending_variants = vec![]; + for variant_builder in builder.variants.into_iter() { + let members = variant_builder.members; + if members.is_empty() { + pending_variants.push(Variant { name: variant_builder.name, members: VariantMembers::Unit }); + break; + } + let record_variant = matches!(members.get(0).unwrap(), VariantMemberBuilder::KeyVal(..)); + + if record_variant { + let pending_members = members + .into_iter() + .map(|var| match var { + VariantMemberBuilder::KeyVal(name, ty) => (name, ty), + _ => panic!("Compiler internal error: variant mismatch"), + }); + + //TODO make this mapping meaningful + let type_ids = pending_members + .into_iter() + .map(|(name, _ty_id)| (name, self.type_id_store.fresh())) + .collect(); + pending_variants + .push(Variant { name: variant_builder.name, members: VariantMembers::Record(type_ids) }); + } else { + let pending_members = members + .into_iter() + .map(|var| match var { + VariantMemberBuilder::Pending(pending_type) => pending_type, + _ => panic!("Compiler internal error: variant mismatch"), + }); + + //TODO make this mapping meaningful + let type_ids = pending_members.into_iter().map(|_ty_id| self.type_id_store.fresh()).collect(); + + pending_variants + .push(Variant { name: variant_builder.name, members: VariantMembers::Tuple(type_ids) }); + } + } + + // Eventually, I will want to have a better way of determining which numeric tag goes with + // which variant. For now, just sort them alphabetically. + pending_variants.sort_unstable_by(|a, b| a.name.cmp(&b.name)); + + let defined = DefinedType { name: builder.name, variants: pending_variants }; self.defined_types.insert(type_id, defined); type_id @@ -38,24 +77,37 @@ impl TypeContext { .and_then(|defined| defined.variants.get(tag as usize)) .map(|variant| variant.name.as_ref()) } + + pub fn lookup_type(&self, type_id: &TypeId) -> Option<&DefinedType> { + self.defined_types.get(type_id) + } } /// A type defined in program source code, as opposed to a builtin. #[allow(dead_code)] -struct DefinedType { - name: String, - //fqsn: Fqsn, +pub struct DefinedType { + pub name: String, + // the variants are in this list according to tag order - variants: Vec, + pub variants: Vec, } -struct Variant { - name: String, +pub struct Variant { + pub name: String, + pub members: VariantMembers, +} + +pub enum VariantMembers { + Unit, + // Should be non-empty + Tuple(Vec), + Record(Vec<(String, TypeId)>), } /// 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)] +#[derive(Debug)] pub struct PendingType { inner: TypeIdentifier, } @@ -83,7 +135,7 @@ impl TypeBuilder { pub struct VariantBuilder { name: String, - members: Vec, + members: Vec, } impl VariantBuilder { @@ -92,17 +144,18 @@ impl VariantBuilder { } pub fn add_member(&mut self, member_ty: PendingType) { - self.members.push(VariantMember::Pending(member_ty)); + self.members.push(VariantMemberBuilder::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)); + self.members.push(VariantMemberBuilder::KeyVal(name.to_string(), ty)); } } -enum VariantMember { +#[derive(Debug)] +enum VariantMemberBuilder { Pending(PendingType), KeyVal(String, PendingType), }