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