1use std::collections::BTreeMap;
10
11use serde::{Deserialize, Serialize};
12
13pub const PROTOCOL_NAME: &str = "wright/opy-hir";
15pub const PROTOCOL_MAJOR: u32 = 2;
17pub const PROTOCOL_VERSION: &str = "2.0.0";
19
20const DEFAULT_VAR_SLOTS: u32 = 128;
31
32pub fn default_var_index(name: &str) -> Option<u32> {
40 if name.is_empty() || name.len() > 2 {
41 return None;
42 }
43 let mut value: u32 = 0;
44 for byte in name.bytes() {
45 if !byte.is_ascii_uppercase() {
46 return None;
47 }
48 value = value * 26 + u32::from(byte - b'A' + 1);
49 }
50 let index = value - 1;
51 (index < DEFAULT_VAR_SLOTS).then_some(index)
52}
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Protocol {
57 pub name: String,
58 pub version: String,
59}
60
61#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63pub struct Generator {
64 pub name: String,
65 pub version: String,
66 pub frontend: String,
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
71pub struct SourceFile {
72 pub id: u32,
73 pub path: String,
74}
75
76#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
78pub struct Define {
79 pub name: String,
80 #[serde(default)]
81 pub is_function: bool,
82 #[serde(default)]
84 pub is_member: bool,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub span: Option<Span>,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
91pub struct Span {
92 pub file: u32,
93 pub start: Position,
94 pub end: Position,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
99pub struct Position {
100 pub line: u32,
101 pub col: u32,
102}
103
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct Program {
107 pub protocol: Protocol,
108 pub generator: Generator,
109 pub files: Vec<SourceFile>,
110 #[serde(default)]
111 pub defines: Vec<Define>,
112 #[serde(default)]
113 pub declarations: Vec<Declaration>,
114 #[serde(default)]
115 pub rules: Vec<RuleEntry>,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub settings: Option<Settings>,
119 #[serde(default)]
123 pub preprocessing: PreprocessingState,
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
128pub struct PreprocessingState {
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub main_file: Option<DirectiveValue>,
131 pub allow_macro_redeclaration: bool,
132 #[serde(default)]
133 pub rule_prefix: Option<DirectiveValue>,
134 #[serde(default)]
135 pub rule_prefix_template: Option<DirectiveValue>,
136 #[serde(default)]
137 pub translations: Option<TranslationState>,
138 #[serde(default)]
139 pub optimization: OptimizationState,
140 #[serde(default)]
141 pub replacements: Vec<DirectiveValue>,
142 #[serde(default)]
143 pub suppressed_warnings: Vec<String>,
144 #[serde(default)]
145 pub directives: Vec<DirectiveRecord>,
146 #[serde(skip)]
150 pub(crate) source_file_initial_optimization: BTreeMap<u32, OptimizationState>,
151}
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct DirectiveValue {
156 pub value: String,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub span: Option<Span>,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
164pub struct TranslationState {
165 pub languages: Vec<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub span: Option<Span>,
168}
169
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct OptimizationState {
173 pub enabled: bool,
174 pub for_size: bool,
175 pub for_size_aggressive: bool,
176 pub strict: bool,
177}
178
179impl Default for OptimizationState {
180 fn default() -> Self {
181 Self {
182 enabled: true,
183 for_size: false,
184 for_size_aggressive: false,
185 strict: false,
186 }
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193pub struct DirectiveRecord {
194 pub name: String,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub value: Option<String>,
197 pub scope_col: u32,
198 #[serde(default)]
199 pub scope_depth: u32,
200 #[serde(default)]
201 pub state: PreprocessingSnapshot,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub span: Option<Span>,
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
207pub struct PreprocessingSnapshot {
208 pub allow_macro_redeclaration: bool,
209 pub optimization: OptimizationState,
210 #[serde(default)]
211 pub rule_prefix: Option<String>,
212 #[serde(default)]
213 pub rule_prefix_template: Option<String>,
214 #[serde(default)]
215 pub translations: Option<Vec<String>>,
216 #[serde(default)]
217 pub replacements: Vec<String>,
218}
219
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
222pub struct Settings {
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub span: Option<Span>,
225 #[serde(default)]
226 pub children: Vec<SettingsNode>,
227}
228
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
231#[serde(tag = "kind", rename_all = "camelCase")]
232pub enum SettingsNode {
233 Group {
234 name: String,
235 #[serde(default)]
236 children: Vec<SettingsNode>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 span: Option<Span>,
239 },
240 Number {
241 name: String,
242 value: f64,
243 #[serde(skip_serializing_if = "Option::is_none")]
244 span: Option<Span>,
245 },
246 Bool {
247 name: String,
248 value: bool,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 span: Option<Span>,
251 },
252 String {
253 name: String,
254 value: String,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 span: Option<Span>,
257 },
258 Raw {
260 name: String,
261 value: String,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 span: Option<Span>,
264 },
265 List {
266 name: String,
267 #[serde(default)]
268 elements: Vec<SettingsListElement>,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 span: Option<Span>,
271 },
272}
273
274impl SettingsNode {
275 pub fn span(&self) -> Option<&Span> {
277 match self {
278 SettingsNode::Group { span, .. }
279 | SettingsNode::Number { span, .. }
280 | SettingsNode::Bool { span, .. }
281 | SettingsNode::String { span, .. }
282 | SettingsNode::Raw { span, .. }
283 | SettingsNode::List { span, .. } => span.as_ref(),
284 }
285 }
286}
287
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
290pub struct SettingsListElement {
291 pub value: String,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub span: Option<Span>,
294}
295
296#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
298#[serde(tag = "kind", rename_all = "camelCase")]
299pub enum Declaration {
300 GlobalVariable {
301 name: String,
302 #[serde(default)]
303 index: Option<u32>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 span: Option<Span>,
306 #[serde(default, skip_serializing_if = "Option::is_none")]
308 name_span: Option<Span>,
309 #[serde(default)]
310 initializer: Option<Box<Expr>>,
311 },
312 PlayerVariable {
313 name: String,
314 #[serde(default)]
315 index: Option<u32>,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 span: Option<Span>,
318 #[serde(default, skip_serializing_if = "Option::is_none")]
320 name_span: Option<Span>,
321 #[serde(default)]
322 initializer: Option<Box<Expr>>,
323 },
324 Subroutine {
325 name: String,
326 #[serde(default)]
327 index: Option<u32>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 span: Option<Span>,
330 #[serde(default, skip_serializing_if = "Option::is_none")]
332 name_span: Option<Span>,
333 },
334 Constant {
335 name: String,
336 #[serde(skip_serializing_if = "Option::is_none")]
337 span: Option<Span>,
338 value: Box<Expr>,
339 },
340 Macro {
341 name: String,
342 #[serde(default)]
343 args: Vec<String>,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 span: Option<Span>,
346 #[serde(default)]
347 body: Vec<Stmt>,
348 },
349}
350
351#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353#[serde(untagged)]
354pub enum RuleEntry {
355 Rule(Rule),
357 SubroutineDef {
359 #[serde(rename = "kind")]
360 kind: String,
361 name: String,
362 #[serde(default, skip_serializing_if = "String::is_empty")]
363 source_name: String,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 span: Option<Span>,
366 #[serde(default, skip_serializing_if = "Option::is_none")]
368 name_span: Option<Span>,
369 #[serde(default)]
370 body: Vec<Stmt>,
371 #[serde(default)]
372 annotations: Vec<Annotation>,
373 },
374}
375
376#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
378pub struct Rule {
379 pub name: String,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub span: Option<Span>,
382 #[serde(default, skip_serializing_if = "Option::is_none")]
384 pub name_span: Option<Span>,
385 #[serde(default)]
386 pub disabled: bool,
387 #[serde(default)]
388 pub delimiter: bool,
389 #[serde(default, skip_serializing_if = "Option::is_none")]
390 pub new_page: Option<String>,
391 #[serde(default)]
392 pub annotations: Vec<Annotation>,
393 pub event: Event,
394 #[serde(default)]
395 pub conditions: Vec<Expr>,
396 #[serde(default)]
397 pub actions: Vec<Stmt>,
398}
399
400#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
402pub struct Annotation {
403 pub name: String,
404 #[serde(default)]
405 pub args: Vec<AnnotationArg>,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub span: Option<Span>,
408}
409
410#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
411pub struct AnnotationArg {
412 pub text: String,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub span: Option<Span>,
415}
416
417#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
419pub struct Event {
420 pub name: String,
421 #[serde(default)]
422 pub args: Vec<Expr>,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub span: Option<Span>,
425}
426
427#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
429#[serde(tag = "kind", rename_all = "camelCase")]
430pub enum Stmt {
431 Expr {
432 expr: Box<Expr>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 span: Option<Span>,
435 },
436 Assign {
437 target: Box<Expr>,
438 value: Box<Expr>,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 span: Option<Span>,
441 },
442 If {
443 branches: Vec<IfBranch>,
444 #[serde(default)]
445 r#else: Option<Vec<Stmt>>,
446 #[serde(skip_serializing_if = "Option::is_none")]
447 span: Option<Span>,
448 },
449 For {
450 variable: Box<Expr>,
451 iterable: Box<Expr>,
452 #[serde(default)]
453 body: Vec<Stmt>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 span: Option<Span>,
456 },
457 While {
458 condition: Box<Expr>,
459 #[serde(default)]
460 body: Vec<Stmt>,
461 #[serde(skip_serializing_if = "Option::is_none")]
462 span: Option<Span>,
463 },
464 DoWhile {
465 condition: Box<Expr>,
466 #[serde(default)]
467 body: Vec<Stmt>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 span: Option<Span>,
470 },
471 Switch {
472 value: Box<Expr>,
473 #[serde(default)]
474 arms: Vec<SwitchArm>,
475 #[serde(skip_serializing_if = "Option::is_none")]
476 span: Option<Span>,
477 },
478 Delete {
479 target: Box<Expr>,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 span: Option<Span>,
482 },
483 Break {
484 #[serde(skip_serializing_if = "Option::is_none")]
485 span: Option<Span>,
486 },
487 Return {
488 #[serde(skip_serializing_if = "Option::is_none")]
489 span: Option<Span>,
490 },
491 Continue {
492 #[serde(skip_serializing_if = "Option::is_none")]
493 span: Option<Span>,
494 },
495 Goto {
496 #[serde(default, skip_serializing_if = "Option::is_none")]
497 label: Option<String>,
498 #[serde(default, skip_serializing_if = "Option::is_none")]
499 offset: Option<Box<Expr>>,
500 #[serde(default)]
501 rule_start: bool,
502 #[serde(skip_serializing_if = "Option::is_none")]
503 span: Option<Span>,
504 },
505 Label {
506 name: String,
507 #[serde(skip_serializing_if = "Option::is_none")]
508 span: Option<Span>,
509 },
510 CallSubroutine {
511 name: String,
512 #[serde(skip_serializing_if = "Option::is_none")]
513 span: Option<Span>,
514 },
515 Pass {
516 #[serde(skip_serializing_if = "Option::is_none")]
517 span: Option<Span>,
518 },
519}
520
521#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
523pub struct IfBranch {
524 pub condition: Box<Expr>,
525 #[serde(default)]
526 pub body: Vec<Stmt>,
527}
528
529impl Stmt {
530 pub fn span(&self) -> Option<&Span> {
532 match self {
533 Stmt::Expr { span, .. }
534 | Stmt::Assign { span, .. }
535 | Stmt::If { span, .. }
536 | Stmt::For { span, .. }
537 | Stmt::While { span, .. }
538 | Stmt::DoWhile { span, .. }
539 | Stmt::Switch { span, .. }
540 | Stmt::Delete { span, .. }
541 | Stmt::Break { span }
542 | Stmt::Return { span }
543 | Stmt::Continue { span }
544 | Stmt::Goto { span, .. }
545 | Stmt::Label { span, .. }
546 | Stmt::CallSubroutine { span, .. }
547 | Stmt::Pass { span } => span.as_ref(),
548 }
549 }
550}
551
552#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
555#[serde(tag = "kind", rename_all = "camelCase")]
556pub enum SwitchArm {
557 Case {
558 value: Box<Expr>,
559 #[serde(default)]
560 body: Vec<Stmt>,
561 #[serde(skip_serializing_if = "Option::is_none")]
562 span: Option<Span>,
563 },
564 Default {
565 #[serde(default)]
566 body: Vec<Stmt>,
567 #[serde(skip_serializing_if = "Option::is_none")]
568 span: Option<Span>,
569 },
570}
571
572#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
574#[serde(tag = "kind", rename_all = "camelCase")]
575pub enum Expr {
576 Number {
577 value: f64,
578 text: String,
579 #[serde(skip_serializing_if = "Option::is_none")]
580 span: Option<Span>,
581 },
582 String {
583 value: String,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 span: Option<Span>,
586 },
587 Bool {
588 value: bool,
589 #[serde(skip_serializing_if = "Option::is_none")]
590 span: Option<Span>,
591 },
592 Null {
593 #[serde(skip_serializing_if = "Option::is_none")]
594 span: Option<Span>,
595 },
596 Array {
597 #[serde(default)]
598 elements: Vec<Expr>,
599 #[serde(skip_serializing_if = "Option::is_none")]
600 span: Option<Span>,
601 },
602 Dict {
603 #[serde(default)]
604 entries: Vec<DictEntry>,
605 #[serde(skip_serializing_if = "Option::is_none")]
606 span: Option<Span>,
607 },
608 Comprehension {
609 element: Box<Expr>,
610 variable: String,
611 #[serde(skip_serializing_if = "Option::is_none")]
612 variable_span: Option<Span>,
613 #[serde(default, skip_serializing_if = "Option::is_none")]
614 index: Option<String>,
615 #[serde(default, skip_serializing_if = "Option::is_none")]
616 index_span: Option<Span>,
617 iterable: Box<Expr>,
618 #[serde(default, skip_serializing_if = "Option::is_none")]
619 condition: Option<Box<Expr>>,
620 #[serde(skip_serializing_if = "Option::is_none")]
621 span: Option<Span>,
622 },
623 Lambda {
624 #[serde(default)]
625 params: Vec<String>,
626 #[serde(default)]
627 param_spans: Vec<Option<Span>>,
628 body: Box<Expr>,
629 #[serde(skip_serializing_if = "Option::is_none")]
630 span: Option<Span>,
631 },
632 StringModifier {
633 modifier: String,
634 value: String,
635 #[serde(skip_serializing_if = "Option::is_none")]
636 span: Option<Span>,
637 },
638 Local {
639 name: String,
640 #[serde(skip_serializing_if = "Option::is_none")]
641 span: Option<Span>,
642 },
643 Vector {
644 x: Box<Expr>,
645 y: Box<Expr>,
646 z: Box<Expr>,
647 #[serde(skip_serializing_if = "Option::is_none")]
648 span: Option<Span>,
649 },
650 Enum {
651 #[serde(rename = "type")]
652 value_type: String,
653 value: String,
654 #[serde(skip_serializing_if = "Option::is_none")]
655 span: Option<Span>,
656 },
657 GlobalVar {
658 name: String,
659 #[serde(skip_serializing_if = "Option::is_none")]
660 span: Option<Span>,
661 },
662 PlayerVar {
663 player: Box<Expr>,
664 name: String,
665 #[serde(default, skip_serializing_if = "Option::is_none")]
668 member_span: Option<Span>,
669 #[serde(skip_serializing_if = "Option::is_none")]
670 span: Option<Span>,
671 },
672 HostPlayer {
673 #[serde(skip_serializing_if = "Option::is_none")]
674 span: Option<Span>,
675 },
676 Member {
680 receiver: Box<Expr>,
681 member: String,
682 #[serde(default, skip_serializing_if = "Option::is_none")]
683 member_span: Option<Span>,
684 #[serde(skip_serializing_if = "Option::is_none")]
685 span: Option<Span>,
686 },
687 EventPlayer {
688 #[serde(skip_serializing_if = "Option::is_none")]
689 span: Option<Span>,
690 },
691 Constant {
692 name: String,
693 #[serde(skip_serializing_if = "Option::is_none")]
694 span: Option<Span>,
695 },
696 Call {
697 name: String,
698 #[serde(default)]
699 args: Vec<Expr>,
700 #[serde(default, skip_serializing_if = "Option::is_none")]
701 debug_source: Option<String>,
702 #[serde(skip_serializing_if = "Option::is_none")]
703 span: Option<Span>,
704 },
705 ReceiverCall {
706 receiver: Box<Expr>,
707 name: String,
708 #[serde(default)]
709 args: Vec<Expr>,
710 #[serde(skip_serializing_if = "Option::is_none")]
711 span: Option<Span>,
712 },
713 MacroCall {
714 name: String,
715 #[serde(default)]
716 args: Vec<Expr>,
717 #[serde(skip_serializing_if = "Option::is_none")]
718 span: Option<Span>,
719 },
720 MacroParam {
721 name: String,
722 #[serde(skip_serializing_if = "Option::is_none")]
723 span: Option<Span>,
724 },
725 Type {
728 name: String,
729 #[serde(default)]
730 args: Vec<Expr>,
731 #[serde(skip_serializing_if = "Option::is_none")]
732 span: Option<Span>,
733 },
734 Binary {
735 op: String,
736 left: Box<Expr>,
737 right: Box<Expr>,
738 #[serde(skip_serializing_if = "Option::is_none")]
739 span: Option<Span>,
740 },
741 Conditional {
742 then_value: Box<Expr>,
743 condition: Box<Expr>,
744 else_value: Box<Expr>,
745 #[serde(skip_serializing_if = "Option::is_none")]
746 span: Option<Span>,
747 },
748 Unary {
749 op: String,
750 operand: Box<Expr>,
751 #[serde(skip_serializing_if = "Option::is_none")]
752 span: Option<Span>,
753 },
754 Index {
755 array: Box<Expr>,
756 index: Box<Expr>,
757 #[serde(skip_serializing_if = "Option::is_none")]
758 span: Option<Span>,
759 },
760 Format {
761 text: String,
762 #[serde(default)]
763 args: Vec<Expr>,
764 #[serde(skip_serializing_if = "Option::is_none")]
765 span: Option<Span>,
766 },
767}
768
769#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
771pub struct DictEntry {
772 pub key: Box<Expr>,
773 pub value: Box<Expr>,
774 #[serde(skip_serializing_if = "Option::is_none")]
775 pub span: Option<Span>,
776}
777
778impl Expr {
779 pub fn span(&self) -> Option<&Span> {
781 match self {
782 Expr::Number { span, .. }
783 | Expr::String { span, .. }
784 | Expr::Bool { span, .. }
785 | Expr::Null { span }
786 | Expr::Array { span, .. }
787 | Expr::Dict { span, .. }
788 | Expr::Comprehension { span, .. }
789 | Expr::Lambda { span, .. }
790 | Expr::StringModifier { span, .. }
791 | Expr::Local { span, .. }
792 | Expr::Vector { span, .. }
793 | Expr::Enum { span, .. }
794 | Expr::GlobalVar { span, .. }
795 | Expr::PlayerVar { span, .. }
796 | Expr::HostPlayer { span }
797 | Expr::Member { span, .. }
798 | Expr::EventPlayer { span }
799 | Expr::Constant { span, .. }
800 | Expr::Call { span, .. }
801 | Expr::ReceiverCall { span, .. }
802 | Expr::MacroCall { span, .. }
803 | Expr::MacroParam { span, .. }
804 | Expr::Type { span, .. }
805 | Expr::Binary { span, .. }
806 | Expr::Conditional { span, .. }
807 | Expr::Unary { span, .. }
808 | Expr::Index { span, .. }
809 | Expr::Format { span, .. } => span.as_ref(),
810 }
811 }
812
813 pub fn kind_name(&self) -> &'static str {
815 match self {
816 Expr::Number { .. } => "number",
817 Expr::String { .. } => "string",
818 Expr::Bool { .. } => "bool",
819 Expr::Null { .. } => "null",
820 Expr::Array { .. } => "array",
821 Expr::Dict { .. } => "dict",
822 Expr::Comprehension { .. } => "comprehension",
823 Expr::Lambda { .. } => "lambda",
824 Expr::StringModifier { .. } => "stringModifier",
825 Expr::Local { .. } => "local",
826 Expr::Vector { .. } => "vector",
827 Expr::Enum { .. } => "enum",
828 Expr::GlobalVar { .. } => "globalVar",
829 Expr::PlayerVar { .. } => "playerVar",
830 Expr::HostPlayer { .. } => "hostPlayer",
831 Expr::Member { .. } => "member",
832 Expr::EventPlayer { .. } => "eventPlayer",
833 Expr::Constant { .. } => "constant",
834 Expr::Call { .. } => "call",
835 Expr::ReceiverCall { .. } => "receiverCall",
836 Expr::MacroCall { .. } => "macroCall",
837 Expr::MacroParam { .. } => "macroParam",
838 Expr::Type { .. } => "type",
839 Expr::Binary { .. } => "binary",
840 Expr::Conditional { .. } => "conditional",
841 Expr::Unary { .. } => "unary",
842 Expr::Index { .. } => "index",
843 Expr::Format { .. } => "format",
844 }
845 }
846}