1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! [Dialect]s are a mechanism to group related [Op](crate::op::Op)s, [Type](crate::type::Type)s
//! and [Attribute](crate::attribute::Attribute)s.
use std::{fmt::Display, ops::Deref};

use combine::Parser;
use rustc_hash::FxHashMap;

use crate::{
    attribute::{AttrId, AttrParserFn},
    context::Context,
    identifier::Identifier,
    input_err,
    location::{Located, Location},
    op::{OpId, OpObj},
    parsable::{IntoParseResult, Parsable, ParseResult, ParserFn, StateStream},
    printable::{self, Printable},
    r#type::{TypeId, TypeParserFn},
};

/// Dialect name: Safe wrapper around a String.
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct DialectName(Identifier);

impl DialectName {
    /// Create a new DialectName
    pub fn new(name: &str) -> DialectName {
        DialectName(name.try_into().expect("Invalid Identifier for DialectName"))
    }
}

impl From<&str> for DialectName {
    fn from(value: &str) -> Self {
        DialectName::new(value)
    }
}

impl Printable for DialectName {
    fn fmt(
        &self,
        _ctx: &Context,
        _state: &printable::State,
        f: &mut core::fmt::Formatter<'_>,
    ) -> core::fmt::Result {
        <Self as Display>::fmt(self, f)
    }
}

impl Display for DialectName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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::new(&dialect_name);
                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 Deref for DialectName {
    type Target = String;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// A collection of Types and Ops.
/// Dialects are identified by their names.
pub struct Dialect {
    /// Name of this dialect.
    pub name: DialectName,
    /// Ops that are part of this dialect.
    pub(crate) ops: FxHashMap<OpId, ParserFn<Vec<(Identifier, Location)>, OpObj>>,
    /// Types that are part of this dialect.
    pub(crate) types: FxHashMap<TypeId, TypeParserFn>,
    /// Attributes that are part of this dialect.
    pub(crate) attributes: FxHashMap<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 {
    /// Create a new unregistered dialect.
    pub fn new(name: DialectName) -> Dialect {
        Dialect {
            name,
            ops: FxHashMap::default(),
            types: FxHashMap::default(),
            attributes: FxHashMap::default(),
        }
    }

    /// Register this dialect if not already registered.
    pub fn register(self, ctx: &mut Context) {
        ctx.dialects.entry(self.name.clone()).or_insert(self);
    }

    /// Add an [Op](crate::op::Op) to this dialect.
    pub(crate) fn add_op(
        &mut self,
        op: OpId,
        op_parser: ParserFn<Vec<(Identifier, Location)>, OpObj>,
    ) {
        assert!(op.dialect == self.name);
        self.ops.insert(op, op_parser);
    }

    /// Add a [Type](crate::type::Type) to this dialect.
    pub(crate) fn add_type(&mut self, ty: TypeId, ty_parser: TypeParserFn) {
        assert!(ty.dialect == self.name);
        self.types.insert(ty, ty_parser);
    }

    /// Add an [Attribute](crate::attribute::Attribute) to this dialect.
    pub(crate) fn add_attr(&mut self, attr: AttrId, attr_parser: AttrParserFn) {
        assert!(attr.dialect == self.name);
        self.attributes.insert(attr, attr_parser);
    }

    /// This Dialect's name.
    pub fn get_name(&self) -> &DialectName {
        &self.name
    }
}

#[cfg(test)]
mod test {

    use expect_test::expect;

    use crate::{
        builtin,
        context::Context,
        location,
        parsable::{self, state_stream_from_iterator, Parsable},
        printable::Printable,
    };

    use super::DialectName;

    #[test]
    fn parse_dialect_name() {
        let mut ctx = Context::new();
        builtin::register(&mut ctx);

        let state_stream = state_stream_from_iterator(
            "non_existant".chars(),
            parsable::State::new(&mut ctx, location::Source::InMemory),
        );

        let res = DialectName::parser(()).parse(state_stream);
        let err_msg = format!("{}", res.err().unwrap());

        let expected_err_msg = expect![[r#"
            Parse error at line: 1, column: 1
            Unregistered dialect non_existant
        "#]];
        expected_err_msg.assert_eq(&err_msg);

        let state_stream = state_stream_from_iterator(
            "builtin".chars(),
            parsable::State::new(&mut ctx, location::Source::InMemory),
        );

        let parsed = DialectName::parser(()).parse(state_stream).unwrap().0;
        assert_eq!(parsed.disp(&ctx).to_string(), "builtin");
    }
}