use core::fmt::Display;
use crate::{
attribute::{AttrId, AttrParserFn},
combine::Parser,
context::Context,
identifier::Identifier,
impl_printable_for_display, input_err,
location::Located,
op::{OpId, OpParserFn},
parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
printable::{self, Printable},
result::Result,
r#type::{TypeId, TypeParserFn},
utils::table::HMap,
};
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DialectName(Identifier);
impl DialectName {
pub fn try_new(name: &str) -> Result<DialectName> {
Identifier::try_from(name).map(DialectName)
}
}
impl From<DialectName> for Identifier {
fn from(dialect_name: DialectName) -> Self {
dialect_name.0
}
}
impl From<Identifier> for DialectName {
fn from(value: Identifier) -> Self {
DialectName(value)
}
}
impl_printable_for_display!(DialectName);
impl Display for DialectName {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Parsable for DialectName {
type Arg = ();
type Parsed = DialectName;
fn parse<'a>(
state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed>
where
Self: Sized,
{
let loc = state_stream.loc();
let id = Identifier::parser(());
let mut parser = id.then(move |dialect_name| {
let loc = loc.clone();
combine::parser(move |state_stream: &mut StateStream<'a>| {
let dialect_name = DialectName(dialect_name.clone());
if state_stream.state.ctx.dialects.contains_key(&dialect_name) {
Ok(dialect_name).into_parse_result()
} else {
input_err!(loc.clone(), "Unregistered dialect {}", dialect_name)?
}
})
});
parser.parse_stream(state_stream).into()
}
}
impl AsRef<str> for DialectName {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
pub struct Dialect {
pub name: DialectName,
pub(crate) ops: HMap<OpId, OpParserFn>,
pub(crate) types: HMap<TypeId, TypeParserFn>,
pub(crate) attributes: HMap<AttrId, AttrParserFn>,
}
impl Printable for Dialect {
fn fmt(
&self,
ctx: &Context,
_state: &printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{}", self.name.disp(ctx))
}
}
impl Dialect {
pub fn new(name: DialectName) -> Dialect {
Dialect {
name,
ops: HMap::default(),
types: HMap::default(),
attributes: HMap::default(),
}
}
pub fn register<'a>(ctx: &'a mut Context, name: &DialectName) -> &'a mut Dialect {
if !ctx.dialects.contains_key(name) {
ctx.dialects
.insert(name.clone(), Dialect::new(name.clone()));
}
ctx.dialects.get_mut(name).unwrap()
}
pub(crate) fn add_op(&mut self, op: OpId, op_parser: OpParserFn) {
assert!(op.dialect == self.name);
self.ops.insert(op, op_parser);
}
pub(crate) fn add_type(&mut self, ty: TypeId, ty_parser: TypeParserFn) {
assert!(ty.dialect == self.name);
self.types.insert(ty, ty_parser);
}
pub(crate) fn add_attr(&mut self, attr: AttrId, attr_parser: AttrParserFn) {
assert!(attr.dialect == self.name);
self.attributes.insert(attr, attr_parser);
}
pub fn name(&self) -> &DialectName {
&self.name
}
}
#[cfg(test)]
mod test {
use alloc::{format, string::ToString};
use expect_test::expect;
use crate::{
context::Context,
parsable::{Parsable, parse_from_str},
printable::Printable,
result::ExpectOk,
};
use super::*;
#[test]
fn parse_dialect_name() {
let mut ctx = Context::new();
let err_msg = format!(
"{}",
parse_from_str(DialectName::parser(()), &mut ctx, "non_existant")
.err()
.unwrap()
);
let expected_err_msg = expect![[r#"
Compilation error: invalid input program.
Parse error at line: 1, column: 1
Unregistered dialect non_existant
"#]];
expected_err_msg.assert_eq(&err_msg);
let parsed = parse_from_str(DialectName::parser(()), &mut ctx, "builtin").expect_ok(&ctx);
assert_eq!(parsed.disp(&ctx).to_string(), "builtin");
}
}