use std::fmt::Debug; /// Alias for &TypeStruct to reduce repetition /// and make futura migration to interning /// easier pub type Type = InternedType; #[derive(Copy, Clone, PartialEq)] pub struct InternedType(usize); impl Debug for InternedType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Type({})", self.0) } } #[derive(Debug)] 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(FnSig), /// A type that was not given and needs to be inferred Infer, } #[derive(Debug, Clone)] pub struct FnSig { params: Vec, ret: Type, } #[derive(Debug, Clone)] pub struct ElabData { typ: Type, value: ElabValue, } #[derive(Debug, Clone)] 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, Clone)] enum ElabValueData { U32(u32), Bytes(Vec), } /// Types that are only valid during Elaboration #[derive(Debug)] enum ElabKind { /// general, unsized number type Num, } #[derive(Debug)] pub enum GenericArg { Elab(ElabData), Type(Type), } pub struct PrimitiveTypes { pub elabnum: Type, pub logic: Type, pub infer: Type, } pub struct TypingContext { types: Vec, pub primitives: PrimitiveTypes, } impl TypingContext { pub fn new() -> Self { let primitives = PrimitiveTypes { elabnum: InternedType(0), logic: InternedType(1), infer: InternedType(2), }; Self { types: vec![ TypeStruct { kind: TypeKind::ElabType(ElabKind::Num), }, TypeStruct { kind: TypeKind::Logic(ElabData { typ: primitives.elabnum, value: ElabValue::Infer, }), }, TypeStruct { kind: TypeKind::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 make_elabnum_u32(&self, num: u32) -> ElabData { ElabData { typ: self.primitives.elabnum, value: ElabValue::Concrete(ElabValueData::U32(num)), } } pub fn parameterize(&mut self, typ: Type, params: &[GenericArg]) -> Option { // TODO: return proper error type here match &self.get(typ).kind { // Elab types have no params yet TypeKind::ElabType(_) => None, TypeKind::Logic(_) => { if params.len() != 1 { // invalid number of typeargs return None; }; let param = ¶ms[0]; if let GenericArg::Elab(data) = param { if data.typ == self.primitives.elabnum { Some(self.add(TypeStruct { kind: TypeKind::Logic(data.clone()), })) } else { // arg must be elabnum None } } else { // arg must be an elab value None } } TypeKind::UInt(_) => todo!(), TypeKind::Callable(_sig) => todo!("callable generic params"), // need to know what the type is to parameterize it TypeKind::Infer => None, } } pub fn pretty_value(&self, w: &mut dyn std::fmt::Write, data: &ElabData) -> std::fmt::Result { match &data.value { ElabValue::Infer => write!(w, "?: ")?, ElabValue::Concrete(val) => match val { ElabValueData::U32(val) => write!(w, "{:?}: ", val)?, ElabValueData::Bytes(val) => write!(w, "{:?}: ", val)?, }, } self.pretty_type(w, data.typ) } 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(val) => { let mut width = String::new(); self.pretty_value(&mut width, val)?; write!(w, "Logic<{}>", width) } TypeKind::Infer => write!(w, "?"), TypeKind::UInt(_) => todo!("print uint"), TypeKind::Callable(_sig) => todo!("print callable"), } } }