Have TypeContext calculate tag numbers

This commit is contained in:
Greg Shuflin 2021-10-29 17:27:21 -07:00
parent 209b6bba48
commit 6b9ca92e00
2 changed files with 89 additions and 31 deletions

View File

@ -1,3 +1,5 @@
#![allow(clippy::enum_variant_names)]
use std::{ use std::{
collections::{hash_map::Entry, HashMap, HashSet}, collections::{hash_map::Entry, HashMap, HashSet},
fmt, fmt,
@ -11,7 +13,7 @@ use crate::{
VariantKind, VariantKind,
}, },
tokenizing::Location, tokenizing::Location,
type_inference::{PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder}, type_inference::{self, PendingType, TypeBuilder, TypeContext, TypeId, VariantBuilder},
}; };
mod resolver; mod resolver;
@ -450,11 +452,12 @@ impl<'a> SymbolTableRunner<'a> {
let mut type_builder = TypeBuilder::new(type_name.name.as_ref()); let mut type_builder = TypeBuilder::new(type_name.name.as_ref());
let mut fqsn_id_map = HashMap::new();
for variant in variants.iter() { 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()); let mut variant_builder = VariantBuilder::new(name.as_ref());
match kind { match kind {
VariantKind::UnitStruct => (), VariantKind::UnitStruct => (),
@ -473,19 +476,21 @@ impl<'a> SymbolTableRunner<'a> {
} }
let type_id = self.type_context.register_type(type_builder); 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() { // This index is guaranteed to be the correct tag
let Variant { name, kind, id } = variant; for (index, variant) in type_definition.variants.iter().enumerate() {
let fqsn = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone()); let fqsn = Fqsn::from_scope_stack(scope_stack.as_ref(), Rc::new(variant.name.to_string()));
let spec = match kind { let id = fqsn_id_map.get(&fqsn).unwrap();
VariantKind::UnitStruct => let tag = index as u32;
SymbolSpec::DataConstructor { tag: index as u32, arity: 0, type_id }, let spec = match &variant.members {
VariantKind::TupleStruct(items) => type_inference::VariantMembers::Unit =>
SymbolSpec::DataConstructor { tag: index as u32, arity: items.len(), type_id }, SymbolSpec::DataConstructor { tag, arity: 0, type_id },
VariantKind::Record(..) => type_inference::VariantMembers::Tuple(items) =>
SymbolSpec::RecordConstructor { tag: index as u32, members: HashMap::new(), type_id }, 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); self.table.add_symbol(id, fqsn, spec);
} }

View File

@ -1,6 +1,5 @@
use std::{collections::HashMap, convert::From}; use std::{collections::HashMap, convert::From};
//use crate::symbol_table::Fqsn;
use crate::{ use crate::{
ast::TypeIdentifier, ast::TypeIdentifier,
identifier::{define_id_kind, Id, IdStore}, identifier::{define_id_kind, Id, IdStore},
@ -22,11 +21,51 @@ impl TypeContext {
pub fn register_type(&mut self, builder: TypeBuilder) -> TypeId { pub fn register_type(&mut self, builder: TypeBuilder) -> TypeId {
let type_id = self.type_id_store.fresh(); let type_id = self.type_id_store.fresh();
let defined = DefinedType { let mut pending_variants = vec![];
name: builder.name, for variant_builder in builder.variants.into_iter() {
//TODO come up with a canonical tag order let members = variant_builder.members;
variants: builder.variants.into_iter().map(|builder| Variant { name: builder.name }).collect(), 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); self.defined_types.insert(type_id, defined);
type_id type_id
@ -38,24 +77,37 @@ impl TypeContext {
.and_then(|defined| defined.variants.get(tag as usize)) .and_then(|defined| defined.variants.get(tag as usize))
.map(|variant| variant.name.as_ref()) .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. /// A type defined in program source code, as opposed to a builtin.
#[allow(dead_code)] #[allow(dead_code)]
struct DefinedType { pub struct DefinedType {
name: String, pub name: String,
//fqsn: Fqsn,
// the variants are in this list according to tag order // the variants are in this list according to tag order
variants: Vec<Variant>, pub variants: Vec<Variant>,
} }
struct Variant { pub struct Variant {
name: String, pub name: String,
pub members: VariantMembers,
}
pub enum VariantMembers {
Unit,
// Should be non-empty
Tuple(Vec<TypeId>),
Record(Vec<(String, TypeId)>),
} }
/// Represents a type mentioned as a member of another type during the type registration process. /// 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. /// It may not have been registered itself in the relevant context.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)]
pub struct PendingType { pub struct PendingType {
inner: TypeIdentifier, inner: TypeIdentifier,
} }
@ -83,7 +135,7 @@ impl TypeBuilder {
pub struct VariantBuilder { pub struct VariantBuilder {
name: String, name: String,
members: Vec<VariantMember>, members: Vec<VariantMemberBuilder>,
} }
impl VariantBuilder { impl VariantBuilder {
@ -92,17 +144,18 @@ impl VariantBuilder {
} }
pub fn add_member(&mut self, member_ty: PendingType) { 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 // You can't call this and `add_member` on the same fn, there should be a runtime error when
// that's detected. // that's detected.
pub fn add_record_member(&mut self, name: &str, ty: PendingType) { 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), Pending(PendingType),
KeyVal(String, PendingType), KeyVal(String, PendingType),
} }