use std::cell::OnceCell;
use std::{fmt, rc::Rc};
use debris_common::Ident;
use rustc_hash::FxHashMap;
use crate::{
item_id::ItemIdAllocator,
objects::{
obj_bool::ObjBool,
obj_function::FunctionClassRef,
obj_int::ObjInt,
obj_never::ObjNever,
obj_null::ObjNull,
obj_struct::StructRef,
obj_struct_object::ObjStructObject,
obj_tuple_object::{ObjTupleObject, TupleRef},
},
ObjectProperties, ObjectRef, Type, ValidPayload,
};
use super::type_context::TypeContext;
pub type ClassRef = Rc<Class>;
#[derive(Debug, PartialEq, Eq)]
pub enum ClassKind {
Type(Type),
Struct(StructRef),
StructValue(StructRef),
Tuple(TupleRef),
TupleValue(TupleRef),
Function(FunctionClassRef),
}
impl ClassKind {
pub fn as_value(&self) -> Option<ClassKind> {
let value = match self {
ClassKind::StructValue(_) | ClassKind::TupleValue(_) => return None,
ClassKind::Type(typ) => ClassKind::Type(*typ),
ClassKind::Struct(strukt) => ClassKind::StructValue(strukt.clone()),
ClassKind::Tuple(tuple) => ClassKind::TupleValue(tuple.clone()),
ClassKind::Function(function) => ClassKind::Function(function.clone()),
};
Some(value)
}
pub fn matches(&self, other: &ClassKind) -> bool {
match (self, other) {
(ClassKind::Type(typ), other) => typ.matches(other.typ()),
(ClassKind::Struct(strukt), ClassKind::StructValue(other_strukt)) => {
strukt == other_strukt
}
(ClassKind::Tuple(tuple), ClassKind::TupleValue(other_tuple)) => {
tuple.matches(other_tuple)
}
(ClassKind::Function(function), ClassKind::Function(other_function)) => {
function.matches(other_function)
}
(ClassKind::Function(_) | ClassKind::Struct(_) | ClassKind::Tuple(_), _) => false,
(ClassKind::StructValue(_) | ClassKind::TupleValue(_), _) => {
panic!("Cannot match against concrete objects")
}
}
}
pub fn diverges(&self) -> bool {
match self {
ClassKind::Function(function) => function.diverges(),
ClassKind::Struct(strukt) | ClassKind::StructValue(strukt) => strukt.diverges(),
ClassKind::Tuple(tuple) | ClassKind::TupleValue(tuple) => tuple.diverges(),
ClassKind::Type(typ) => typ.diverges(),
}
}
pub fn typ(&self) -> Type {
match self {
ClassKind::Type(typ) => *typ,
ClassKind::Struct(_) => Type::Struct,
ClassKind::StructValue(_) => Type::StructObject,
ClassKind::Tuple(_) => Type::Tuple,
ClassKind::TupleValue(_) => Type::TupleObject,
ClassKind::Function(_) => Type::Function,
}
}
pub fn runtime_encodable(&self) -> bool {
match self {
ClassKind::Type(typ) => typ.runtime_encodable(),
ClassKind::Struct(_) => Type::Struct.runtime_encodable(),
ClassKind::StructValue(strukt) => strukt.runtime_encodable(),
ClassKind::Tuple(_) => Type::Tuple.runtime_encodable(),
ClassKind::TupleValue(tuple) => tuple.runtime_encodable(),
ClassKind::Function(_) => Type::Function.runtime_encodable(),
}
}
pub fn comptime_encodable(&self) -> bool {
match self {
ClassKind::Type(typ) => typ.comptime_encodable(),
ClassKind::Struct(strukt) | ClassKind::StructValue(strukt) => {
strukt.comptime_encodable()
}
ClassKind::Tuple(tuple) | ClassKind::TupleValue(tuple) => tuple.comptime_encodable(),
ClassKind::Function(_) => Type::Function.comptime_encodable(),
}
}
}
#[derive(PartialEq, Eq)]
pub struct Class {
pub kind: ClassKind,
pub properties: OnceCell<ObjectProperties>,
}
impl Class {
pub fn new_empty(kind: ClassKind) -> Self {
let cell = OnceCell::new();
cell.set(ObjectProperties::default()).unwrap();
Class {
kind,
properties: cell,
}
}
pub fn matches(&self, other: &Class) -> bool {
self.kind.matches(&other.kind)
}
pub fn matches_type(&self, other: &Class) -> bool {
self.kind.typ().matches(other.kind.typ())
}
pub fn diverges(&self) -> bool {
self.kind.diverges()
}
pub fn get_property(&self, ident: &Ident) -> Option<ObjectRef> {
self.properties
.get()
.expect("Cannot get property on uninitialized class")
.get(ident)
.cloned()
}
pub fn new_obj_from_allocator(
&self,
ctx: &TypeContext,
allocator: &ItemIdAllocator,
) -> Option<ObjectRef> {
match &self.kind {
ClassKind::Function(_) => None,
ClassKind::Type(typ) => match typ {
Type::Type
| Type::Any
| Type::ComptimeBool
| Type::ComptimeInt
| Type::FormatString
| Type::Function
| Type::FunctionRef
| Type::Module
| Type::String
| Type::Struct
| Type::StructObject
| Type::Tuple
| Type::TupleObject => None,
Type::DynamicBool => Some(ObjBool::new(allocator.next_id()).into_object(ctx)),
Type::DynamicInt => Some(ObjInt::new(allocator.next_id()).into_object(ctx)),
Type::Never => Some(ObjNever.into_object(ctx)),
Type::Null => Some(ObjNull.into_object(ctx)),
},
ClassKind::Tuple(tuple) | ClassKind::TupleValue(tuple) => {
let values = tuple
.layout
.iter()
.map(|class| class.new_obj_from_allocator(ctx, allocator))
.collect::<Option<Vec<_>>>()?;
let tuple = ObjTupleObject::new(values);
Some(tuple.into_object(ctx))
}
ClassKind::Struct(strukt) | ClassKind::StructValue(strukt) => {
let mut values = FxHashMap::default();
values.reserve(strukt.fields.len());
for (ident, class) in &strukt.fields {
let obj = class.new_obj_from_allocator(ctx, allocator)?;
values.insert(ident.clone(), obj);
}
let strukt_obj = ObjStructObject::new(strukt.clone(), values)
.expect("Autogenerated values must be valid");
Some(strukt_obj.into_object(ctx))
}
}
}
}
impl From<Type> for ClassKind {
fn from(value: Type) -> Self {
ClassKind::Type(value)
}
}
impl fmt::Display for ClassKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ClassKind::Type(typ) => fmt::Display::fmt(typ, f),
ClassKind::Struct(strukt) | ClassKind::StructValue(strukt) => {
fmt::Display::fmt(strukt, f)
}
ClassKind::Tuple(tuple) | ClassKind::TupleValue(tuple) => fmt::Display::fmt(tuple, f),
ClassKind::Function(func) => fmt::Display::fmt(func, f),
}
}
}
impl fmt::Display for Class {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = &self.kind;
write!(f, "{kind}")
}
}
impl fmt::Debug for Class {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Class")
.field("kind", &self.kind)
.finish_non_exhaustive()
}
}