use std::{
fmt::{Debug, Display},
hash::Hash,
ops::Deref,
sync::LazyLock,
};
use combine::{Parser, parser, token};
use downcast_rs::{Downcast, impl_downcast};
use dyn_clone::DynClone;
use rustc_hash::FxHashMap;
use crate::{
builtin::attr_interfaces::OutlinedAttr,
common_traits::Verify,
context::{Context, collect_deduped_interface_verifiers},
dialect::{Dialect, DialectName},
identifier::Identifier,
impl_printable_for_display, input_err,
irfmt::{
parsers::{attr_parser, delimited_list_parser, spaced},
printers::iter_with_sep,
},
location::Located,
parsable::{Parsable, ParseResult, StateStream},
printable::{self, Printable},
result::Result,
};
#[derive(Clone)]
struct AttributeDictKeyVal<'a> {
key: &'a Identifier,
val: &'a AttrObj,
}
impl<'a> Printable for AttributeDictKeyVal<'a> {
fn fmt(
&self,
ctx: &Context,
_state: &printable::State,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{}: {}", self.key, self.val.disp(ctx))
}
}
impl<'b> Parsable for AttributeDictKeyVal<'b> {
type Arg = ();
type Parsed = (Identifier, AttrObj);
fn parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed> {
(Identifier::parser(()), spaced(token(':')), attr_parser())
.map(|(key, _, val)| (key, val))
.parse_stream(state_stream)
.into_result()
}
}
impl Printable for AttributeDict {
fn fmt(
&self,
ctx: &Context,
_state: &printable::State,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(
f,
"[{}]",
iter_with_sep(
self.0
.iter()
.map(|(key, val)| AttributeDictKeyVal { key, val }),
printable::ListSeparator::CharSpace(','),
)
.disp(ctx)
)
}
}
impl Parsable for AttributeDict {
type Arg = ();
type Parsed = Self;
fn parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed> {
delimited_list_parser('[', ']', ',', AttributeDictKeyVal::parser(()))
.map(|key_vals| AttributeDict(key_vals.into_iter().collect()))
.parse_stream(state_stream)
.into_result()
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct AttributeDict(pub FxHashMap<Identifier, AttrObj>);
impl AttributeDict {
pub fn get<T: Attribute>(&self, k: &Identifier) -> Option<&T> {
self.0.get(k).and_then(|ao| ao.downcast_ref::<T>())
}
pub fn get_mut<T: Attribute>(&mut self, k: &Identifier) -> Option<&mut T> {
self.0.get_mut(k).and_then(|ao| ao.downcast_mut::<T>())
}
pub fn get_as<T: ?Sized + AttrInterfaceMarker + 'static>(&self, k: &Identifier) -> Option<&T> {
self.0.get(k).and_then(|ao| attr_cast::<T>(&**ao))
}
pub fn set<T: Attribute>(&mut self, k: Identifier, v: T) {
self.0.insert(k, Box::new(v));
}
pub fn clone_skip_outlined(&self) -> Self {
self.0
.iter()
.filter_map(|(k, v)| {
if attr_impls::<dyn OutlinedAttr>(&**v) {
None
} else {
Some((k.clone(), dyn_clone::clone_box(&**v)))
}
})
.collect::<FxHashMap<Identifier, AttrObj>>()
.into()
}
}
impl From<FxHashMap<Identifier, AttrObj>> for AttributeDict {
fn from(value: FxHashMap<Identifier, AttrObj>) -> Self {
AttributeDict(value)
}
}
pub trait Attribute: Printable + Verify + Downcast + Sync + Send + DynClone + Debug {
fn eq_attr(&self, other: &dyn Attribute) -> bool;
fn get_attr_id(&self) -> AttrId;
fn get_attr_id_static() -> AttrId
where
Self: Sized;
#[doc(hidden)]
fn verify_interfaces(&self, ctx: &Context) -> Result<()>;
fn register<A: Attribute>(ctx: &mut Context)
where
Self: Sized + Parsable<Arg = (), Parsed = A>,
{
let attr_parser: AttrParserFn = Box::new(|&()| {
combine::parser(move |parsable_state: &mut StateStream<'_>| {
Self::parse(parsable_state, ())
.map(|(attr, r)| -> (AttrObj, _) { (Box::new(attr), r) })
})
.boxed()
});
let attrid = Self::get_attr_id_static();
Dialect::register(ctx, &attrid.dialect).add_attr(attrid.clone(), attr_parser);
}
}
impl_downcast!(Attribute);
dyn_clone::clone_trait_object!(Attribute);
pub type AttrObj = Box<dyn Attribute>;
pub(crate) type AttrParserFn = Box<
for<'a> fn(
&'a (),
) -> Box<dyn Parser<StateStream<'a>, Output = AttrObj, PartialState = ()> + 'a>,
>;
impl PartialEq for AttrObj {
fn eq(&self, other: &Self) -> bool {
(**self).eq_attr(&**other)
}
}
impl<T: Attribute> From<T> for AttrObj {
fn from(value: T) -> Self {
Box::new(value)
}
}
impl Eq for AttrObj {}
impl Printable for AttrObj {
fn fmt(
&self,
ctx: &Context,
state: &printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{} ", self.get_attr_id())?;
Printable::fmt(self.deref(), ctx, state, f)
}
}
impl Parsable for AttrObj {
type Arg = ();
type Parsed = AttrObj;
fn parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed> {
let loc = state_stream.loc();
let attr_id_parser = spaced(AttrId::parser(()));
let mut attr_parser = attr_id_parser.then(move |attr_id: AttrId| {
let loc = loc.clone();
combine::parser(move |parsable_state: &mut StateStream<'a>| {
let state = &parsable_state.state;
let dialect = state
.ctx
.dialects
.get(&attr_id.dialect)
.expect("Dialect name parsed but dialect isn't registered");
let Some(attr_parser) = dialect.attributes.get(&attr_id) else {
input_err!(
loc.clone(),
"Unregistered attribute {}",
attr_id.disp(state.ctx)
)?
};
attr_parser(&()).parse_stream(parsable_state).into_result()
})
});
attr_parser.parse_stream(state_stream).into_result()
}
}
pub fn verify_attr(attr: &dyn Attribute, ctx: &Context) -> Result<()> {
attr.verify_interfaces(ctx)?;
Verify::verify(attr, ctx)
}
impl Verify for AttrObj {
fn verify(&self, ctx: &Context) -> Result<()> {
verify_attr(self.as_ref(), ctx)
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` not an attribute interface.",
label = "If `{Self}` is a trait, annotate it with #[attr_interface] to be able to cast to it from a `&dyn Attribute`",
note = "If you want to cast to a concrete `Attribute`, use `downcast_ref` instead."
)]
pub trait AttrInterfaceMarker {}
pub fn attr_cast<T: ?Sized + AttrInterfaceMarker + 'static>(attr: &dyn Attribute) -> Option<&T> {
crate::utils::trait_cast::any_to_trait::<T>(attr.as_any())
}
pub fn attr_impls<T: ?Sized + AttrInterfaceMarker + 'static>(attr: &dyn Attribute) -> bool {
attr_cast::<T>(attr).is_some()
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct AttrName(String);
impl AttrName {
pub fn new(name: &str) -> AttrName {
AttrName(name.to_string())
}
}
impl_printable_for_display!(AttrName);
impl Display for AttrName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Parsable for AttrName {
type Arg = ();
type Parsed = AttrName;
fn parse<'a>(
state_stream: &mut crate::parsable::StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed>
where
Self: Sized,
{
Identifier::parser(())
.map(|name| AttrName::new(&name))
.parse_stream(state_stream)
.into()
}
}
impl Deref for AttrName {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct AttrId {
pub dialect: DialectName,
pub name: AttrName,
}
impl_printable_for_display!(AttrId);
impl Display for AttrId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.dialect, self.name)
}
}
impl Parsable for AttrId {
type Arg = ();
type Parsed = AttrId;
fn parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed>
where
Self: Sized,
{
let mut parser = DialectName::parser(())
.skip(parser::char::char('.'))
.and(AttrName::parser(()))
.map(|(dialect, name)| AttrId { dialect, name });
parser.parse_stream(state_stream).into()
}
}
pub type AttrInterfaceVerifier = fn(&dyn Attribute, &Context) -> Result<()>;
pub type AttrInterfaceAllVerifiers = fn() -> Vec<AttrInterfaceVerifier>;
#[doc(hidden)]
type AttrInterfaceVerifierInfo = (std::any::TypeId, AttrInterfaceAllVerifiers);
#[cfg(not(target_family = "wasm"))]
pub mod statics {
use super::*;
#[::pliron::linkme::distributed_slice]
pub static ATTR_INTERFACE_VERIFIERS: [LazyLock<AttrInterfaceVerifierInfo>] = [..];
pub fn get_attr_interface_verifiers()
-> impl Iterator<Item = &'static LazyLock<AttrInterfaceVerifierInfo>> {
ATTR_INTERFACE_VERIFIERS.iter()
}
}
#[cfg(target_family = "wasm")]
pub mod statics {
use super::*;
use crate::utils::inventory::LazyLockWrapper;
::pliron::inventory::collect!(LazyLockWrapper<AttrInterfaceVerifierInfo>);
pub fn get_attr_interface_verifiers()
-> impl Iterator<Item = &'static LazyLock<AttrInterfaceVerifierInfo>> {
::pliron::inventory::iter::<LazyLockWrapper<AttrInterfaceVerifierInfo>>().map(|llw| llw.0)
}
}
pub use statics::*;
#[doc(hidden)]
pub static ATTR_INTERFACE_VERIFIERS_MAP: LazyLock<
FxHashMap<std::any::TypeId, Vec<AttrInterfaceVerifier>>,
> = LazyLock::new(|| collect_deduped_interface_verifiers(get_attr_interface_verifiers()));