-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmod.rs
More file actions
1379 lines (1243 loc) · 48.4 KB
/
mod.rs
File metadata and controls
1379 lines (1243 loc) · 48.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use std::fmt;
use crate::raw;
use crate::resolve::{DefId, LoopId, NodeId, RegId};
use crate::ident::{Ident, ResIdent};
use crate::pos::{Sp, Span};
use crate::game::LanguageKey;
use crate::value;
use crate::diff_switch_utils as ds_util;
pub use meta::Meta;
pub mod meta;
pub mod pseudo;
// =============================================================================
/// Type used in the AST for the span of a single token with no useful data.
///
/// This can be used for things like keywords. We use [`Sp`]`<()>` instead of [`Span`]
/// because the latter would have an impact on equality tests.
pub type TokenSpan = Sp<()>;
/// Represents a complete script file.
#[derive(Debug, Clone, PartialEq)]
pub struct ScriptFile {
pub mapfiles: Vec<Sp<LitString>>,
pub image_sources: Vec<Sp<LitString>>,
pub items: Vec<Sp<Item>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Item {
Func(ItemFunc),
Script {
keyword: TokenSpan,
number: Option<Sp<raw::LangInt>>,
ident: Sp<Ident>, // not `ResIdent` because it doesn't define something in all languages
code: Block,
},
Meta {
keyword: Sp<MetaKeyword>,
fields: Sp<meta::Fields>,
},
ConstVar {
ty_keyword: Sp<TypeKeyword>,
vars: Vec<Sp<(Sp<Var>, Sp<Expr>)>>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct ItemFunc {
pub qualifier: Option<Sp<FuncQualifier>>,
pub ty_keyword: Sp<TypeKeyword>,
pub ident: Sp<ResIdent>,
pub params: Vec<Sp<FuncParam>>,
/// `Some` for definitions, `None` for declarations.
pub code: Option<Block>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FuncParam {
pub qualifier: Option<Sp<ParamQualifier>>,
pub ty_keyword: Sp<TypeKeyword>,
pub ident: Option<Sp<ResIdent>>,
}
string_enum! {
// 'const' parameters will exist *some day*, this is here now to reduce churn.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ParamQualifier {}
}
impl Item {
pub fn descr(&self) -> &'static str { match self {
Item::Func(ItemFunc { qualifier: Some(sp_pat![token![const]]), .. }) => "const function definition",
Item::Func(ItemFunc { qualifier: Some(sp_pat![token![inline]]), .. }) => "inline function definition",
Item::Func(ItemFunc { qualifier: None, .. }) => "exported function definition",
Item::Script { .. } => "script",
Item::Meta { .. } => "meta",
Item::ConstVar { .. } => "const definition",
}}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FuncQualifier {
/// `entry` block for a texture in ANM.
#[strum(serialize = "const")] Const,
#[strum(serialize = "inline")] Inline,
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MetaKeyword {
/// `entry` block for a texture in ANM.
#[strum(serialize = "entry")] Entry,
#[strum(serialize = "meta")] Meta,
}
}
// =============================================================================
#[derive(Debug, Clone, PartialEq)]
pub struct Stmt {
pub node_id: Option<NodeId>,
pub diff_label: Option<Sp<DiffLabel>>,
pub kind: StmtKind,
}
/// Difficulty label. `{"ENH"}:`
#[derive(Debug, Clone, PartialEq)]
pub struct DiffLabel {
/// Cached bitflag form of the difficulty mask. This may be `None` before
/// [`crate::passes::resolution::compute_diff_label_masks`] runs.
pub mask: Option<crate::bitset::BitSet32>,
pub string: Sp<LitString>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StmtKind {
/// Some items are allowed to appear as statements. (`const`s and functions)
Item(Box<Sp<Item>>),
/// Unconditional goto. `goto label @ time;`
Jump(StmtJumpKind),
/// Conditional goto. `if (a == b) goto label @ time;`
CondJump {
keyword: Sp<CondKeyword>,
cond: Sp<Expr>,
jump: StmtJumpKind,
},
/// `return;` or `return expr;`
Return {
keyword: TokenSpan,
value: Option<Sp<Expr>>,
},
/// A chain of conditional blocks. `if (...) { ... } else if (...) { ... } else { ... }`
CondChain(StmtCondChain),
/// Unconditional loop. `loop { ... }`
Loop {
loop_id: Option<LoopId>,
keyword: TokenSpan,
block: Block,
},
/// While loop. `while (...) { ... }` or `do { ... } while (...);`
While {
loop_id: Option<LoopId>,
while_keyword: TokenSpan,
do_keyword: Option<TokenSpan>,
cond: Sp<Expr>,
block: Block,
},
/// Times loop. `times(n) { ... }`
Times {
loop_id: Option<LoopId>,
keyword: TokenSpan,
clobber: Option<Sp<Var>>,
count: Sp<Expr>,
block: Block,
},
/// Expression followed by a semicolon.
///
/// This is primarily for void-type "expressions" like raw instruction
/// calls (which are grammatically indistinguishable from value-returning
/// function calls).
Expr(Sp<Expr>),
/// Free-standing block.
Block(Block),
/// `a = expr;` or `a += expr;`
Assignment {
var: Sp<Var>,
op: Sp<AssignOpKind>,
value: Sp<Expr>,
},
/// Local variable declaration `int a = 20;`. (`const` vars fall under [`Self::Item`] instead)
Declaration {
ty_keyword: Sp<TypeKeyword>,
vars: Vec<Sp<(Sp<Var>, Option<Sp<Expr>>)>>,
},
/// An explicit subroutine call. (ECL only)
///
/// Will always have at least one of either the `@` or `async`.
/// (otherwise, it will fall under `Expr` instead)
CallSub {
at_symbol: bool,
async_: Option<CallAsyncKind>,
func: Sp<Ident>,
args: Vec<Sp<Expr>>,
},
/// An interrupt label: `interrupt[2]:`.
InterruptLabel(Sp<Expr>),
/// An absolute time label: `30:` or `-30:`.
AbsTimeLabel(Sp<raw::LangInt>),
/// A relative time label: `+30:`. This value cannot be negative.
RelTimeLabel {
delta: Sp<Expr>,
// This is used during decompilation ONLY, in order to allow the code formatter to
// write `//` comments with the total time on these labels without having to perform
// its own semantic analysis.
_absolute_time_comment: Option<raw::LangInt>,
},
/// A label `label:` that can be jumped to.
Label(Sp<Ident>),
/// A virtual statement that marks the end of a variable's lexical scope.
///
/// Blocks are eliminated during early compilation passes, leaving behind these as the only
/// remaining way of identifying the end of a variable's scope. They are used during lowering
/// to determine when to release resources (like registers) held by locals.
ScopeEnd(DefId),
/// A virtual statement that completely disappears during compilation.
///
/// This is a trivial statement that doesn't even compile to a `nop();`.
/// It is inserted at the beginning and end of code blocks in order to help implement some
/// of the following things:
///
/// * A time label at the end of a block.
/// * A time label at the beginning of a loop's body.
///
/// Note that these may also appear in the middle of a block in the AST if a transformation
/// pass has e.g. inlined the contents of one block into another.
NoInstruction,
}
impl StmtKind {
pub fn descr(&self) -> &'static str { match self {
StmtKind::Item(item) => item.descr(),
StmtKind::Jump(StmtJumpKind::Goto { .. }) => "goto",
StmtKind::Jump(StmtJumpKind::BreakContinue { .. }) => "loop jump",
StmtKind::CondJump { jump: StmtJumpKind::Goto { .. }, .. } => "conditional goto",
StmtKind::CondJump { jump: StmtJumpKind::BreakContinue { .. }, .. } => "conditional loop jump",
StmtKind::Return { .. } => "return statement",
StmtKind::CondChain { .. } => "conditional chain",
StmtKind::Loop { .. } => "loop",
StmtKind::While { .. } => "while(..)",
StmtKind::Times { .. } => "times(..)",
StmtKind::Expr { .. } => "expression statement",
StmtKind::Block { .. } => "block statement",
StmtKind::Assignment { .. } => "assignment",
StmtKind::Declaration { .. } => "var declaration",
StmtKind::CallSub { .. } => "sub call",
StmtKind::InterruptLabel { .. } => "interrupt label",
StmtKind::AbsTimeLabel { .. } => "time label",
StmtKind::RelTimeLabel { .. } => "time label",
StmtKind::Label { .. } => "label",
StmtKind::ScopeEnd { .. } => "<ScopeEnd>",
StmtKind::NoInstruction { .. } => "<NoInstruction>",
}}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StmtJumpKind {
/// The body of a `goto` statement, without the `;`.
Goto(StmtGoto) ,
/// A `break` or `continue`.
BreakContinue {
keyword: Sp<BreakContinueKeyword>,
/// This is used to prevent or detect bugs where a `break` or `continue` could somehow
/// end up referring to the wrong loop after a code transformation.
loop_id: Option<LoopId>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct StmtGoto {
pub destination: Sp<Ident>,
pub time: Option<Sp<raw::LangInt>>,
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BreakContinueKeyword {
#[strum(serialize = "break")] Break,
// `continue` could be implemented in a variety of ways and there could be confusing
// inconsistencies between loops and while loops. Hold off on it for now...
// #[strum(serialize = "continue")] Continue,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct StmtCondChain {
pub cond_blocks: Vec<CondBlock>,
pub else_block: Option<Block>,
}
impl StmtCondChain {
pub fn last_block(&self) -> &Block {
self.else_block.as_ref()
.unwrap_or_else(|| &self.cond_blocks.last().unwrap().block)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CondBlock {
pub keyword: Sp<CondKeyword>,
pub cond: Sp<Expr>,
pub block: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CallAsyncKind {
CallAsync,
CallAsyncId(Box<Sp<Expr>>),
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CondKeyword {
#[strum(serialize = "if")] If,
#[strum(serialize = "unless")] Unless,
}
}
impl CondKeyword {
pub fn negate(self) -> Self { match self {
CondKeyword::If => CondKeyword::Unless,
CondKeyword::Unless => CondKeyword::If,
}}
}
// TODO: Parse
pub type DifficultyLabel = String;
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(strum::EnumIter)]
pub enum AssignOpKind {
#[strum(serialize = "=")] Assign,
#[strum(serialize = "+=")] Add,
#[strum(serialize = "-=")] Sub,
#[strum(serialize = "*=")] Mul,
#[strum(serialize = "/=")] Div,
#[strum(serialize = "%=")] Rem,
#[strum(serialize = "|=")] BitOr,
#[strum(serialize = "^=")] BitXor,
#[strum(serialize = "&=")] BitAnd,
#[strum(serialize = "<<=")] ShiftLeft,
#[strum(serialize = ">>=")] ShiftRightSigned,
#[strum(serialize = ">>>=")] ShiftRightUnsigned,
}
}
impl AssignOpKind {
pub fn class(self) -> OpClass {
match self {
Self::Assign => OpClass::DirectAssignment,
Self::Add => OpClass::Arithmetic,
Self::Sub => OpClass::Arithmetic,
Self::Mul => OpClass::Arithmetic,
Self::Div => OpClass::Arithmetic,
Self::Rem => OpClass::Arithmetic,
Self::BitOr => OpClass::Bitwise,
Self::BitXor => OpClass::Bitwise,
Self::BitAnd => OpClass::Bitwise,
Self::ShiftLeft => OpClass::Shift,
Self::ShiftRightSigned => OpClass::Shift,
Self::ShiftRightUnsigned => OpClass::Shift,
}
}
/// Iterate over all assign ops.
pub fn iter() -> impl Iterator<Item=AssignOpKind> {
<Self as strum::IntoEnumIterator>::iter()
}
pub fn corresponding_binop(self) -> Option<BinOpKind> {
match self {
token![=] => None,
token![+=] => Some(token![+]),
token![-=] => Some(token![-]),
token![*=] => Some(token![*]),
token![/=] => Some(token![/]),
token![%=] => Some(token![%]),
token![|=] => Some(token![|]),
token![^=] => Some(token![^]),
token![&=] => Some(token![&]),
token![<<=] => Some(token![<<]),
token![>>=] => Some(token![>>]),
token![>>>=] => Some(token![>>>]),
}
}
}
/// A braced series of statements, typically written at an increased indentation level.
///
/// Every Block always has at least two [`Stmt`]s, as on creation it is bookended by dummy
/// statements to ensure it has a well-defined start and end time.
#[derive(Debug, Clone, PartialEq)]
pub struct Block(pub Vec<Sp<Stmt>>);
impl Block {
/// Node ID for looking up e.g. the time label before the first statement in the loop body.
pub fn start_node_id(&self) -> NodeId { self.first_stmt().node_id.unwrap() }
/// Node ID for looking up e.g. the time label after the final statement in the loop body.
pub fn end_node_id(&self) -> NodeId { self.last_stmt().node_id.unwrap() }
/// Zero-length span at beginning of block interior.
pub fn start_span(&self) -> Span { self.first_stmt().span.start_span() }
/// Zero-length span at end of block interior.
pub fn end_span(&self) -> Span { self.last_stmt().span.end_span() }
pub fn first_stmt(&self) -> &Sp<Stmt> {
self.0.get(0).expect("(bug?) unexpected empty block!")
}
pub fn last_stmt(&self) -> &Sp<Stmt> {
self.0.last().expect("(bug?) unexpected empty block!")
}
}
// =============================================================================
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Ternary {
cond: Box<Sp<Expr>>,
question: Sp<()>,
left: Box<Sp<Expr>>,
colon: Sp<()>,
right: Box<Sp<Expr>>,
},
BinOp(Box<Sp<Expr>>, Sp<BinOpKind>, Box<Sp<Expr>>),
UnOp(Sp<UnOpKind>, Box<Sp<Expr>>),
/// `--` or `++`.
XcrementOp {
op: Sp<XcrementOpKind>,
order: XcrementOpOrder,
var: Sp<Var>,
},
Var(Sp<Var>),
Call(ExprCall),
/// (a:b:c:d) syntax. Always has at least two items. Items aside from the first may be omitted (None).
DiffSwitch(ds_util::DiffSwitchVec<Sp<Expr>>),
LitInt {
value: raw::LangInt,
/// A hint to the formatter on how it should write the integer.
///
/// (not meaningful/left as default when parsing source code;
/// if you want an integer in a diagnostic to appear as it was written,
/// consider labeling its span in the message instead)
format: IntFormat,
},
LitFloat { value: raw::LangFloat },
LitString(LitString),
LabelProperty {
label: Sp<Ident>,
keyword: Sp<LabelPropertyKeyword>
},
EnumConst {
enum_name: Sp<Ident>,
ident: Sp<ResIdent>,
},
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IntFormat {
pub signed: bool,
pub radix: IntRadix,
}
impl IntFormat {
pub const UNSIGNED: IntFormat = IntFormat { signed: false, radix: IntRadix::Dec };
pub const SIGNED: IntFormat = IntFormat { signed: true, radix: IntRadix::Dec };
pub const HEX: IntFormat = IntFormat { signed: false, radix: IntRadix::Hex };
pub const BIN: IntFormat = IntFormat { signed: false, radix: IntRadix::Bin };
pub const BOOL: IntFormat = IntFormat { signed: true, radix: IntRadix::Bool };
/// Used to decompile `jmp` in function syntax.
pub const SIGNED_HEX: IntFormat = IntFormat { signed: true, radix: IntRadix::Hex };
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IntRadix {
/// Display as decimal.
Dec,
/// Display as hexadecimal, with an `0x` prefix.
Hex,
/// Display as binary, with an `0b` prefix.
Bin,
/// Use `true` and `false` if the value is `1` or `0`. Otherwise, fall back to decimal.
Bool,
}
impl Expr {
pub fn int_of_ty(value: i32, ty: value::ScalarType) -> Self { match ty {
value::ScalarType::Int => value.into(),
value::ScalarType::Float => (value as f32).into(),
value::ScalarType::String => panic!("Expr::zero() called on type String"),
}}
pub fn descr(&self) -> &'static str { match self {
Expr::Ternary { .. } => "ternary",
Expr::BinOp { .. } => "binary operator",
Expr::UnOp { .. } => "unary operator",
Expr::XcrementOp { .. } => "in-place operator",
Expr::Call { .. } => "call expression",
Expr::DiffSwitch { .. } => "difficulty switch",
Expr::LitInt { .. } => "literal integer",
Expr::LabelProperty { .. } => "label property",
Expr::LitFloat { .. } => "literal float",
Expr::LitString { .. } => "literal string",
Expr::EnumConst { .. } => "enum const",
Expr::Var { .. } => "var expression",
}}
pub fn can_lower_to_immediate(&self) -> bool { match self {
Expr::LabelProperty { .. } => true,
Expr::LitInt { .. } => true,
Expr::LitFloat { .. } => true,
Expr::LitString { .. } => true,
// FIXME: this just plain feels funny, but it works for the current uses of this function.
// There is an integration test to ensure we run into problems if we try to use
// this to validate 'const' function args
Expr::DiffSwitch(cases) => {
cases.iter().flat_map(|opt| opt.as_ref())
.all(|case| case.can_lower_to_immediate())
},
_ => false,
}}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ExprCall {
// note: deliberately called 'name' instead of 'ident' so that you can
// match both this and the inner ident without shadowing
pub name: Sp<CallableName>,
/// Args beginning with `@` that represent raw pieces of an instruction.
pub pseudos: Vec<Sp<PseudoArg>>,
/// Positional args. One should check [`Self::actually_has_args`] before using this.
pub args: Vec<Sp<Expr>>,
}
impl ExprCall {
/// Extract a `@blob` pseudo-arg if one exists.
pub fn blob(&self) -> Option<&Sp<PseudoArg>> {
self.pseudos.iter().find(|p| matches!(p.kind.value, token![blob]))
}
/// This returns `true` if [`Self::args`] actually represents a list of arguments.
///
/// A counter-example would be a call with `@blob`. Such a function call will have an
/// empty argument list, but should not be misinterpreted as a call with 0 arity.
pub fn actually_has_args(&self) -> bool {
self.blob().is_none()
}
}
/// An identifier in a function call.
///
/// Raw instructions (`ins_`) are separately recognized so that they don't have to take part
/// in name resolution. This makes it easier to use the AST VM [`crate::vm::AstVm`].
#[derive(Debug, Clone, PartialEq)]
pub enum CallableName {
Normal {
ident: ResIdent,
/// This field is `None` until initialized by [`crate::passes::assign_languages`] (after which it may still be `None`).
///
/// It is only here so that name resolution can consider instruction aliases; nothing else should ever need it,
/// as all useful information can be found through the resolved [`DefId`].
language_if_ins: Option<LanguageKey>,
},
Ins {
opcode: u16,
/// This field is `None` until initialized by [`crate::passes::assign_languages`] (after which it is guaranteed to be `Some`).
/// It exists to help a variety of other passes look up e.g. type info about raw registers.
///
/// Notably, in ECL, some of these may be set to [`InstrLanguage::Timeline`] instead of [`InstrLanguage::ECL`].
language: Option<LanguageKey>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct Var {
/// A variable mentioned by name, possibly with a type sigil.
pub ty_sigil: Option<VarSigil>,
pub name: VarName,
}
impl VarName {
/// Construct from the identifier of a local, parameter, or constant. (but NOT a register alias)
pub fn new_non_reg(ident: ResIdent) -> Self {
VarName::Normal { ident, language_if_reg: None }
}
/// For use internally by the parser on idents that may or may not be register aliases.
///
/// Code outside of the parser should not use this; it should instead use [`Self::new_non_register`] if the
/// ident is known not to refer to a register, else it should construct one manually with the correct language.
pub(crate) fn from_parsed_ident(ident: ResIdent) -> Self {
// for the parser, it is okay to use 'language_if_reg: None' on register aliases because a later pass will fill it in.
VarName::Normal { ident, language_if_reg: None }
}
pub fn is_named(&self) -> bool { match self {
VarName::Normal { .. } => true,
VarName::Reg { .. } => false,
}}
/// Panic if it's not a normal ident. This should be safe to call on vars in declarations.
#[track_caller]
pub fn expect_ident(&self) -> &ResIdent { match self {
VarName::Normal { ident, .. } => ident,
VarName::Reg { .. } => panic!("unexpected register"),
}}
}
#[derive(Debug, Clone, PartialEq)]
pub enum VarName {
Normal {
ident: ResIdent,
/// This field is `None` until initialized by [`crate::passes::assign_languages`] (after which it may still be `None`).
///
/// It is only here so that name resolution can consider register aliases; nothing else should ever need it,
/// as all useful information can be found through the resolved [`DefId`].
language_if_reg: Option<LanguageKey>,
},
Reg {
reg: RegId,
/// This field is `None` until initialized by [`crate::passes::assign_languages`] (after which it is guaranteed to be `Some`).
/// It exists to help a variety of other passes look up e.g. type info about raw instructions.
language: Option<LanguageKey>,
},
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(strum::EnumIter)]
pub enum BinOpKind {
#[strum(serialize = "+")] Add,
#[strum(serialize = "-")] Sub,
#[strum(serialize = "*")] Mul,
#[strum(serialize = "/")] Div,
#[strum(serialize = "%")] Rem,
#[strum(serialize = "==")] Eq,
#[strum(serialize = "!=")] Ne,
#[strum(serialize = "<")] Lt,
#[strum(serialize = "<=")] Le,
#[strum(serialize = ">")] Gt,
#[strum(serialize = ">=")] Ge,
#[strum(serialize = "|")] BitOr,
#[strum(serialize = "^")] BitXor,
#[strum(serialize = "&")] BitAnd,
#[strum(serialize = "||")] LogicOr,
#[strum(serialize = "&&")] LogicAnd,
#[strum(serialize = "<<")] ShiftLeft,
#[strum(serialize = ">>")] ShiftRightSigned,
#[strum(serialize = ">>>")] ShiftRightUnsigned,
}
}
impl BinOpKind {
pub fn class(self) -> OpClass {
use BinOpKind as B;
match self {
B::Add | B::Sub | B::Mul | B::Div | B::Rem => OpClass::Arithmetic,
B::Eq | B::Ne | B::Lt | B::Le | B::Gt | B::Ge => OpClass::Comparison,
B::BitOr | B::BitXor | B::BitAnd => OpClass::Bitwise,
B::LogicOr | B::LogicAnd => OpClass::Logical,
B::ShiftLeft | B::ShiftRightSigned | B::ShiftRightUnsigned => OpClass::Shift,
}
}
pub fn is_comparison(self) -> bool {
self.class() == OpClass::Comparison
}
/// Iterate over all binops.
pub fn iter() -> impl Iterator<Item=BinOpKind> {
<Self as strum::IntoEnumIterator>::iter()
}
/// Iterate over the comparison ops.
pub fn iter_comparison() -> impl Iterator<Item=BinOpKind> {
Self::iter().filter(|&op| Self::is_comparison(op))
}
pub fn negate_comparison(self) -> Option<BinOpKind> { match self {
token![==] => Some(token![!=]),
token![!=] => Some(token![==]),
token![<=] => Some(token![>]),
token![>=] => Some(token![<]),
token![<] => Some(token![>=]),
token![>] => Some(token![<=]),
_ => None,
}}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(strum::EnumIter)]
pub enum UnOpKind {
#[strum(serialize = "!")] Not,
#[strum(serialize = "-")] Neg,
#[strum(serialize = "~")] BitNot,
#[strum(serialize = "sin")] Sin,
#[strum(serialize = "cos")] Cos,
#[strum(serialize = "tan")] Tan,
#[strum(serialize = "asin")] Asin,
#[strum(serialize = "acos")] Acos,
#[strum(serialize = "atan")] Atan,
#[strum(serialize = "sqrt")] Sqrt,
#[strum(serialize = "$")] EncodeI,
#[strum(serialize = "%")] EncodeF,
#[strum(serialize = "int")] CastI,
#[strum(serialize = "float")] CastF,
}
}
impl UnOpKind {
pub fn class(&self) -> OpClass {
match self {
UnOpKind::Not => OpClass::Logical,
UnOpKind::Neg => OpClass::Arithmetic,
UnOpKind::BitNot => OpClass::Bitwise,
UnOpKind::Sin => OpClass::FloatMath,
UnOpKind::Cos => OpClass::FloatMath,
UnOpKind::Tan => OpClass::FloatMath,
UnOpKind::Asin => OpClass::FloatMath,
UnOpKind::Acos => OpClass::FloatMath,
UnOpKind::Atan => OpClass::FloatMath,
UnOpKind::Sqrt => OpClass::FloatMath,
UnOpKind::CastI => OpClass::Cast,
UnOpKind::CastF => OpClass::Cast,
UnOpKind::EncodeI => OpClass::TySigil,
UnOpKind::EncodeF => OpClass::TySigil,
}
}
/// Iterate over all unops.
pub fn iter() -> impl Iterator<Item=UnOpKind> {
<Self as strum::IntoEnumIterator>::iter()
}
/// Convert the `%` and `$` operators into var sigils.
pub fn as_ty_sigil(&self) -> Option<VarSigil> {
match self {
token![unop $] => Some(token![$]),
token![unop %] => Some(token![%]),
_ => None,
}
}
/// Convert the `%`, `$`, `int`, and `float` operators into var sigils.
pub fn as_ty_sigil_with_auto_cast(&self) -> Option<VarSigil> {
match self {
token![unop int] => Some(token![$]),
token![unop float] => Some(token![%]),
_ => self.as_ty_sigil(),
}
}
pub fn is_cast_of_type(&self, ty: value::ScalarType) -> bool {
match (self, ty) {
(token![unop int], value::ScalarType::Int) => true,
(token![unop float], value::ScalarType::Float) => true,
_ => false,
}
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(strum::EnumIter)]
pub enum XcrementOpKind {
#[strum(serialize="++")] Inc,
#[strum(serialize="--")] Dec,
}
}
impl XcrementOpKind {
/// Iterate over all xcrement ops.
pub fn iter() -> impl Iterator<Item=XcrementOpKind> {
<Self as strum::IntoEnumIterator>::iter()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(strum::EnumIter)]
pub enum XcrementOpOrder { Pre, Post }
impl XcrementOpOrder {
/// Iterate over all xcrement orders.
pub fn iter() -> impl Iterator<Item=XcrementOpOrder> {
<Self as strum::IntoEnumIterator>::iter()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OpClass {
Arithmetic,
Comparison,
Bitwise,
Shift,
Logical,
FloatMath,
TySigil,
Cast,
DirectAssignment,
}
impl OpClass {
pub fn descr(&self) -> &'static str {
match self {
Self::Arithmetic => "arithmetic",
Self::Comparison => "comparison",
Self::Bitwise => "bitwise",
Self::Shift => "shift",
Self::Logical => "logical",
Self::FloatMath => "mathematical",
Self::Cast => "cast",
Self::TySigil => "sigil",
Self::DirectAssignment => "direct",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PseudoArg {
pub at_sign: Sp<()>,
pub kind: Sp<PseudoArgKind>,
pub eq_sign: Sp<()>,
pub value: Sp<Expr>,
}
impl PseudoArg {
/// Get the span that represents the "heading" of a pseudo arg. (everything but the value)
pub fn tag_span(&self) -> Span {
self.at_sign.span.merge(self.eq_sign.span)
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PseudoArgKind {
#[strum(serialize = "mask")] Mask,
#[strum(serialize = "pop")] Pop,
#[strum(serialize = "blob")] Blob,
#[strum(serialize = "arg0")] ExtraArg,
#[strum(serialize = "nargs")] ArgCount,
}
}
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LabelPropertyKeyword {
#[strum(serialize = "offsetof")] OffsetOf,
#[strum(serialize = "timeof")] TimeOf,
}
}
impl From<raw::LangInt> for Expr {
fn from(value: raw::LangInt) -> Expr { Expr::LitInt { value, format: IntFormat::SIGNED } }
}
impl From<raw::LangFloat> for Expr {
fn from(value: raw::LangFloat) -> Expr { Expr::LitFloat { value } }
}
impl From<String> for Expr {
fn from(string: String) -> Expr { Expr::LitString(LitString { string }) }
}
impl From<Sp<raw::LangInt>> for Sp<Expr> {
fn from(num: Sp<raw::LangInt>) -> Sp<Expr> { sp!(num.span => Expr::from(num.value)) }
}
impl From<Sp<raw::LangFloat>> for Sp<Expr> {
fn from(num: Sp<raw::LangFloat>) -> Sp<Expr> { sp!(num.span => Expr::from(num.value)) }
}
impl From<Sp<Var>> for Sp<Expr> {
fn from(var: Sp<Var>) -> Sp<Expr> { sp!(var.span => Expr::Var(var)) }
}
impl From<Sp<String>> for Sp<Expr> {
fn from(string: Sp<String>) -> Sp<Expr> { sp!(string.span => Expr::from(string.value)) }
}
// =============================================================================
string_enum! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TypeKeyword {
#[strum(serialize = "int")] Int,
#[strum(serialize = "float")] Float,
#[strum(serialize = "string")] String,
#[strum(serialize = "var")] Var,
#[strum(serialize = "void")] Void,
}
}
impl TypeKeyword {
/// Get the type, when used on a keyword that comes from a [`Var`].
pub fn var_ty(self) -> value::VarType {
match self {
TypeKeyword::Int => value::ScalarType::Int.into(),
TypeKeyword::Float => value::ScalarType::Float.into(),
TypeKeyword::String => value::ScalarType::String.into(),
TypeKeyword::Var => value::VarType::Untyped { explicit: false },
TypeKeyword::Void => unreachable!("void var"),
}
}
/// Get the type, when used on a keyword for a return type.
pub fn expr_ty(self) -> value::ExprType {
match self {
TypeKeyword::Int => value::ScalarType::Int.into(),
TypeKeyword::Float => value::ScalarType::Float.into(),
TypeKeyword::String => value::ScalarType::String.into(),
TypeKeyword::Void => value::ExprType::Void,
TypeKeyword::Var => unreachable!("var return type"),
}
}
}
impl From<value::ScalarType> for TypeKeyword {
fn from(ty: value::ScalarType) -> Self { match ty {
value::ScalarType::Int => Self::Int,
value::ScalarType::Float => Self::Float,
value::ScalarType::String => Self::String,
}}
}
impl From<value::VarType> for TypeKeyword {
fn from(ty: value::VarType) -> Self { match ty {
value::VarType::Typed(ty) => ty.into(),
value::VarType::Untyped { .. } => Self::Var,
}}
}
impl From<value::ExprType> for TypeKeyword {
fn from(ty: value::ExprType) -> Self { match ty {
value::ExprType::Value(ty) => ty.into(),
value::ExprType::Void => Self::Void,
}}
}
string_enum! {
/// A type sigil on a variable.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(enum_map::Enum)]
pub enum VarSigil {
#[strum(serialize = "$")] Int,
#[strum(serialize = "%")] Float,
}
}
impl VarSigil {
pub fn from_ty(x: value::ScalarType) -> Option<VarSigil> {
match x {
value::ScalarType::Int => Some(VarSigil::Int),
value::ScalarType::Float => Some(VarSigil::Float),
value::ScalarType::String => None,
}
}
}
impl From<VarSigil> for value::ScalarType {
fn from(x: VarSigil) -> value::ScalarType {
match x {
VarSigil::Int => value::ScalarType::Int,
VarSigil::Float => value::ScalarType::Float,
}
}
}
/// A string literal.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LitString {
pub string: String,
}
impl From<String> for LitString {
fn from(string: String) -> Self { LitString { string } }
}
impl From<&str> for LitString {
fn from(str: &str) -> Self { LitString { string: str.to_owned() } }
}
// =============================================================================
impl std::fmt::Display for CallableName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CallableName::Normal { ident, language_if_ins: _ } => fmt::Display::fmt(ident, f),