donet_core/parser/
ast.rs

1/*
2    This file is part of Donet.
3
4    Copyright © 2024-2025 Max Rodriguez
5
6    Donet is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Affero General Public License,
8    as published by the Free Software Foundation, either version 3
9    of the License, or (at your option) any later version.
10
11    Donet is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU Affero General Public License for more details.
15
16    You should have received a copy of the GNU Affero General Public
17    License along with Donet. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20//! Enum and Struct definitions that are used to build the DC File [`AST`].
21//! Used by [`crate::parser::parser`].
22//!
23//! [`AST`]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
24
25use super::lexer::{DCToken, Span};
26use crate::dctype::DCTypeEnum;
27
28/// Paired with the `type_declarations` production in the Context Free Grammar.
29#[derive(Debug, Clone)]
30pub struct Root {
31    pub type_declarations: Vec<TypeDeclaration>,
32}
33
34/// Paired with the `type_decl` production in the Context Free Grammar.
35#[derive(Debug, Clone)]
36pub enum TypeDeclaration {
37    // A single Python-style DC Import line can translate to
38    // multiple [`PythonImport`] structures per symbol imported.
39    PythonImport(PythonImport),
40    KeywordType(KeywordDefinition),
41    StructType(Struct),
42    DClassType(DClass),
43    TypedefType(TypeDefinition),
44}
45
46/// Paired with the `python_style_import` production in the Context Free Grammar.
47#[derive(Debug, Clone)]
48pub struct PythonImport {
49    pub span: Span,
50    pub module: SymbolWithViews,
51    pub class: SymbolWithViews,
52}
53
54/// Paired with the `py_module` and `dclass_import`
55/// productions in the Context Free Grammar.
56#[derive(Debug, Clone)]
57pub struct SymbolWithViews {
58    pub span: Span,
59    pub symbol: String,
60    pub symbol_views: Vec<ViewSuffix>,
61}
62
63/// Paired with the `view_suffixes` production in the Context Free Grammar.
64pub type ViewSuffixes = Vec<ViewSuffix>;
65
66/// Paired with the `view_suffix` production in the Context Free Grammar.
67#[derive(Debug, Clone)]
68pub struct ViewSuffix {
69    pub span: Span,
70    pub view: String, // 'AI', 'OV', 'UD', etc.
71}
72
73/// Paired with the `type_definition` production in the Context Free Grammar.
74#[derive(Debug, Clone)]
75pub struct TypeDefinition {
76    pub span: Span,
77    pub data_type: NonMethodDataType,
78    pub array_range: Option<ArrayRange>,
79    pub alias_identifier: Option<String>,
80}
81
82/// Paired with the `keyword_type` production in the Context Free Grammar.
83#[derive(Debug, Clone)]
84pub struct KeywordDefinition {
85    pub span: Span,
86    pub identifier: String,
87    pub historical: bool,
88}
89
90/// Paired with the `distributed_class_type` production in the Context Free Grammar.
91#[derive(Debug, Clone)]
92pub struct DClass {
93    pub span: Span,
94    pub identifier: String,
95    pub parents: Vec<String>,
96    pub fields: ClassFields,
97}
98
99/// Paired with the `optional_class_fields` production in the Context Free Grammar.
100pub type ClassFields = Vec<AtomicOrMolecular>;
101
102/// Paired with the `class_field` production in the Context Free Grammar.
103#[derive(Debug, Clone)]
104pub enum AtomicOrMolecular {
105    Atomic(AtomicField),
106    Molecular(MolecularField),
107}
108
109/// The Atomic Field variant of the [`AtomicOrMolecular`] enum.
110#[derive(Debug, Clone)]
111pub struct AtomicField {
112    pub span: Span,
113    pub identifier: Option<String>,
114    pub keywords: Vec<String>,
115    pub parameters: MethodBody,
116}
117
118impl AtomicField {
119    pub fn from_named_field(field: NamedField, kw_list: KeywordList, span: Span) -> Self {
120        match field {
121            NamedField::ParameterField(pf) => Self {
122                span,
123                identifier: pf.parameter.identifier.clone(),
124                keywords: kw_list,
125                parameters: vec![pf.parameter],
126            },
127            NamedField::MethodAsField(mf) => Self {
128                span,
129                identifier: Some(mf.identifier),
130                keywords: kw_list,
131                parameters: mf.parameters,
132            },
133        }
134    }
135}
136
137/// Paired with the `molecular_field` production in the Context Free Grammar.
138#[derive(Debug, Clone)]
139pub struct MolecularField {
140    pub span: Span,
141    pub identifier: String,
142    pub atomic_field_identifiers: Vec<String>,
143}
144
145/// Paired with the `parameter_values` production in the Context Free Grammar.
146pub type ParameterValues = Vec<TypeValue>;
147
148/// Paired with the `struct_type` production in the Context Free Grammar.
149#[derive(Debug, Clone)]
150pub struct Struct {
151    pub span: Span,
152    pub identifier: String,
153    pub fields: Vec<StructField>,
154}
155
156/// Paired with the `struct_field` production in the Context Free Grammar.
157#[derive(Debug, Clone)]
158pub enum StructField {
159    ParameterField(ParameterField),
160    Switch(Switch),
161}
162
163impl From<NamedField> for StructField {
164    fn from(value: NamedField) -> Self {
165        match value {
166            NamedField::ParameterField(pf) => Self::ParameterField(pf),
167            NamedField::MethodAsField(_) => panic!("MethodAsField cannot be a StructField."),
168        }
169    }
170}
171
172/// Paired with the `switch_type` production in the Context Free Grammar.
173#[derive(Debug, Clone)]
174pub struct Switch {
175    pub span: Span,
176    pub identifier: Option<String>,
177    pub key_parameter: ParameterField,
178    pub cases: Vec<Case>,
179}
180
181/// Paired with the `switch_case` production in the Context Free Grammar.
182#[derive(Debug, Clone)]
183pub struct Case {
184    pub span: Span,
185    // `None` condition means this is a default case.
186    pub condition: Option<TypeValue>,
187    pub fields: Vec<NamedField>,
188    pub breaks: bool, // if case ends with a break
189}
190
191/// Paired with the `named_field` production in the Context Free Grammar.
192#[derive(Debug, Clone)]
193pub enum NamedField {
194    ParameterField(ParameterField),
195    MethodAsField(MethodAsField),
196}
197
198/// Paired with the `method_as_field` production in the Context Free Grammar.
199#[derive(Debug, Clone)]
200pub struct MethodAsField {
201    pub span: Span,
202    pub identifier: String,
203    pub parameters: MethodBody,
204}
205
206/// Paired with the `method_body` production in the Context Free Grammar.
207pub type MethodBody = Vec<Parameter>;
208
209/// Paired with the `parameter_field` production in the Context Free Grammar.
210#[derive(Debug, Clone)]
211pub struct ParameterField {
212    pub parameter: Parameter,
213    pub keywords: KeywordList,
214}
215
216impl From<Parameter> for ParameterField {
217    fn from(value: Parameter) -> Self {
218        Self {
219            parameter: value,
220            keywords: vec![],
221        }
222    }
223}
224
225/// Paired with the `dc_keyword_list` production in the Context Free Grammar.
226pub type KeywordList = Vec<String>;
227
228/// Paired with the `parameter` production in the Context Free Grammar.
229#[derive(Debug, Clone)]
230pub struct Parameter {
231    pub span: Span,
232    pub data_type: NonMethodDataType,
233    pub identifier: Option<String>,
234    pub default_value: Option<TypeValue>,
235}
236
237impl From<NonMethodType> for Parameter {
238    fn from(value: NonMethodType) -> Self {
239        Self {
240            span: value.span,
241            data_type: value.data_type,
242            identifier: value.identifier,
243            default_value: None,
244        }
245    }
246}
247
248/// Paired with the `nonmethod_type` production in the Context Free Grammar.
249#[derive(Debug, Clone)]
250pub struct NonMethodType {
251    pub span: Span,
252    pub identifier: Option<String>,
253    pub data_type: NonMethodDataType,
254}
255
256#[derive(Debug, Clone)]
257pub enum NonMethodDataType {
258    NumericType(NumericType),
259    StructType(String),
260    TypeWithArray(TypeWithArray),
261}
262
263/// Paired with the `type_with_array` production in the Context Free Grammar.
264#[derive(Debug, Clone)]
265pub struct TypeWithArray {
266    pub span: Span,
267    pub data_type: ArrayableType,
268    pub array_ranges: Vec<ArrayRange>,
269}
270
271#[derive(Debug, Clone)]
272pub enum ArrayableType {
273    Numeric(NumericType),
274    Struct(String),
275    Sized(SizedTypeToken),
276}
277
278/// Paired with the `array_expansion` production in the Context Free Grammar.
279pub type ArrayExpansion = (TypeValue, u32);
280
281/// Paired with the `type_or_sized_value` production in the Context Free Grammar.
282#[derive(Debug, Clone)]
283pub enum TypeOrSizedValue {
284    Type(TypeValue),
285    Sized(DCToken),
286}
287
288/// Paired with the `type_value` production in the Context Free Grammar.
289#[derive(Debug, Clone)]
290pub enum TypeValue {
291    I64(i64),
292    Char(char),
293    String(String),
294    ArrayValue(Vec<ArrayExpansion>),
295}
296
297/// Paired with the `numeric_type` production in the Context Free Grammar.
298#[derive(Debug, Clone)]
299pub struct NumericType {
300    pub span: Span,
301    pub base_type: DCTypeEnum,
302    // Transforms
303    pub modulus: Option<f64>,
304    pub divisor: Option<f64>,
305    pub range: Option<NumericRange>,
306}
307
308impl NumericType {
309    pub fn from_type(value: DCTypeEnum, span: Span) -> Self {
310        Self {
311            span,
312            base_type: value,
313            modulus: None,
314            divisor: None,
315            range: None,
316        }
317    }
318
319    pub fn add_modulus(&mut self, value: Number) {
320        match value {
321            Number::Decimal(dl) => {
322                self.modulus = Some(dl as f64);
323            }
324            Number::Float(fl) => {
325                self.modulus = Some(fl);
326            }
327        }
328    }
329
330    pub fn add_divisor(&mut self, value: Number) {
331        match value {
332            Number::Decimal(dl) => {
333                self.divisor = Some(dl as f64);
334            }
335            Number::Float(fl) => {
336                self.divisor = Some(fl);
337            }
338        }
339    }
340}
341
342/// Paired with the `numeric_range` production in the Context Free Grammar.
343pub type NumericRange = std::ops::Range<f64>;
344
345/// Paired with the `array_range` production in the Context Free Grammar.
346pub type ArrayRange = std::ops::Range<f64>;
347
348/// Paired with the `sized_type_token` production in the Context Free Grammar.
349#[derive(Debug, Clone)]
350pub enum SizedTypeToken {
351    String,
352    Blob,
353}
354
355/// Paired with the `char_or_number` production in the Context Free Grammar.
356#[derive(Debug, Clone, Copy)]
357pub enum CharOrNumber {
358    Char(char),
359    I64(i64),
360    F64(f64),
361}
362
363/// Paired with the 'number' production in the Context Free Grammar.
364pub enum Number {
365    Decimal(i64),
366    Float(f64),
367}
368
369/// Paired with the `char_or_u16` production in the Context Free Grammar.
370#[derive(Debug, Clone, Copy)]
371pub enum CharOrU16 {
372    Char(char),
373    U16(u16),
374}
375
376#[derive(Debug, Clone)]
377pub struct DataType {
378    pub span: Span,
379    pub token: DCToken,
380    pub dctype: DCTypeEnum,
381}
382
383impl DataType {
384    pub fn from_token(value: DCToken, span: Span) -> Self {
385        Self {
386            span,
387            token: value.clone(),
388            dctype: match value {
389                DCToken::Float32T => DCTypeEnum::TFloat32,
390                DCToken::Float64T => DCTypeEnum::TFloat64,
391                DCToken::Int8T => DCTypeEnum::TInt8,
392                DCToken::Int16T => DCTypeEnum::TInt16,
393                DCToken::Int32T => DCTypeEnum::TInt32,
394                DCToken::Int64T => DCTypeEnum::TInt64,
395                DCToken::UInt8T => DCTypeEnum::TUInt8,
396                DCToken::UInt16T => DCTypeEnum::TUInt16,
397                DCToken::UInt32T => DCTypeEnum::TUInt32,
398                DCToken::UInt64T => DCTypeEnum::TUInt64,
399                _ => panic!("DC token matches no production in CFG."),
400            },
401        }
402    }
403}