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