use std::fmt;
use kailua_env::{Span, Spanned, WithLoc};
use kailua_diag::{self, Report, Reporter};
use kailua_syntax::Name;
use kailua_types::ty::{TypeContext, ClassSystemId, ClassId, Class, Display, DisplayState};
use kailua_types::ty::{Slot, SpannedSlotSeq, Key, T, Nil};
use message as m;
pub trait ClassSystem: Send + Sync + fmt::Debug {
fn make_class(&self, self_csid: ClassSystemId, argtys: SpannedSlotSeq, outerspan: Span,
ctx: &mut TypeContext, report: &Report) -> kailua_diag::Result<Option<ClassId>> {
if let Some(parent) = extract_parent(argtys, ctx, report)? {
self.assume_class(self_csid, parent, outerspan, ctx, report)
} else {
Ok(None)
}
}
fn assume_class(&self, self_csid: ClassSystemId, parent: Option<Spanned<ClassId>>,
outerspan: Span, ctx: &mut TypeContext,
report: &Report) -> kailua_diag::Result<Option<ClassId>>;
fn name_class(&self, cid: ClassId, name: Spanned<Name>) -> Result<(), Spanned<Name>>;
fn is_subclass_of(&self, lhs: ClassId, rhs: ClassId) -> bool;
fn index_rval(&self, cls: Class, key: Spanned<&Key>, expspan: Span,
ctx: &mut TypeContext, report: &Report) -> kailua_diag::Result<Option<Slot>>;
fn index_lval(&self, cls: Class, key: Spanned<&Key>, expspan: Span,
hint: Option<&Slot>, ctx: &mut TypeContext,
report: &Report) -> kailua_diag::Result<Option<(bool, Slot)>>;
fn fmt_class(&self, cid: ClassId, f: &mut fmt::Formatter, st: &DisplayState) -> fmt::Result;
fn list_fields(&self, cls: Class,
f: &mut FnMut(&Key, &Slot) -> Result<(), ()>) -> Result<(), ()>;
fn list_parents(&self, cid: ClassId,
f: &mut FnMut(ClassId) -> Result<(), ()>) -> Result<(), ()>;
}
pub mod dumb;
pub mod gideros;
pub fn make_predefined_class_system(name: &str) -> Option<Box<ClassSystem>> {
match name {
"gideros" => Some(Box::new(gideros::GiderosClassSystem::new())),
_ => None,
}
}
fn extract_parent(mut argtys: SpannedSlotSeq, ctx: &mut TypeContext,
report: &Report) -> kailua_diag::Result<Option<Option<Spanned<ClassId>>>> {
let argty = argtys.ensure_at(0);
let parent = if let Some(arg) = ctx.resolve_exact_type(&argty.unlift()) {
if let T::None = *arg {
Some(None)
} else if let T::Class(Class::Prototype(cid)) = *arg {
if arg.nil() == Nil::Silent { Some(Some(cid)) } else { None }
} else {
None
}
} else {
None
};
if let Some(parent) = parent {
Ok(Some(parent.map(|cid| cid.with_loc(argty))))
} else {
report.error(argty, m::BadClassParent { ty: argty.unlift().display(ctx) }).done()?;
Ok(None)
}
}