donet_core/parser/
parser.rs

1/*
2    This file is part of Donet.
3
4    Copyright © 2024 Max Rodriguez <[email protected]>
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//! Definition of the DC language [`Context Free Grammar`] for the
21//! LALR(1) parser processing the stream of lexical tokens.
22//!
23//! [`Context Free Grammar`]: https://en.wikipedia.org/wiki/Context-free_grammar
24
25// Please see plex issue #45. https://github.com/goffrie/plex/issues/45
26#![allow(
27    clippy::type_complexity,
28    clippy::redundant_field_names,
29    clippy::ptr_arg,
30    clippy::redundant_closure_call,
31    clippy::enum_variant_names,
32    clippy::let_unit_value,
33    clippy::unused_unit // non-terminals that return void require `()`
34)]
35
36use super::ast;
37use super::lexer::DCToken::*;
38use super::lexer::{DCToken, Span};
39use crate::dctype::DCTypeEnum;
40
41use plex::parser;
42use std::mem::discriminant;
43
44parser! {
45    fn parse_(DCToken, Span);
46
47    // Instruct parser how to combine two spans
48    (a, b) {
49        Span {
50            min: a.min,
51            max: b.max,
52            line: a.line, // only keep a's line number
53        }
54    }
55
56    // The 'dc_file' production is the root production of the grammar.
57    // Plex knows this is the start symbol of our grammar as it is declared first.
58    dc_file: ast::Root {
59        epsilon => ast::Root {
60            type_declarations: vec![],
61        },
62        dc_file[mut root] type_decl[type_decl] => {
63            root.type_declarations.push(type_decl);
64            root
65        },
66    }
67
68    type_decl: ast::TypeDeclaration {
69        // only python-style imports do not require a semicolon delimiter
70        python_style_import[py_imports] => ast::TypeDeclaration::PythonImport(py_imports),
71        keyword_type[keyword] Semicolon => ast::TypeDeclaration::KeywordType(keyword),
72        struct_type[strct] Semicolon => ast::TypeDeclaration::StructType(strct),
73        distributed_class_type[dclass] Semicolon => ast::TypeDeclaration::DClassType(dclass),
74        type_definition[type_def] Semicolon => match type_def {
75            Some(td) => ast::TypeDeclaration::TypedefType(td),
76            None => ast::TypeDeclaration::Ignore,
77        },
78    }
79
80    // ---------- Python-style Imports ---------- //
81
82    python_style_import: ast::PythonImport {
83        py_module[module] dclass_import[class] => {
84            ast::PythonImport {
85                span: span!(),
86                module,
87                class,
88            }
89        },
90    }
91
92    // e.g. "from views ..."
93    // e.g. "from game.views.Donut/AI ..."
94    py_module: ast::SymbolWithViews {
95        From modules[modules] view_suffixes[views] => {
96
97            // We need to join all module identifiers into one string
98            let mut modules_string: String = String::new();
99
100            for (i, mod_) in modules.into_iter().enumerate() {
101                if i != 0 {
102                    modules_string.push('.');
103                }
104                modules_string.push_str(&mod_);
105            }
106
107            ast::SymbolWithViews {
108                span: span!(),
109                symbol: modules_string,
110                symbol_views: views,
111            }
112        }
113    }
114
115    // Bundles module names in 'from' statements, e.g. "myviews.Donut".
116    modules: Vec<String> {
117        legal_python_module_identifiers[module] => vec![module],
118        modules[mut vector] Period legal_python_module_identifiers[module] => {
119            vector.push(module);
120            vector
121        }
122    }
123
124    /* Mandatory fix for resolving issue #12.
125     *
126     * Specifically used by the Python-style DC import grammar to accept
127     * **LEGAL** python module identifiers that may lexed as other tokens.
128     */
129    legal_python_module_identifiers: String {
130        Identifier(id) => id,
131        DCKeyword(id) => id,
132        CharT => "char".to_string(),
133        Int8T => "int8".to_string(),
134        Int16T => "int16".to_string(),
135        Int32T => "int32".to_string(),
136        Int64T => "int64".to_string(),
137        UInt8T => "uint8".to_string(),
138        UInt16T => "uint16".to_string(),
139        UInt32T => "uint32".to_string(),
140        UInt64T => "uint64".to_string(),
141        Float32T => "float32".to_string(),
142        Float64T => "float64".to_string(),
143        Int8ArrayT => "int8array".to_string(),
144        Int16ArrayT => "int16array".to_string(),
145        Int32ArrayT => "int32array".to_string(),
146        UInt8ArrayT => "uint8array".to_string(),
147        UInt16ArrayT => "uint16array".to_string(),
148        UInt32ArrayT => "uint32array".to_string(),
149        UInt32UInt8ArrayT => "uint32uint8array".to_string(),
150        StringT => "string".to_string(),
151        BlobT => "blob".to_string(),
152        Blob32T => "blob32".to_string(),
153        DClass => "dclass".to_string(),
154        Struct => "struct".to_string(),
155        Keyword => "keyword".to_string(),
156        Typedef => "typedef".to_string(),
157        Switch => "switch".to_string(),
158        Default => "default".to_string(),
159        Break => "break".to_string(),
160    }
161
162    // e.g. "... import DistributedDonut/AI/OV"
163    // e.g. "... import *"
164    dclass_import: ast::SymbolWithViews {
165        Import Identifier(c) view_suffixes[cs] => ast::SymbolWithViews {
166            span: span!(),
167            symbol: c,
168            symbol_views: cs,
169        },
170        Import Star => ast::SymbolWithViews {
171            span: span!(),
172            symbol: "*".into(),
173            symbol_views: vec![],
174        },
175    }
176
177    // Bundle up all views of a dclass/module to be imported, into a vector
178    // of strings, each corresponding to a view suffix. (AI, UD, OV..)
179    //
180    //      view_suffixes -> ε
181    //      view_suffixes -> view_suffixes '/' Identifier
182    view_suffixes: ast::ViewSuffixes {
183        epsilon => vec![],
184        view_suffixes[mut si] ForwardSlash view_suffix[id] => {
185            si.push(id);
186            si
187        }
188    }
189
190    view_suffix: ast::ViewSuffix {
191        ViewSuffix(id) => ast::ViewSuffix {
192            span: span!(),
193            view: id,
194        },
195    }
196
197    // ---------- Type Definitions ---------- //
198
199    type_definition: Option<ast::TypeDefinition> {
200        Typedef nonmethod_type_with_name[nmt] => {
201            Some(ast::TypeDefinition {
202                span: span!(),
203                deprecated: true,
204                data_type: nmt.data_type,
205                array_range: None,
206                alias_identifier: nmt.identifier,
207            })
208        },
209        // This rule handles a specific piece of illegal grammar that is legal in Panda.
210        // The parser will print a useful message to stdout describing the issue,
211        // and will ignore this grammar and continue without a panic.
212        Typedef UInt8T BoolT => {
213            println!("{}\n\n\"typedef uint8 bool;\" is deprecated!\n\n\
214            Cannot declare type alias for uint8 as 'bool', as it is a reserved identifier \
215            in the DC language.\nDonet introduces the 'bool' data type, which is an alias \
216            for uint8 under the hood.\n", span!());
217            None
218        },
219        type_definition[td] OpenBrackets array_range[ar] CloseBrackets => {
220            if td.is_none() {
221                return td;
222            }
223            let mut type_def = td.unwrap();
224
225            type_def.array_range = ar;
226
227            Some(type_def)
228        },
229    }
230
231    // ---------- DC Keyword ---------- //
232
233    keyword_type: ast::KeywordDefinition {
234        Keyword Identifier(id) => {
235            ast::KeywordDefinition {
236                span: span!(),
237                identifier: id,
238                historical: false,
239            }
240        },
241        Keyword DCKeyword(historic) => {
242            ast::KeywordDefinition {
243                span: span!(),
244                identifier: historic,
245                historical: true,
246            }
247        }
248    }
249
250    // ---------- Distributed Class ---------- //
251
252    distributed_class_type: ast::DClass {
253        DClass Identifier(id) optional_inheritance[parents] OpenBraces
254        optional_class_fields[fields] CloseBraces => {
255            ast::DClass {
256                span: span!(),
257                identifier: id,
258                parents,
259                fields,
260            }
261        }
262    }
263
264    optional_class_fields: ast::ClassFields {
265        epsilon => vec![],
266        optional_class_fields[mut vector] class_field[field] Semicolon => {
267            vector.push(field);
268            vector
269        },
270    }
271
272    class_field: ast::AtomicOrMolecular {
273        // e.g. "setPos(float64 x, float64 y, float64 z) ram broadcast" (atomic)
274        // e.g. "string DcObjectType db" (plain field)
275        named_field[nf] dc_keyword_list[keywords] => {
276            ast::AtomicOrMolecular::Atomic(
277                ast::AtomicField::from_named_field(nf, keywords, span!())
278            )
279        },
280        // e.g. "setStats : setAvatarCount, setNewAvatarCount"
281        molecular_field[molecular] => {
282            ast::AtomicOrMolecular::Molecular(molecular)
283        },
284    }
285
286    optional_inheritance: Vec<String> {
287        epsilon => vec![],
288        Colon Identifier(parent) class_parents[mut cp] => {
289            cp.insert(0, parent);
290            cp
291        },
292    }
293
294    class_parents: Vec<String> {
295        epsilon => vec![],
296        class_parents[mut cp] Comma Identifier(class) => {
297            cp.push(class);
298            cp
299        }
300    }
301
302    // ---------- Molecular Field ---------- //
303
304    // e.g. "setStats : setAvatarCount, setNewAvatarCount"
305    molecular_field: ast::MolecularField {
306        // Molecular fields require at least one atomic name.
307        // They **should** require a minimum of two as suggested by Astron
308        // docs and Panda source comments, but one atomic name is historically legal.
309        Identifier(id) Colon Identifier(first_atomic) molecular_atom_list[mut atomics] => {
310            ast::MolecularField {
311                span: span!(),
312                identifier: id,
313                atomic_field_identifiers: {
314                    let mut vec: Vec<String> = vec![first_atomic];
315
316                    vec.append(&mut atomics);
317                    vec
318                }
319            }
320        },
321    }
322
323    molecular_atom_list: Vec<String> {
324        epsilon => vec![],
325        molecular_atom_list[mut atomics] Comma Identifier(atomic_name) => {
326            atomics.push(atomic_name);
327            atomics
328        },
329    }
330
331    // ---------- DC Struct ---------- //
332
333    struct_type: ast::Struct {
334        Struct Identifier(id) OpenBraces struct_fields[fields] CloseBraces => {
335            ast::Struct {
336                span: span!(),
337                identifier: id,
338                fields,
339            }
340        },
341    }
342
343    struct_fields: Vec<ast::StructField> {
344        epsilon => vec![],
345        struct_fields[mut vec] struct_field[field] Semicolon => {
346            vec.push(field);
347            vec
348        },
349    }
350
351    struct_field: ast::StructField {
352        switch_type[sw] => ast::StructField::Switch(sw),
353        unnamed_field[pf] => ast::StructField::ParameterField(pf),
354        named_field[nf] => nf.into(),
355    }
356
357    // ---------- DC Switch Statements ---------- //
358
359    switch_type: ast::Switch {
360        Switch optional_name[id] OpenParenthesis parameter_field[field] CloseParenthesis
361        OpenBraces switch_cases[cases] CloseBraces => {
362            ast::Switch {
363                span: span!(),
364                identifier: id,
365                key_parameter: field,
366                cases: cases,
367            }
368        }
369    }
370
371    switch_cases: Vec<ast::Case> {
372        epsilon => vec![],
373        switch_cases[mut vec] switch_case[case] => {
374            vec.push(case);
375            vec
376        },
377    }
378
379    switch_case: ast::Case {
380        switch_case_body[mut case] optional_break[breaks] => {
381            case.span = span!(); // update span
382            case.breaks = breaks;
383            case
384        }
385    }
386
387    switch_case_body: ast::Case {
388        Default Colon => ast::Case {
389            span: span!(),
390            condition: None, // `None` means default
391            fields: vec![],
392            breaks: false,
393        },
394        Case type_value[condition] Colon => ast::Case {
395            span: span!(),
396            condition: Some(condition),
397            fields: vec![],
398            breaks: false,
399        },
400        switch_case[mut case] switch_field[field] Semicolon => {
401            case.fields.push(field);
402            case
403        },
404    }
405
406    switch_field: ast::NamedField {
407        named_field[nf] => nf,
408    }
409
410    optional_break: bool {
411        epsilon => false,
412        Break Semicolon => true,
413    }
414
415    // ---------- DC Fields ---------- //
416
417    named_field: ast::NamedField {
418        method_as_field[mf] => ast::NamedField::MethodAsField(mf),
419        nonmethod_type_with_name[nmt] => {
420            let param: ast::Parameter = nmt.into();
421
422            ast::NamedField::ParameterField(param.into())
423        },
424        field_with_name_as_array[field] => ast::NamedField::ParameterField(field),
425        field_with_name_and_default[field] => ast::NamedField::ParameterField(field),
426    }
427
428    field_with_name_as_array: ast::ParameterField {
429        nonmethod_type_with_name[nmt]
430        OpenBrackets array_range[_] CloseBrackets => {
431            let param: ast::Parameter = nmt.into();
432
433            // FIXME: apply array range
434            param.into()
435        },
436        field_with_name_as_array[pf]
437        OpenBrackets array_range[_] CloseBrackets => {
438            // FIXME: apply array range
439            pf
440        },
441    }
442
443    field_with_name_and_default: ast::ParameterField {
444        nonmethod_type_with_name[nmt] Equals type_value[value] => {
445            let mut param: ast::Parameter = nmt.into();
446
447            param.default_value = Some(value);
448            param.into()
449        },
450        field_with_name_as_array[mut field] Equals type_value[value] => {
451            field.parameter.default_value = Some(value);
452            field
453        },
454    }
455
456    unnamed_field: ast::ParameterField {
457        nonmethod_type[nmt] => {
458            let param: ast::Parameter = nmt.into();
459            param.into()
460        },
461        nonmethod_type[nmt] Equals type_value[value] => {
462            let mut param: ast::Parameter = nmt.into();
463            param.default_value = Some(value);
464
465            param.into()
466        },
467    }
468
469    // e.g. "setName(string)"
470    method_as_field: ast::MethodAsField {
471        Identifier(id) method_body[parameters] => {
472            ast::MethodAsField {
473                span: span!(),
474                identifier: id,
475                parameters,
476            }
477        },
478    }
479
480    // e.g. "(int8, int16, string, blob)"
481    method_body: ast::MethodBody {
482        OpenParenthesis parameters[params] CloseParenthesis => params,
483    }
484
485    // ---------- Parameter Fields ---------- //
486
487    parameter_fields: Vec<ast::ParameterField> {
488        epsilon => vec![],
489        parameter_fields[mut vec] Comma parameter_field[pf] => {
490            vec.push(pf);
491            vec
492        },
493    }
494
495    parameter_field: ast::ParameterField {
496        parameter[param] dc_keyword_list[kl] => {
497            let mut pf: ast::ParameterField = param.into();
498
499            pf.keywords = kl;
500            pf
501        },
502    }
503
504    dc_keyword_list: ast::KeywordList {
505        epsilon => vec![],
506        dc_keyword_list[mut vec] Identifier(keyword) => {
507            vec.push(keyword);
508            vec
509        }
510        dc_keyword_list[mut vec] DCKeyword(keyword) => {
511            vec.push(keyword);
512            vec
513        }
514    }
515
516    // ---------- Parameter ---------- //
517
518    parameters: Vec<ast::Parameter> {
519        epsilon => vec![],
520        #[no_reduce(Comma)] // don't reduce if we're expecting more params
521        parameters[mut vector] parameter[param] => {
522            vector.push(param);
523            vector
524        },
525        parameters[mut vector] parameter[param] Comma => {
526            vector.push(param);
527            vector
528        },
529    }
530
531    parameter: ast::Parameter {
532        nonmethod_type[nmt] => nmt.into(),
533        nonmethod_type[nmt] Equals type_value[value] => {
534            let mut param: ast::Parameter = nmt.into();
535
536            param.default_value = Some(value);
537            param
538        },
539    }
540
541    // ---------- DC Data Types ---------- //
542
543    nonmethod_type_with_name: ast::NonMethodType {
544        nonmethod_type[mut nmt] Identifier(id) => {
545            nmt.identifier = Some(id);
546            nmt
547        },
548    }
549
550    nonmethod_type: ast::NonMethodType {
551        nonmethod_type_no_array[nmt] => nmt,
552        #[no_reduce(OpenBrackets)] // avoids conflict with `type_with_array`
553        type_with_array[twa] => ast::NonMethodType {
554            span: span!(),
555            identifier: None,
556            data_type: ast::NonMethodDataType::TypeWithArray(twa),
557        },
558    }
559
560    nonmethod_type_no_array: ast::NonMethodType {
561        #[no_reduce(OpenBrackets)]
562        Identifier(id) => ast::NonMethodType {
563            span: span!(),
564            identifier: None,
565            data_type: ast::NonMethodDataType::StructType(id),
566        },
567        #[no_reduce(OpenBrackets)]
568        numeric_type[nt] => ast::NonMethodType {
569            span: span!(),
570            identifier: None,
571            data_type: ast::NonMethodDataType::NumericType(nt),
572        },
573        #[no_reduce(OpenBrackets)]
574        builtin_array_type[twa] => ast::NonMethodType {
575            span: span!(),
576            identifier: None,
577            data_type: ast::NonMethodDataType::TypeWithArray(twa),
578        },
579    }
580
581    type_with_array: ast::TypeWithArray {
582        numeric_type[nt] OpenBrackets array_range[ar] CloseBrackets => {
583            ast::TypeWithArray {
584                span: span!(),
585                data_type: ast::ArrayableType::Numeric(nt),
586                array_ranges: match ar {
587                    Some(range) => vec![range],
588                    None => vec![],
589                },
590            }
591        },
592        Identifier(id) OpenBrackets array_range[ar] CloseBrackets => {
593            ast::TypeWithArray {
594                span: span!(),
595                data_type: ast::ArrayableType::Struct(id),
596                array_ranges: match ar {
597                    Some(range) => vec![range],
598                    None => vec![],
599                },
600            }
601        },
602        builtin_array_type[mut twa] OpenBrackets array_range[ar] CloseBrackets => {
603            if let Some(range) = ar {
604                twa.array_ranges.push(range);
605            }
606            twa
607        },
608        type_with_array[mut twa] OpenBrackets array_range[ar] CloseBrackets => {
609            if let Some(range) = ar {
610                twa.array_ranges.push(range);
611            }
612            twa
613        },
614    }
615
616    builtin_array_type: ast::TypeWithArray {
617        sized_type_token[st] => ast::TypeWithArray {
618            span: span!(),
619            data_type: ast::ArrayableType::Sized(st),
620            array_ranges: vec![],
621        },
622        sized_type_token[st] OpenParenthesis array_range[ar] CloseParenthesis => {
623            ast::TypeWithArray {
624                span: span!(),
625                data_type: ast::ArrayableType::Sized(st),
626                array_ranges: match ar {
627                    Some(range) => vec![range],
628                    None => vec![],
629                },
630            }
631        },
632    }
633
634    // e.g. "[0 * 14]"
635    array_expansion: ast::ArrayExpansion {
636        type_value[tv] => (tv, 1_u32), // factor of 1 by default
637        signed_integer[i] Star unsigned_32_bit_int[f] => (ast::TypeValue::I64(i), f),
638        DecimalLiteral(i) Star unsigned_32_bit_int[f] => (ast::TypeValue::I64(i), f),
639        HexLiteral(hs) Star unsigned_32_bit_int[f] => (ast::TypeValue::String(hs), f),
640        StringLiteral(s) Star unsigned_32_bit_int[f] => (ast::TypeValue::String(s), f),
641    }
642
643    array_value: Vec<ast::ArrayExpansion> {
644        OpenBrackets CloseBrackets => vec![],
645        OpenBrackets element_values[ev] CloseBrackets => ev,
646    }
647
648    element_values: Vec<ast::ArrayExpansion> {
649        array_expansion[ae] => vec![ae],
650        element_values[mut ev] Comma array_expansion[ae] => {
651            ev.push(ae);
652            ev
653        },
654    }
655
656    parameter_values: ast::ParameterValues {
657        type_value[tv] => vec![tv],
658        parameter_values[mut vec] Comma type_value[tv] => {
659            vec.push(tv);
660            vec
661        },
662    }
663
664    type_or_sized_value: ast::TypeOrSizedValue {
665        type_value[tv] => ast::TypeOrSizedValue::TypeValue(tv),
666        sized_type_token[st] => ast::TypeOrSizedValue::SizedValue(st),
667    }
668
669    type_value: ast::TypeValue {
670        BooleanLiteral(b) => ast::TypeValue::I64(match b {
671            true => 1,
672            false => 0,
673        }),
674        DecimalLiteral(i) => ast::TypeValue::I64(i),
675        CharacterLiteral(c) => ast::TypeValue::Char(c),
676        StringLiteral(s) => ast::TypeValue::String(s),
677        HexLiteral(hs) => ast::TypeValue::String(hs),
678        signed_integer[i] => ast::TypeValue::I64(i),
679        array_value[av] => ast::TypeValue::ArrayValue(av),
680    }
681
682    numeric_type: ast::NumericType {
683        numeric_type_token[nt] => nt,
684        numeric_with_explicit_cast[nt] => nt,
685        numeric_with_modulus[nt] => nt,
686        numeric_with_divisor[nt] => nt,
687        numeric_with_range[nt] => nt,
688    }
689
690    numeric_with_range: ast::NumericType {
691        numeric_type_token[mut nt] OpenParenthesis numeric_range[nr] CloseParenthesis => {
692            nt.range = nr;
693            nt
694        },
695        numeric_with_explicit_cast[mut nt] OpenParenthesis numeric_range[nr] CloseParenthesis => {
696            nt.range = nr;
697            nt
698        },
699        numeric_with_modulus[mut nt] OpenParenthesis numeric_range[nr] CloseParenthesis => {
700            nt.range = nr;
701            nt
702        },
703        numeric_with_divisor[mut nt] OpenParenthesis numeric_range[nr] CloseParenthesis => {
704            nt.range = nr;
705            nt
706        },
707    }
708
709    numeric_with_divisor: ast::NumericType {
710        numeric_type_token[mut nt] ForwardSlash number[num] => {
711            nt.add_divisor(num);
712            nt
713        },
714        numeric_with_explicit_cast[mut nt] ForwardSlash number[num] => {
715            nt.add_divisor(num);
716            nt
717        },
718        numeric_with_modulus[mut nt] ForwardSlash number[num] => {
719            nt.add_divisor(num);
720            nt
721        },
722    }
723
724    numeric_with_modulus: ast::NumericType {
725        numeric_type_token[mut nt] Percent number[num] => {
726            nt.add_modulus(num);
727            nt
728        },
729        numeric_with_explicit_cast[mut nt] Percent number[num] => {
730            nt.add_modulus(num);
731            nt
732        },
733    }
734
735    // This is unique to Donet, and a new addition to the historic DC language.
736    // Originally, the DC system was used with Python clients, which do not need
737    // strict type annotations as Python is a dynamically typed language.
738    //
739    // Since we are not expecting the client to use a dynamically typed language, we need
740    // to explicitly tell the client what data type to cast to when we perform these
741    // operations on numeric types after they are received from the network.
742    numeric_with_explicit_cast: ast::NumericType {
743        // Explicit casts do not use the `numeric_type_token` non-terminal, because
744        // there is zero need to cast any numeric data type to a Char or Bool, since
745        // this is used for types that have arithmetic operations applied, such as division.
746        //
747        // Also because it is 2:27 AM and its giving me a shift-reduce conflict again.
748        numeric_type_token[mut nt]
749        OpenParenthesis signed_integer_type[dt] CloseParenthesis => {
750            nt.cast = Some(dt);
751            nt
752        },
753        numeric_type_token[mut nt]
754        OpenParenthesis unsigned_integer_type[dt] CloseParenthesis => {
755            nt.cast = Some(dt);
756            nt
757        },
758        numeric_type_token[mut nt]
759        OpenParenthesis floating_point_type[dt] CloseParenthesis => {
760            nt.cast = Some(dt);
761            nt
762        },
763    }
764
765    numeric_range: Option<ast::NumericRange> {
766        epsilon => None,
767
768        char_or_number[v] => match v {
769            ast::CharOrNumber::Char(c) => {
770                let min_max: f64 = f64::from(u32::from(c));
771                Some(min_max .. min_max)
772            },
773            ast::CharOrNumber::I64(i) => {
774                let min_max: f64 = i as f64;
775                Some(min_max .. min_max)
776            },
777            ast::CharOrNumber::F64(f) => Some(f .. f),
778        },
779
780        char_or_number[min] Hyphen char_or_number[max] => {
781            assert!(
782                discriminant(&min) == discriminant(&max),
783                "{}\nCannot define a numeric range with a min and max of different data types!",
784                span!()
785            );
786
787            match min {
788                ast::CharOrNumber::Char(min_c) => {
789                    let min: f64 = f64::from(u32::from(min_c));
790                    let max: f64 = match max {
791                        ast::CharOrNumber::Char(max_c) => f64::from(u32::from(max_c)),
792                        _ => unreachable!("Assertion makes this case impossible."),
793                    };
794                    Some(min .. max)
795                },
796                ast::CharOrNumber::I64(min_i) => {
797                    Some(min_i as f64 .. match max {
798                        ast::CharOrNumber::I64(max_i) => max_i as f64,
799                        _ => unreachable!("Assertion makes this case impossible."),
800                    })
801                },
802                ast::CharOrNumber::F64(min_f) => Some(min_f .. match max {
803                    ast::CharOrNumber::F64(max_f) => max_f,
804                    _ => unreachable!("Assertion makes this case impossible."),
805                }),
806            }
807        },
808    }
809
810    array_range: Option<ast::NumericRange> {
811        epsilon => None,
812        char_or_u16[v] => match v {
813            ast::CharOrU16::Char(c) => {
814                let min_max: f64 = f64::from(u32::from(c));
815                Some(min_max .. min_max)
816            },
817            ast::CharOrU16::U16(u) => {
818                let min_max: f64 = f64::from(u);
819                Some(min_max .. min_max)
820            },
821        },
822        char_or_u16[min] Hyphen char_or_u16[max] => {
823            let min_float: f64 = match min {
824                ast::CharOrU16::Char(c) => f64::from(u32::from(c)),
825                ast::CharOrU16::U16(u) => f64::from(u),
826            };
827            let max_float: f64 = match max {
828                ast::CharOrU16::Char(c) => f64::from(u32::from(c)),
829                ast::CharOrU16::U16(u) => f64::from(u),
830            };
831            Some(min_float .. max_float)
832        },
833    }
834
835    // Both of these types represent a sized type (aka, array type)
836    // Strings and blobs are another form of array types.
837    sized_type_token: ast::SizedTypeToken {
838        StringT => ast::SizedTypeToken::String,
839        BlobT => ast::SizedTypeToken::Blob,
840        Blob32T => ast::SizedTypeToken::Blob32,
841        array_data_type[dt] => match dt.token {
842            Int8ArrayT => ast::SizedTypeToken::Int8Array,
843            Int16ArrayT => ast::SizedTypeToken::Int16Array,
844            Int32ArrayT => ast::SizedTypeToken::Int32Array,
845            UInt8ArrayT => ast::SizedTypeToken::UInt8Array,
846            UInt16ArrayT => ast::SizedTypeToken::UInt16Array,
847            UInt32ArrayT => ast::SizedTypeToken::UInt32Array,
848            UInt32UInt8ArrayT => ast::SizedTypeToken::UInt32UInt8Array,
849            _ => unreachable!("Not possible due to production rules."),
850        },
851    }
852
853    numeric_type_token: ast::NumericType {
854        CharT => ast::NumericType::from_type(DCTypeEnum::TChar, span!()),
855        // 'bool' is an alias for uint8
856        BoolT => ast::NumericType::from_type(DCTypeEnum::TUInt8, span!()),
857        signed_integer_type[dt] => ast::NumericType::from_type(dt.dctype, span!()),
858        unsigned_integer_type[dt] => ast::NumericType::from_type(dt.dctype, span!()),
859        floating_point_type[dt] => ast::NumericType::from_type(dt.dctype, span!()),
860    }
861
862    char_or_number: ast::CharOrNumber {
863        CharacterLiteral(c) => ast::CharOrNumber::Char(c),
864        signed_integer[v] => ast::CharOrNumber::I64(v),
865
866        number[num] => match num {
867            ast::Number::Decimal(dl) => ast::CharOrNumber::I64(dl),
868            ast::Number::Float(fl) => ast::CharOrNumber::F64(fl),
869        },
870    }
871
872    signed_integer: i64 {
873        Plus DecimalLiteral(dl) => dl,
874        Hyphen DecimalLiteral(dl) => -dl, // hyphen consumed by lexer, so its parsed as positive
875    }
876
877    number: ast::Number {
878        DecimalLiteral(dl) => ast::Number::Decimal(dl),
879        FloatLiteral(fl) => ast::Number::Float(fl),
880    }
881
882    char_or_u16: ast::CharOrU16 {
883        CharacterLiteral(cl) => ast::CharOrU16::Char(cl),
884        unsigned_32_bit_int[u] => ast::CharOrU16::U16(u as u16),
885    }
886
887    // In Panda's parser, this production is known as 'small_unsigned_integer'.
888    // C++ standard for an 'unsigned int' size is at least 16 bits.
889    // 16 bits for LP32 data model; ILP32, LLP64, & LP64 are 32 bits.
890    // Most C/C++ compilers store 'unsigned int' types with 32 bits.
891    unsigned_32_bit_int: u32 {
892        DecimalLiteral(v) => {
893            match u32::try_from(v) {
894                Ok(n) => { n },
895                Err(err) => {
896                    // Downcast failed, number must be out of range.
897                    panic!("{}\nNumber out of range for u32.\n{}", span!(), err);
898                },
899            }
900        }
901    }
902
903    floating_point_type: ast::DataType {
904        Float32T => ast::DataType::from_token(Float32T, span!()),
905        Float64T => ast::DataType::from_token(Float64T, span!()),
906    }
907
908    signed_integer_type: ast::DataType {
909        Int8T => ast::DataType::from_token(Int8T, span!()),
910        Int16T => ast::DataType::from_token(Int16T, span!()),
911        Int32T => ast::DataType::from_token(Int32T, span!()),
912        Int64T => ast::DataType::from_token(Int64T, span!()),
913    }
914
915    unsigned_integer_type: ast::DataType {
916        UInt8T => ast::DataType::from_token(UInt8T, span!()),
917        UInt16T => ast::DataType::from_token(UInt16T, span!()),
918        UInt32T => ast::DataType::from_token(UInt32T, span!()),
919        UInt64T => ast::DataType::from_token(UInt64T, span!()),
920    }
921
922    array_data_type: ast::DataType {
923        Int8ArrayT => ast::DataType::from_token(Int8ArrayT, span!()),
924        Int16ArrayT => ast::DataType::from_token(Int16ArrayT, span!()),
925        Int32ArrayT => ast::DataType::from_token(Int32ArrayT, span!()),
926        UInt8ArrayT => ast::DataType::from_token(UInt8ArrayT, span!()),
927        UInt16ArrayT => ast::DataType::from_token(UInt16ArrayT, span!()),
928        UInt32ArrayT => ast::DataType::from_token(UInt32ArrayT, span!()),
929        UInt32UInt8ArrayT => ast::DataType::from_token(UInt32UInt8ArrayT, span!()),
930    }
931
932    optional_name: Option<String> {
933        epsilon => None,
934        Identifier(id) => Some(id)
935    }
936
937    epsilon: () {
938        => {}, // alias for 'epsilon' (ε), a.k.a 'none' in GNU Bison
939    }
940}
941
942/// Public function for the DC parser, takes in a stream of lexical tokens.
943pub fn parse<I: Iterator<Item = (DCToken, Span)>>(
944    i: I,
945) -> Result<ast::Root, (Option<(DCToken, Span)>, &'static str)> {
946    parse_(i)
947}
948
949#[cfg(test)]
950mod tests {
951    use super::ast;
952    use super::parse;
953    use crate::parser::lexer::Lexer;
954
955    fn parse_dcfile_string(input: &str) -> ast::Root {
956        let lexer = Lexer::new(input).inspect(|tok| eprintln!("token: {:?}", tok));
957        let dc_file_ast: ast::Root = parse(lexer).unwrap();
958
959        dc_file_ast
960    }
961
962    #[test]
963    fn python_module_imports() {
964        let dc_file: ast::Root = parse_dcfile_string(
965            "
966            from example_views import DistributedDonut
967            from views import DistributedDonut/AI/OV
968            from views/AI/OV/UD import DistributedDonut/AI/OV/UD
969            from views/AI import DistributedDonut
970            from game.views.Donut/AI import DistributedDonut/AI
971            from views import *
972
973            /* The next one tests handling legal python identifiers
974            * that may be lexed as tokens other than Id/Module.
975            */
976            from db.char import DistributedDonut
977            ",
978        );
979
980        assert_eq!(dc_file.type_declarations.len(), 7);
981    }
982
983    #[test]
984    fn legal_python_module_identifiers() {
985        // See comment at 'legal_python_module_identifiers' non-terminal.
986        #[rustfmt::skip]
987        let legal_identifiers: Vec<&str> = vec![
988            "char", "int8", "int16", "int32", "int64",
989            "uint8", "uint16", "uint32", "uint64", "float32", "float64",
990            "int8array", "int16array", "int32array",
991            "uint8array", "uint16array", "uint32array", "uint32uint8array",
992            "string", "blob", "blob32", "dclass", "struct", "keyword",
993            "typedef", "switch", "default", "break",
994        ];
995        let mut dc_file: String = String::new();
996
997        for module_name in &legal_identifiers {
998            let code: String = format!("from {} import DistributedClass\n", *module_name);
999            dc_file.push_str(code.as_str());
1000        }
1001        parse_dcfile_string(dc_file.as_str());
1002    }
1003
1004    #[test]
1005    fn keyword_definitions() {
1006        parse_dcfile_string(
1007            "
1008            keyword p2p;
1009            keyword monkey;
1010            keyword unreliable;
1011            keyword db;
1012            ",
1013        );
1014    }
1015
1016    #[test]
1017    fn struct_declarations() {
1018        parse_dcfile_string(
1019            "
1020            struct GiftItem {
1021                blob Item;
1022                string giftTag;
1023            };
1024
1025            struct Activity {
1026                string activityName;
1027                uint8 activityId;
1028            };
1029
1030            struct Party {
1031                activity activities[];
1032                uint8 status;
1033            };
1034
1035            struct Fixture {
1036                bool;
1037                int32/10 x;
1038                int32/10 y;
1039                int32/10 z;
1040                int16/10 h;
1041                int16/10 p;
1042                int16/10 r;
1043                string state;
1044            };
1045            ",
1046        );
1047    }
1048
1049    #[test]
1050    fn distributed_class() {
1051        parse_dcfile_string(
1052            "
1053            dclass Avatar {
1054                string name;
1055                uint16 health;
1056
1057                set_xyzh(int16 x, int16 y, int16 z, int16 h) broadcast required;
1058                indicate_intent(int16 / 10, int16 / 10) ownsend airecv;
1059            };
1060
1061            dclass OfflineShardManager : DistributedObject {
1062                clientSetZone(uint32) airecv clsend;
1063                requestZoneIdMessage(uint32, uint16) airecv clsend;
1064                requestZoneIdResponse(uint32, uint16);
1065            };
1066
1067            dclass ShardStats {
1068                setShardId(uint32) broadcast required ram;
1069                setAvatarCount(uint32) broadcast required ram;
1070                setNewAvatarCount(uint32) broadcast required ram;
1071                setStats : setAvatarCount, setNewAvatarCount;
1072            };
1073
1074            dclass DistributedChild : Parent, Parent2 {
1075            };
1076            ",
1077        );
1078    }
1079
1080    #[test]
1081    fn switch_fields() {
1082        parse_dcfile_string(
1083            "
1084            struct BuffData {
1085                switch (uint16) {
1086                    case 0:
1087                        break;
1088                    case 1:
1089                        uint8 val1;
1090                        break;
1091                    case 2:
1092                        uint8 val1;
1093                        uint8 val2;
1094                        break;
1095                    case 3:
1096                        uint8 val1;
1097                        break;
1098                    case 4:
1099                        int16/100 val1;
1100                        break;
1101                };
1102                switch OptionalName (uint8) {
1103                    case 0:
1104                        break;
1105                    default:
1106                        uint8 value[0-5];
1107                        uint32uint8array value2;
1108                        SomeStruct value3;
1109                        break;
1110                };
1111                switch WithDefault (char) {
1112                    case 'a':
1113                        break;
1114                    case 'b':
1115                    case 'c':
1116                    case 'd':
1117                    default:
1118                        string val1;
1119                        break;
1120                };
1121            };
1122            ",
1123        );
1124    }
1125
1126    #[test]
1127    #[should_panic]
1128    fn switch_redundant_break() {
1129        parse_dcfile_string(
1130            "
1131            struct BuffData {
1132                switch (uint16) {
1133                    case 0:
1134                        break;
1135                        break;
1136                };
1137            };
1138            ",
1139        );
1140    }
1141
1142    #[test]
1143    fn atomic_fields() {
1144        parse_dcfile_string(
1145            "
1146            dclass AtomicFields {
1147                simple();
1148                keyw0rd() ram;
1149                keywords() db ownsend airecv;
1150                parameter(string);
1151                params(bool, char, float64);
1152                named_params(bool flag = true, string text);
1153            };
1154            ",
1155        );
1156    }
1157
1158    #[test]
1159    fn molecular_fields() {
1160        parse_dcfile_string(
1161            "
1162            dclass MolecularFields {
1163                setXYZ : setX, setY, setZ;
1164                setPos : setXYZ;
1165                setXY : setX, setY;
1166                setHPR : setH, setP, setR;
1167            };
1168            ",
1169        );
1170    }
1171
1172    #[test]
1173    fn field_data_types() {
1174        parse_dcfile_string(
1175            "
1176            struct MethodDataTypesTest {
1177                Char character;
1178                blob Item;
1179                blob32 pandaOnlyToken;
1180                float32 astronOnlyToken;
1181                string giftTag;
1182                int32(0-990999) testMethodValue;
1183                int8(-1-1) testNegativeValues;
1184                int8(-5--99) testNegativeValuesPartTwo;
1185                int8(+0-+9) plusForPositiveForSomeReason;
1186                int8array arrayDataTypeTest;
1187                int16array anotherArray;
1188                int32array evenMoreComplexArray;
1189                uint8array byteArray;
1190                uint16array unsignedIntegerArray;
1191                uint32array unsignedLongArray;
1192                uint32uint8array thisWeirdPandaArrayType;
1193            };
1194            ",
1195        );
1196    }
1197
1198    #[test]
1199    fn value_transforms() {
1200        parse_dcfile_string(
1201            "
1202            struct TransformedTypesTest {
1203                int32%360 angle;
1204                int32%360/1000 floatingPointAngle;
1205                int32/1000 efficientFloatIn32Bits;
1206                float32 waitIsntAstronsFloat32TheSame;
1207                int16(int32) forTheStaticallyTypedLanguages;
1208                int16(float64)(0.0-1.0) withRangeTest;
1209                int16(float32)%360/10.0 anotherTest;
1210                int16(uint32)/10 moreTests;
1211                bool thisIsLiterallyJustAn8BitInt;
1212                uint16/1000(0-1) youCanStackThemToo;
1213                int64/10000(+50-+999) [] thisIsValid;
1214                int8%10(0-10) anotherOne;
1215                int32('a'-'b') numericRangeWithChar;
1216                float32(0.1-0.99) floatingRange;
1217                float32%10.0 modulusWithFloat;
1218                float32(float64)%10.0 coverage;
1219                int16%100/10(-80-+100) lastTest;
1220            };
1221            ",
1222        );
1223    }
1224
1225    #[test]
1226    fn numeric_ranges() {
1227        parse_dcfile_string(
1228            "
1229            struct NumericRanges {
1230                int8(0-1) thisIsLiterallyABoolean;
1231                int64(-5) signedRange;
1232                int64(+50-+999) thisIsValid;
1233                int32('a') numericRangeWithChar;
1234                int32('a'-'z') rangeMinMaxWithChar;
1235                float32(0.1-0.99) floatingRange;
1236                float32(0.1) anotherFloatRange;
1237                int32() pandaSaysThisIsLegal;
1238            };
1239            ",
1240        );
1241    }
1242
1243    #[test]
1244    #[should_panic]
1245    fn invalid_numeric_ranges() {
1246        parse_dcfile_string(
1247            "
1248            struct InvalidNumericRange {
1249                uint64('a'-10);
1250            };
1251            ",
1252        );
1253    }
1254
1255    #[test]
1256    fn parameters_with_default() {
1257        parse_dcfile_string(
1258            "
1259            struct ParamsWithDefaultTest {
1260                string = \"\";
1261                MyStruct[] = [];
1262                MyStruct strukt[] = [];
1263                int32 = -99;
1264                string = \"VALUE\";
1265                string = 0xabcdef;
1266                uint16 accessLevel = 0;
1267                bool = false;
1268            };
1269            ",
1270        );
1271    }
1272
1273    #[test]
1274    fn array_ranges() {
1275        parse_dcfile_string(
1276            "
1277            struct ArrayRangesTest {
1278                uint8 test['a'];
1279                uint8 test2[9];
1280                uint32uint8array[0-1] test3;
1281                uint32uint8array[0-1][9-99] test4;
1282                uint8 test5['a'-'b'] [ ];
1283                string(5) test6; // builtin array type
1284            };
1285            ",
1286        );
1287    }
1288
1289    #[test]
1290    fn array_expansions() {
1291        parse_dcfile_string(
1292            "
1293            struct ArrayExpansionsTest {
1294                uint8array test = [0];
1295                uint8array test2 = [0 * 10];
1296                int8array test3 = [-1 * 10];
1297                int8array test4 = [5 * 5, 10 * 10, -2 * 4];
1298                uint8array test5 = [0xf * 10];
1299                uint8array test6 = [\"TEST\" * 2];
1300            };
1301            ",
1302        );
1303    }
1304
1305    #[test]
1306    #[should_panic]
1307    fn integer_literal_overflow() {
1308        parse_dcfile_string(
1309            "
1310            struct OverflowTest {
1311                test(uint8array = [0 * 4294967296]);
1312            };
1313            ",
1314        );
1315    }
1316
1317    #[test]
1318    fn developer_defined_keywords() {
1319        parse_dcfile_string(
1320            "
1321            keyword f6f7;
1322
1323            dclass DistributedDonut {
1324                testingField() f6f7;
1325            };
1326            ",
1327        );
1328    }
1329
1330    #[test]
1331    fn handle_deprecated_bool_alias() {
1332        // The lexer picks up 'bool' as a data type token,
1333        // not an identifier, so it would be illegal grammar.
1334        // This test ensures we handle this as a deprecation warning.
1335        parse_dcfile_string(
1336            "
1337            typedef uint8 bool;
1338            ",
1339        );
1340    }
1341}