use std::fmt::Debug; /// Alias for &TypeStruct to reduce repetition /// and make futura migration to interning /// easier pub type Type = InternedType; #[derive(Debug, Copy, Clone, PartialEq)] pub struct InternedType(usize); pub struct TypeStruct { kind: TypeKind, } #[derive(Debug)] enum TypeKind { /// Elaboration-time types ElabType(ElabKind), /// Signal/Wire of generic width Logic(ElabData), /// UInt of generic width UInt(ElabData), /// Callable Callable, } #[derive(Debug)] struct ElabData { typ: Type, value: ElabValue, } #[derive(Debug)] enum ElabValue { /// the value is not given and has to be inferred Infer, /// the value is given as some byte representation Concrete(ElabValueData), } #[derive(Debug)] enum ElabValueData { U32(u32), Bytes(Vec), } /// Types that are only valid during Elaboration #[derive(Debug)] enum ElabKind { /// general, unsized number type Num, } pub struct PrimitiveTypes { pub elabnum: Type, pub logic: Type, } pub struct TypingContext { types: Vec, pub primitives: PrimitiveTypes, } impl TypingContext { pub fn new() -> Self { let primitives = PrimitiveTypes { elabnum: InternedType(0), logic: InternedType(1), }; Self { types: vec![TypeStruct { kind: TypeKind::Logic(ElabData { typ: primitives.elabnum, value: ElabValue::Infer, }), }], primitives, } } pub fn add(&mut self, typ: TypeStruct) -> Type { let id = self.types.len(); self.types.push(typ); InternedType(id) } pub fn get(&self, typ: Type) -> &TypeStruct { &self.types[typ.0] } pub fn pretty_type(&self, w: &mut dyn std::fmt::Write, typ: Type) -> std::fmt::Result { match &self.get(typ).kind { TypeKind::ElabType(val) => write!(w, "{{{:?}}}", val), TypeKind::Logic(_) => todo!("print logic"), TypeKind::UInt(_) => todo!("print uint"), TypeKind::Callable => todo!("print callable"), } } }