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(skip_serializing_if = "Option::is_none")]
81 pub span: Option<Span>,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86pub struct Span {
87 pub file: u32,
88 pub start: Position,
89 pub end: Position,
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
94pub struct Position {
95 pub line: u32,
96 pub col: u32,
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub struct Program {
102 pub protocol: Protocol,
103 pub generator: Generator,
104 pub files: Vec<SourceFile>,
105 #[serde(default)]
106 pub defines: Vec<Define>,
107 #[serde(default)]
108 pub declarations: Vec<Declaration>,
109 #[serde(default)]
110 pub rules: Vec<RuleEntry>,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub settings: Option<Settings>,
114 #[serde(default)]
117 pub preprocessing: PreprocessingState,
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
122pub struct PreprocessingState {
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub main_file: Option<DirectiveValue>,
125 pub allow_macro_redeclaration: bool,
126 #[serde(default)]
127 pub rule_prefix: Option<DirectiveValue>,
128 #[serde(default)]
129 pub rule_prefix_template: Option<DirectiveValue>,
130 #[serde(default)]
131 pub translations: Option<TranslationState>,
132 #[serde(default)]
133 pub optimization: OptimizationState,
134 #[serde(default)]
135 pub replacements: Vec<DirectiveValue>,
136 #[serde(default)]
137 pub suppressed_warnings: Vec<String>,
138 #[serde(default)]
139 pub directives: Vec<DirectiveRecord>,
140}
141
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct DirectiveValue {
145 pub value: String,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub span: Option<Span>,
148}
149
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
153pub struct TranslationState {
154 pub languages: Vec<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub span: Option<Span>,
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub struct OptimizationState {
163 pub enabled: bool,
164 pub for_size: bool,
165 pub for_size_aggressive: bool,
166 pub strict: bool,
167}
168
169impl Default for OptimizationState {
170 fn default() -> Self {
171 Self {
172 enabled: true,
173 for_size: false,
174 for_size_aggressive: false,
175 strict: false,
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct DirectiveRecord {
184 pub name: String,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub value: Option<String>,
187 pub scope_col: u32,
188 #[serde(default)]
189 pub scope_depth: u32,
190 #[serde(default)]
191 pub state: PreprocessingSnapshot,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub span: Option<Span>,
194}
195
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
197pub struct PreprocessingSnapshot {
198 pub allow_macro_redeclaration: bool,
199 pub optimization: OptimizationState,
200 #[serde(default)]
201 pub rule_prefix: Option<String>,
202 #[serde(default)]
203 pub rule_prefix_template: Option<String>,
204 #[serde(default)]
205 pub translations: Option<Vec<String>>,
206 #[serde(default)]
207 pub replacements: Vec<String>,
208}
209
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
212pub struct Settings {
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub span: Option<Span>,
215 #[serde(default)]
216 pub children: Vec<SettingsNode>,
217}
218
219#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221#[serde(tag = "kind", rename_all = "camelCase")]
222pub enum SettingsNode {
223 Group {
224 name: String,
225 #[serde(default)]
226 children: Vec<SettingsNode>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 span: Option<Span>,
229 },
230 Number {
231 name: String,
232 value: f64,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 span: Option<Span>,
235 },
236 Bool {
237 name: String,
238 value: bool,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 span: Option<Span>,
241 },
242 String {
243 name: String,
244 value: String,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 span: Option<Span>,
247 },
248 List {
249 name: String,
250 #[serde(default)]
251 elements: Vec<SettingsListElement>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 span: Option<Span>,
254 },
255}
256
257impl SettingsNode {
258 pub fn span(&self) -> Option<&Span> {
260 match self {
261 SettingsNode::Group { span, .. }
262 | SettingsNode::Number { span, .. }
263 | SettingsNode::Bool { span, .. }
264 | SettingsNode::String { span, .. }
265 | SettingsNode::List { span, .. } => span.as_ref(),
266 }
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
272pub struct SettingsListElement {
273 pub value: String,
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub span: Option<Span>,
276}
277
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
280#[serde(tag = "kind", rename_all = "camelCase")]
281pub enum Declaration {
282 GlobalVariable {
283 name: String,
284 #[serde(default)]
285 index: Option<u32>,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 span: Option<Span>,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
290 name_span: Option<Span>,
291 #[serde(default)]
292 initializer: Option<Box<Expr>>,
293 },
294 PlayerVariable {
295 name: String,
296 #[serde(default)]
297 index: Option<u32>,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 span: Option<Span>,
300 #[serde(default, skip_serializing_if = "Option::is_none")]
302 name_span: Option<Span>,
303 #[serde(default)]
304 initializer: Option<Box<Expr>>,
305 },
306 Subroutine {
307 name: String,
308 #[serde(default)]
309 index: Option<u32>,
310 #[serde(skip_serializing_if = "Option::is_none")]
311 span: Option<Span>,
312 #[serde(default, skip_serializing_if = "Option::is_none")]
314 name_span: Option<Span>,
315 },
316 Constant {
317 name: String,
318 #[serde(skip_serializing_if = "Option::is_none")]
319 span: Option<Span>,
320 value: Box<Expr>,
321 },
322 Macro {
323 name: String,
324 #[serde(default)]
325 args: Vec<String>,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 span: Option<Span>,
328 #[serde(default)]
329 body: Vec<Stmt>,
330 },
331}
332
333#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
335#[serde(untagged)]
336pub enum RuleEntry {
337 Rule(Rule),
339 SubroutineDef {
341 #[serde(rename = "kind")]
342 kind: String,
343 name: String,
344 #[serde(default, skip_serializing_if = "String::is_empty")]
345 source_name: String,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 span: Option<Span>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
350 name_span: Option<Span>,
351 #[serde(default)]
352 body: Vec<Stmt>,
353 #[serde(default)]
354 annotations: Vec<Annotation>,
355 },
356}
357
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
360pub struct Rule {
361 pub name: String,
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub span: Option<Span>,
364 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub name_span: Option<Span>,
367 #[serde(default)]
368 pub disabled: bool,
369 #[serde(default)]
370 pub delimiter: bool,
371 #[serde(default, skip_serializing_if = "Option::is_none")]
372 pub new_page: Option<String>,
373 #[serde(default)]
374 pub annotations: Vec<Annotation>,
375 pub event: Event,
376 #[serde(default)]
377 pub conditions: Vec<Expr>,
378 #[serde(default)]
379 pub actions: Vec<Stmt>,
380}
381
382#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
384pub struct Annotation {
385 pub name: String,
386 #[serde(default)]
387 pub args: Vec<AnnotationArg>,
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub span: Option<Span>,
390}
391
392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
393pub struct AnnotationArg {
394 pub text: String,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub span: Option<Span>,
397}
398
399#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
401pub struct Event {
402 pub name: String,
403 #[serde(default)]
404 pub args: Vec<Expr>,
405 #[serde(skip_serializing_if = "Option::is_none")]
406 pub span: Option<Span>,
407}
408
409#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
411#[serde(tag = "kind", rename_all = "camelCase")]
412pub enum Stmt {
413 Expr {
414 expr: Box<Expr>,
415 #[serde(skip_serializing_if = "Option::is_none")]
416 span: Option<Span>,
417 },
418 Assign {
419 target: Box<Expr>,
420 value: Box<Expr>,
421 #[serde(skip_serializing_if = "Option::is_none")]
422 span: Option<Span>,
423 },
424 If {
425 branches: Vec<IfBranch>,
426 #[serde(default)]
427 r#else: Option<Vec<Stmt>>,
428 #[serde(skip_serializing_if = "Option::is_none")]
429 span: Option<Span>,
430 },
431 For {
432 variable: Box<Expr>,
433 iterable: Box<Expr>,
434 #[serde(default)]
435 body: Vec<Stmt>,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 span: Option<Span>,
438 },
439 While {
440 condition: Box<Expr>,
441 #[serde(default)]
442 body: Vec<Stmt>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 span: Option<Span>,
445 },
446 DoWhile {
447 condition: Box<Expr>,
448 #[serde(default)]
449 body: Vec<Stmt>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 span: Option<Span>,
452 },
453 Switch {
454 value: Box<Expr>,
455 #[serde(default)]
456 arms: Vec<SwitchArm>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 span: Option<Span>,
459 },
460 Break {
461 #[serde(skip_serializing_if = "Option::is_none")]
462 span: Option<Span>,
463 },
464 CallSubroutine {
465 name: String,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 span: Option<Span>,
468 },
469 Pass {
470 #[serde(skip_serializing_if = "Option::is_none")]
471 span: Option<Span>,
472 },
473}
474
475#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
477pub struct IfBranch {
478 pub condition: Box<Expr>,
479 #[serde(default)]
480 pub body: Vec<Stmt>,
481}
482
483impl Stmt {
484 pub fn span(&self) -> Option<&Span> {
486 match self {
487 Stmt::Expr { span, .. }
488 | Stmt::Assign { span, .. }
489 | Stmt::If { span, .. }
490 | Stmt::For { span, .. }
491 | Stmt::While { span, .. }
492 | Stmt::DoWhile { span, .. }
493 | Stmt::Switch { span, .. }
494 | Stmt::Break { span }
495 | Stmt::CallSubroutine { span, .. }
496 | Stmt::Pass { span } => span.as_ref(),
497 }
498 }
499}
500
501#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
504#[serde(tag = "kind", rename_all = "camelCase")]
505pub enum SwitchArm {
506 Case {
507 value: Box<Expr>,
508 #[serde(default)]
509 body: Vec<Stmt>,
510 #[serde(skip_serializing_if = "Option::is_none")]
511 span: Option<Span>,
512 },
513 Default {
514 #[serde(default)]
515 body: Vec<Stmt>,
516 #[serde(skip_serializing_if = "Option::is_none")]
517 span: Option<Span>,
518 },
519}
520
521#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
523#[serde(tag = "kind", rename_all = "camelCase")]
524pub enum Expr {
525 Number {
526 value: f64,
527 text: String,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 span: Option<Span>,
530 },
531 String {
532 value: String,
533 #[serde(skip_serializing_if = "Option::is_none")]
534 span: Option<Span>,
535 },
536 Bool {
537 value: bool,
538 #[serde(skip_serializing_if = "Option::is_none")]
539 span: Option<Span>,
540 },
541 Null {
542 #[serde(skip_serializing_if = "Option::is_none")]
543 span: Option<Span>,
544 },
545 Array {
546 #[serde(default)]
547 elements: Vec<Expr>,
548 #[serde(skip_serializing_if = "Option::is_none")]
549 span: Option<Span>,
550 },
551 Dict {
552 #[serde(default)]
553 entries: Vec<DictEntry>,
554 #[serde(skip_serializing_if = "Option::is_none")]
555 span: Option<Span>,
556 },
557 Comprehension {
558 element: Box<Expr>,
559 variable: String,
560 #[serde(skip_serializing_if = "Option::is_none")]
561 variable_span: Option<Span>,
562 #[serde(default, skip_serializing_if = "Option::is_none")]
563 index: Option<String>,
564 #[serde(default, skip_serializing_if = "Option::is_none")]
565 index_span: Option<Span>,
566 iterable: Box<Expr>,
567 #[serde(default, skip_serializing_if = "Option::is_none")]
568 condition: Option<Box<Expr>>,
569 #[serde(skip_serializing_if = "Option::is_none")]
570 span: Option<Span>,
571 },
572 Lambda {
573 #[serde(default)]
574 params: Vec<String>,
575 #[serde(default)]
576 param_spans: Vec<Option<Span>>,
577 body: Box<Expr>,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 span: Option<Span>,
580 },
581 StringModifier {
582 modifier: String,
583 value: String,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 span: Option<Span>,
586 },
587 Local {
588 name: String,
589 #[serde(skip_serializing_if = "Option::is_none")]
590 span: Option<Span>,
591 },
592 Vector {
593 x: Box<Expr>,
594 y: Box<Expr>,
595 z: Box<Expr>,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 span: Option<Span>,
598 },
599 Enum {
600 #[serde(rename = "type")]
601 value_type: String,
602 value: String,
603 #[serde(skip_serializing_if = "Option::is_none")]
604 span: Option<Span>,
605 },
606 GlobalVar {
607 name: String,
608 #[serde(skip_serializing_if = "Option::is_none")]
609 span: Option<Span>,
610 },
611 PlayerVar {
612 player: Box<Expr>,
613 name: String,
614 #[serde(default, skip_serializing_if = "Option::is_none")]
617 member_span: Option<Span>,
618 #[serde(skip_serializing_if = "Option::is_none")]
619 span: Option<Span>,
620 },
621 HostPlayer {
622 #[serde(skip_serializing_if = "Option::is_none")]
623 span: Option<Span>,
624 },
625 Member {
629 receiver: Box<Expr>,
630 member: String,
631 #[serde(default, skip_serializing_if = "Option::is_none")]
632 member_span: Option<Span>,
633 #[serde(skip_serializing_if = "Option::is_none")]
634 span: Option<Span>,
635 },
636 EventPlayer {
637 #[serde(skip_serializing_if = "Option::is_none")]
638 span: Option<Span>,
639 },
640 Constant {
641 name: String,
642 #[serde(skip_serializing_if = "Option::is_none")]
643 span: Option<Span>,
644 },
645 Call {
646 name: String,
647 #[serde(default)]
648 args: Vec<Expr>,
649 #[serde(skip_serializing_if = "Option::is_none")]
650 span: Option<Span>,
651 },
652 ReceiverCall {
653 receiver: Box<Expr>,
654 name: String,
655 #[serde(default)]
656 args: Vec<Expr>,
657 #[serde(skip_serializing_if = "Option::is_none")]
658 span: Option<Span>,
659 },
660 MacroCall {
661 name: String,
662 #[serde(default)]
663 args: Vec<Expr>,
664 #[serde(skip_serializing_if = "Option::is_none")]
665 span: Option<Span>,
666 },
667 MacroParam {
668 name: String,
669 #[serde(skip_serializing_if = "Option::is_none")]
670 span: Option<Span>,
671 },
672 Type {
675 name: String,
676 #[serde(default)]
677 args: Vec<Expr>,
678 #[serde(skip_serializing_if = "Option::is_none")]
679 span: Option<Span>,
680 },
681 Binary {
682 op: String,
683 left: Box<Expr>,
684 right: Box<Expr>,
685 #[serde(skip_serializing_if = "Option::is_none")]
686 span: Option<Span>,
687 },
688 Conditional {
689 then_value: Box<Expr>,
690 condition: Box<Expr>,
691 else_value: Box<Expr>,
692 #[serde(skip_serializing_if = "Option::is_none")]
693 span: Option<Span>,
694 },
695 Unary {
696 op: String,
697 operand: Box<Expr>,
698 #[serde(skip_serializing_if = "Option::is_none")]
699 span: Option<Span>,
700 },
701 Index {
702 array: Box<Expr>,
703 index: Box<Expr>,
704 #[serde(skip_serializing_if = "Option::is_none")]
705 span: Option<Span>,
706 },
707 Format {
708 text: String,
709 #[serde(default)]
710 args: Vec<Expr>,
711 #[serde(skip_serializing_if = "Option::is_none")]
712 span: Option<Span>,
713 },
714}
715
716#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
718pub struct DictEntry {
719 pub key: Box<Expr>,
720 pub value: Box<Expr>,
721 #[serde(skip_serializing_if = "Option::is_none")]
722 pub span: Option<Span>,
723}
724
725impl Expr {
726 pub fn span(&self) -> Option<&Span> {
728 match self {
729 Expr::Number { span, .. }
730 | Expr::String { span, .. }
731 | Expr::Bool { span, .. }
732 | Expr::Null { span }
733 | Expr::Array { span, .. }
734 | Expr::Dict { span, .. }
735 | Expr::Comprehension { span, .. }
736 | Expr::Lambda { span, .. }
737 | Expr::StringModifier { span, .. }
738 | Expr::Local { span, .. }
739 | Expr::Vector { span, .. }
740 | Expr::Enum { span, .. }
741 | Expr::GlobalVar { span, .. }
742 | Expr::PlayerVar { span, .. }
743 | Expr::HostPlayer { span }
744 | Expr::Member { span, .. }
745 | Expr::EventPlayer { span }
746 | Expr::Constant { span, .. }
747 | Expr::Call { span, .. }
748 | Expr::ReceiverCall { span, .. }
749 | Expr::MacroCall { span, .. }
750 | Expr::MacroParam { span, .. }
751 | Expr::Type { span, .. }
752 | Expr::Binary { span, .. }
753 | Expr::Conditional { span, .. }
754 | Expr::Unary { span, .. }
755 | Expr::Index { span, .. }
756 | Expr::Format { span, .. } => span.as_ref(),
757 }
758 }
759
760 pub fn kind_name(&self) -> &'static str {
762 match self {
763 Expr::Number { .. } => "number",
764 Expr::String { .. } => "string",
765 Expr::Bool { .. } => "bool",
766 Expr::Null { .. } => "null",
767 Expr::Array { .. } => "array",
768 Expr::Dict { .. } => "dict",
769 Expr::Comprehension { .. } => "comprehension",
770 Expr::Lambda { .. } => "lambda",
771 Expr::StringModifier { .. } => "stringModifier",
772 Expr::Local { .. } => "local",
773 Expr::Vector { .. } => "vector",
774 Expr::Enum { .. } => "enum",
775 Expr::GlobalVar { .. } => "globalVar",
776 Expr::PlayerVar { .. } => "playerVar",
777 Expr::HostPlayer { .. } => "hostPlayer",
778 Expr::Member { .. } => "member",
779 Expr::EventPlayer { .. } => "eventPlayer",
780 Expr::Constant { .. } => "constant",
781 Expr::Call { .. } => "call",
782 Expr::ReceiverCall { .. } => "receiverCall",
783 Expr::MacroCall { .. } => "macroCall",
784 Expr::MacroParam { .. } => "macroParam",
785 Expr::Type { .. } => "type",
786 Expr::Binary { .. } => "binary",
787 Expr::Conditional { .. } => "conditional",
788 Expr::Unary { .. } => "unary",
789 Expr::Index { .. } => "index",
790 Expr::Format { .. } => "format",
791 }
792 }
793}