esl_compiler/builder/
def_type.rs

1//! Type builder
2use crate::builder::value::{build_some_value, Value, ValueContent};
3use crate::context::Context;
4use crate::cursor::{Cursor, Error, Token, UnexpectedRuleSnafu};
5use crate::diagnostics::{DiagnosticError, DiagnosticWarning};
6use crate::location::FileLocation;
7use crate::map::Map;
8use crate::parser::Rule;
9use crate::reference::{Reference, TypeDefinitionTarget};
10
11/// Type definition with a name, location and inner contents for the different variants.
12#[derive(Clone, Debug, PartialEq)]
13pub struct TypeDefinition {
14    /// Type definition name.
15    pub name: Token,
16    /// Complete type definition's textual location.
17    pub loc: FileLocation,
18    /// Inner contents of this type definition.
19    pub contents: TypeDefinitionContents,
20}
21impl std::fmt::Display for TypeDefinition {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        write!(f, "{}", self.name)
24    }
25}
26
27/// Type definition inner contents.
28#[derive(Clone, Debug, PartialEq)]
29pub enum TypeDefinitionContents {
30    /// Regular type definition.
31    Regular(RegularTypeDefinition),
32    /// An enumeration type definition.
33    Enum(EnumTypeDefinition),
34    /// Bundle type definition.
35    Bundle(BundleTypeDefinition),
36}
37
38/// Regular type definition's inner contents.
39#[derive(Clone, Debug, Default, PartialEq)]
40pub struct RegularTypeDefinition {
41    /// Optional parent type.
42    parent: Option<Reference<Token, TypeDefinitionTarget>>,
43    /// Attached units for this type.
44    units: Map<String, Token>,
45    /// Optional domain.
46    domain: Option<TypeDomain>,
47}
48
49/// Type's domain consisting of the defined intervals.
50#[derive(Clone, Debug, Default, PartialEq)]
51pub struct TypeDomain {
52    /// Domain is span by multiple intervals (OR-concatenated).
53    intervals: Vec<TypeInterval>,
54}
55
56/// Type interval.
57#[derive(Clone, Debug, PartialEq)]
58pub enum TypeInterval {
59    /// Closed interval between a lower and an upper bound.
60    Closed { lower: Bound, upper: Bound },
61    /// An open interval with a lower bound.
62    LowerBound(Bound),
63    /// An open interval with an upper bound.
64    UpperBound(Bound),
65    /// Point interval. Whether the bound is inclusive determines whether it is a point inclusion
66    /// or exclusion.
67    Point(Bound),
68}
69
70/// Type interval bound.
71#[derive(Clone, Debug, PartialEq)]
72pub struct Bound {
73    /// Value of this bound.
74    pub value: Value,
75    /// Whether the bound is inclusive (the value itself is allowed).
76    pub inclusive: bool,
77}
78impl Default for Bound {
79    fn default() -> Self {
80        Self {
81            value: Value {
82                content: ValueContent::ToBeDetermined,
83                loc: Default::default(),
84                unit: Default::default(),
85            },
86            inclusive: true,
87        }
88    }
89}
90
91/// An enumeration type definition's inner contents.
92#[derive(Clone, Debug, Default, PartialEq)]
93pub struct EnumTypeDefinition {
94    /// Map of allowed values.
95    pub values: Map<String, Value>,
96}
97
98/// Bundle type definition's inner contents.
99#[derive(Clone, Debug, Default, PartialEq)]
100pub struct BundleTypeDefinition {
101    /// Map of bundle field definitions.
102    pub fields: Map<String, BundleFieldDefinition>,
103}
104
105/// Single bundle field definition.
106#[derive(Clone, Debug, PartialEq)]
107pub struct BundleFieldDefinition {
108    /// Bundle field definition name.
109    pub name: Token,
110    /// Bundle definition textual location.
111    pub loc: FileLocation,
112    /// Type definition for this bundle field.
113    pub definition: Reference<Token, TypeDefinitionTarget>,
114}
115impl std::fmt::Display for BundleFieldDefinition {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", self.name)
118    }
119}
120
121/// Build a type definition section and add all type definitions to the context's specification.
122pub fn build_type_definition(ctx: &mut Context, type_definition: Cursor) -> Result<(), Error> {
123    let mut cs = type_definition.into_inner();
124    let contents = cs.req_first(Rule::type_definition_contents)?;
125    for type_definition_any in contents.into_inner().get_all(Rule::type_definition_any) {
126        let def = build_type_definition_any(ctx, type_definition_any)?;
127        if let Err(dupe) = ctx.specification.types.insert(def.to_string(), def) {
128            ctx.error(DiagnosticError::DuplicateDefinition, &dupe.loc.clone());
129        }
130    }
131    Ok(())
132}
133
134/// Build any type definition.
135pub fn build_type_definition_any(
136    ctx: &mut Context,
137    type_definition_any: Cursor,
138) -> Result<TypeDefinition, Error> {
139    let mut cs = type_definition_any.into_inner();
140    let c = cs.req_next()?;
141    match c.as_rule() {
142        Rule::type_regular => build_regular_type_definition(ctx, c),
143        Rule::type_bundle => build_bundle_type(ctx, c),
144        Rule::type_enum => build_enum_type(ctx, c),
145        _ => UnexpectedRuleSnafu::from(c).fail()?,
146    }
147}
148
149/// Build a regular type definition.
150pub fn build_regular_type_definition(
151    ctx: &mut Context,
152    type_regular: Cursor,
153) -> Result<TypeDefinition, Error> {
154    let loc = type_regular.as_location();
155    let mut cs = type_regular.into_inner();
156    let name = cs.req_first(Rule::some_type)?.into();
157
158    let mut def = RegularTypeDefinition::default();
159
160    for c in cs {
161        match c.as_rule() {
162            Rule::type_parent => {
163                let parent: Token = c.into_inner().req_first(Rule::some_type)?.into();
164                def.parent = Some(parent.into());
165            }
166            Rule::type_units => build_type_units(ctx, &mut def.units, c)?,
167            Rule::type_domain => {
168                def.domain = Some(build_type_domain(ctx, c)?);
169            }
170            _ => UnexpectedRuleSnafu::from(c).fail()?,
171        };
172    }
173
174    let def = TypeDefinition {
175        name,
176        loc,
177        contents: TypeDefinitionContents::Regular(def),
178    };
179
180    Ok(def)
181}
182
183/// Build a type's units from a cursor.
184pub fn build_type_units(
185    ctx: &mut Context,
186    units: &mut Map<String, Token>,
187    type_units: Cursor,
188) -> Result<(), Error> {
189    for some_unit in type_units.into_inner().get_all(Rule::some_unit) {
190        let token: Token = some_unit.into();
191        if let Err(dupe) = units.insert(token.to_string(), token) {
192            ctx.warn(DiagnosticWarning::Generic, &dupe.loc);
193        }
194    }
195    Ok(())
196}
197
198/// Build a type's domain consisting of one or more bounds.
199pub fn build_type_domain(ctx: &mut Context, type_domain: Cursor) -> Result<TypeDomain, Error> {
200    let mut domain = TypeDomain::default();
201    for c in type_domain.into_inner() {
202        match c.as_rule() {
203            Rule::type_domain_closed_range => {
204                let mut lower = Bound::default();
205                let mut upper = Bound::default();
206                for c in c.into_inner() {
207                    match c.as_rule() {
208                        Rule::type_domain_range_lower => {
209                            lower = build_type_domain_range_lower(ctx, c)?;
210                        }
211                        Rule::type_domain_range_upper => {
212                            upper = build_type_domain_range_upper(ctx, c)?;
213                        }
214                        Rule::kw_separator_enum_word => (),
215                        _ => UnexpectedRuleSnafu::from(c).fail()?,
216                    };
217                }
218                domain.intervals.push(TypeInterval::Closed { lower, upper });
219            }
220            Rule::type_domain_range_lower => {
221                domain
222                    .intervals
223                    .push(TypeInterval::LowerBound(build_type_domain_range_lower(
224                        ctx, c,
225                    )?))
226            }
227
228            Rule::type_domain_range_upper => {
229                domain
230                    .intervals
231                    .push(TypeInterval::UpperBound(build_type_domain_range_upper(
232                        ctx, c,
233                    )?))
234            }
235            Rule::type_domain_constant => {
236                domain.intervals.push(build_type_domain_constant(ctx, c)?)
237            }
238            _ => UnexpectedRuleSnafu::from(c).fail()?,
239        };
240    }
241    Ok(domain)
242}
243
244/// Build a type domain lower bounded interval bound.
245pub fn build_type_domain_range_lower(
246    ctx: &mut Context,
247    type_domain_range_lower: Cursor,
248) -> Result<Bound, Error> {
249    let mut bound = Bound::default();
250    for c in type_domain_range_lower.into_inner() {
251        match c.as_rule() {
252            Rule::kw_comparison_least => {
253                bound.inclusive = true;
254            }
255            Rule::some_value => {
256                bound.value = build_some_value(ctx, c)?;
257            }
258            Rule::kw_control_of => (),
259            Rule::kw_comparison_at => (),
260            Rule::kw_comparison_greater => (),
261            Rule::kw_comparison_than => (),
262            _ => UnexpectedRuleSnafu::from(c).fail()?,
263        };
264    }
265    Ok(bound)
266}
267
268/// Build a type domain upper bounded interval bound.
269pub fn build_type_domain_range_upper(
270    ctx: &mut Context,
271    type_domain_range_upper: Cursor,
272) -> Result<Bound, Error> {
273    let mut bound = Bound::default();
274    for c in type_domain_range_upper.into_inner() {
275        match c.as_rule() {
276            Rule::kw_comparison_most => {
277                bound.inclusive = true;
278            }
279            Rule::some_value => {
280                bound.value = build_some_value(ctx, c)?;
281            }
282            Rule::kw_control_of => (),
283            Rule::kw_comparison_at => (),
284            Rule::kw_comparison_smaller => (),
285            Rule::kw_comparison_than => (),
286            _ => UnexpectedRuleSnafu::from(c).fail()?,
287        };
288    }
289    Ok(bound)
290}
291
292/// Build a type domain constant inclusion or exclusion.
293pub fn build_type_domain_constant(
294    ctx: &mut Context,
295    type_domain_constant: Cursor,
296) -> Result<TypeInterval, Error> {
297    let mut cs = type_domain_constant.into_inner();
298    let not_equal = cs
299        .req_next()
300        .map(|p| p.as_rule() == Rule::kw_comparison_not)?;
301    let value = cs
302        .req_first(Rule::some_value)
303        .and_then(|some_value| build_some_value(ctx, some_value))?;
304    Ok(TypeInterval::Point(Bound {
305        value,
306        inclusive: !not_equal,
307    }))
308}
309
310/// Build an enumeration type definition.
311pub fn build_enum_type(ctx: &mut Context, type_enum: Cursor) -> Result<TypeDefinition, Error> {
312    let mut cs = type_enum.into_inner();
313    let name = cs
314        .req_first(Rule::some_type)
315        .map(|some_type| some_type.into())?;
316
317    let mut enum_def = EnumTypeDefinition::default();
318    let loc = cs.as_location();
319
320    for c in cs {
321        if let Some(values) = match c.as_rule() {
322            Rule::enum_values_inline => Some(c.into_inner().get_all(Rule::some_value)),
323            Rule::enum_value_line => Some(c.into_inner().get_all(Rule::some_value)),
324            _ => None,
325        } {
326            for some_value in values {
327                let value = build_some_value(ctx, some_value)?;
328                if let Err(dupe) = enum_def.values.insert(value.to_string(), value) {
329                    ctx.warn(DiagnosticWarning::Redefinition, &dupe.loc);
330                }
331            }
332        }
333    }
334
335    let def = TypeDefinition {
336        name,
337        loc,
338        contents: TypeDefinitionContents::Enum(enum_def),
339    };
340
341    Ok(def)
342}
343
344/// Build a bundle type definition.
345pub fn build_bundle_type(ctx: &mut Context, type_bundle: Cursor) -> Result<TypeDefinition, Error> {
346    let mut bundle = BundleTypeDefinition::default();
347    let mut cs = type_bundle.into_inner();
348    let name = cs
349        .req_first(Rule::some_type)
350        .map(|some_type| some_type.into())?;
351
352    let loc = cs.as_location();
353
354    for c in cs {
355        if let Some(fields) = match c.as_rule() {
356            Rule::bundle_fields_inline => Some(c.into_inner().get_all(Rule::bundle_field)),
357            Rule::bundle_field_line => Some(c.into_inner().get_all(Rule::bundle_field)),
358            _ => None,
359        } {
360            for bundle_field in fields {
361                let field = build_bundle_field(ctx, bundle_field)?;
362                if let Err(dupe) = bundle.fields.insert(field.to_string(), field) {
363                    ctx.warn(DiagnosticWarning::Redefinition, &dupe.loc);
364                }
365            }
366        }
367    }
368
369    let def = TypeDefinition {
370        name,
371        loc,
372        contents: TypeDefinitionContents::Bundle(bundle),
373    };
374
375    Ok(def)
376}
377
378/// Build a single bundle field.
379pub fn build_bundle_field(
380    ctx: &mut Context,
381    bundle_field: Cursor,
382) -> Result<BundleFieldDefinition, Error> {
383    let mut cs = bundle_field.into_inner();
384    let name = cs.req_first(Rule::some_name)?.into();
385    let definition: Reference<Token, _> = cs
386        .req_first(Rule::some_type)
387        .map(Into::<Token>::into)?
388        .into();
389    Ok(BundleFieldDefinition {
390        name,
391        loc: cs.as_location(),
392        definition,
393    })
394}