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
use std::fmt;

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Bytecode {
    Halt,
    Dup,
    PushLocal(u8, u8),
    PushArgument(u8, u8),
    PushField(u8),
    PushBlock(u8),
    PushConstant(u8),
    PushConstant0,
    PushConstant1,
    PushConstant2,
    PushGlobal(u8),
    Push0,
    Push1,
    PushNil,
    Pop,
    PopLocal(u8, u8),
    PopArgument(u8, u8),
    PopField(u8),
    Send1(u8),
    Send2(u8),
    Send3(u8),
    SendN(u8),
    SuperSend1(u8),
    SuperSend2(u8),
    SuperSend3(u8),
    SuperSendN(u8),
    ReturnLocal,
    ReturnNonLocal,
}

impl Bytecode {
    /// Get the instruction's name.
    #[rustfmt::skip]
    pub fn name(self) -> &'static str {
        // NAMES[self as usize]
        match self {
            Self::Halt               => "HALT",
            Self::Dup                => "DUP",
            Self::PushLocal(_, _)    => "PUSH_LOCAL",
            Self::PushArgument(_, _) => "PUSH_ARGUMENT",
            Self::PushField(_)       => "PUSH_FIELD",
            Self::PushBlock(_)       => "PUSH_BLOCK",
            Self::PushConstant(_)    => "PUSH_CONSTANT",
            Self::PushConstant0      => "PUSH_CONSTANT_0",
            Self::PushConstant1      => "PUSH_CONSTANT_1",
            Self::PushConstant2      => "PUSH_CONSTANT_2",
            Self::PushGlobal(_)      => "PUSH_GLOBAL",
            Self::Push0              => "PUSH_0",
            Self::Push1              => "PUSH_1",
            Self::PushNil            => "PUSH_NIL",
            Self::Pop                => "POP",
            Self::PopLocal(_, _)     => "POP_LOCAL",
            Self::PopArgument(_, _)  => "POP_ARGUMENT",
            Self::PopField(_)        => "POP_FIELD",
            Self::Send1(_)            => "SEND_1",
            Self::Send2(_)            => "SEND_2",
            Self::Send3(_)            => "SEND_3",
            Self::SendN(_)            => "SEND_N",
            Self::SuperSend1(_)       => "SUPER_SEND_1",
            Self::SuperSend2(_)       => "SUPER_SEND_2",
            Self::SuperSend3(_)       => "SUPER_SEND_3",
            Self::SuperSendN(_)       => "SUPER_SEND_N",
            Self::ReturnLocal        => "RETURN_LOCAL",
            Self::ReturnNonLocal     => "RETURN_NON_LOCAL",
        }
    }

    /// Get the instruction's name padded so that every padded names are of the same length.
    #[rustfmt::skip]
    pub fn padded_name(self) -> &'static str {
        // PADDED_NAMES[self as usize]
        match self {
            Self::Halt               => "HALT            ",
            Self::Dup                => "DUP             ",
            Self::PushLocal(_, _)    => "PUSH_LOCAL      ",
            Self::PushArgument(_, _) => "PUSH_ARGUMENT   ",
            Self::PushField(_)       => "PUSH_FIELD      ",
            Self::PushBlock(_)       => "PUSH_BLOCK      ",
            Self::PushConstant(_)    => "PUSH_CONSTANT   ",
            Self::PushConstant0      => "PUSH_CONSTANT_0 ",
            Self::PushConstant1      => "PUSH_CONSTANT_1 ",
            Self::PushConstant2      => "PUSH_CONSTANT_2 ",
            Self::PushGlobal(_)      => "PUSH_GLOBAL     ",
            Self::Push0              => "PUSH_0          ",
            Self::Push1              => "PUSH_1          ",
            Self::PushNil            => "PUSH_NIL        ",
            Self::Pop                => "POP             ",
            Self::PopLocal(_, _)     => "POP_LOCAL       ",
            Self::PopArgument(_, _)  => "POP_ARGUMENT    ",
            Self::PopField(_)        => "POP_FIELD       ",
            Self::Send1(_)           => "SEND_1          ",
            Self::Send2(_)           => "SEND_2          ",
            Self::Send3(_)           => "SEND_3          ",
            Self::SendN(_)           => "SEND_N          ",
            Self::SuperSend1(_)      => "SUPER_SEND_1    ",
            Self::SuperSend2(_)      => "SUPER_SEND_2    ",
            Self::SuperSend3(_)      => "SUPER_SEND_3    ",
            Self::SuperSendN(_)      => "SUPER_SEND_N    ",
            Self::ReturnLocal        => "RETURN_LOCAL    ",
            Self::ReturnNonLocal     => "RETURN_NON_LOCAL",
        }
    }
}

pub static NAMES: [&str; 28] = [
    "HALT",
    "DUP",
    "PUSH_LOCAL",
    "PUSH_ARGUMENT",
    "PUSH_FIELD",
    "PUSH_BLOCK",
    "PUSH_CONSTANT",
    "PUSH_CONSTANT_0",
    "PUSH_CONSTANT_1",
    "PUSH_CONSTANT_2",
    "PUSH_GLOBAL",
    "PUSH_0",
    "PUSH_1",
    "PUSH_NIL",
    "POP",
    "POP_LOCAL",
    "POP_ARGUMENT",
    "POP_FIELD",
    "SEND_1",
    "SEND_2",
    "SEND_3",
    "SEND_N",
    "SUPER_SEND_1",
    "SUPER_SEND_2",
    "SUPER_SEND_3",
    "SUPER_SEND_N",
    "RETURN_LOCAL",
    "RETURN_NON_LOCAL",
];

pub static PADDED_NAMES: [&str; 28] = [
    "HALT            ",
    "DUP             ",
    "PUSH_LOCAL      ",
    "PUSH_ARGUMENT   ",
    "PUSH_FIELD      ",
    "PUSH_BLOCK      ",
    "PUSH_CONSTANT   ",
    "PUSH_CONSTANT_0 ",
    "PUSH_CONSTANT_1 ",
    "PUSH_CONSTANT_2 ",
    "PUSH_GLOBAL     ",
    "PUSH_0          ",
    "PUSH_1          ",
    "PUSH_NIL        ",
    "POP             ",
    "POP_LOCAL       ",
    "POP_ARGUMENT    ",
    "POP_FIELD       ",
    "SEND_1          ",
    "SEND_2          ",
    "SEND_3          ",
    "SEND_N          ",
    "SUPER_SEND_1    ",
    "SUPER_SEND_2    ",
    "SUPER_SEND_3    ",
    "SUPER_SEND_N    ",
    "RETURN_LOCAL    ",
    "RETURN_NON_LOCAL",
];

impl fmt::Display for Bytecode {
    #[rustfmt::skip]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Halt                      => write!(f, "HALT"),
            Self::Dup                       => write!(f, "DUP"),
            Self::PushLocal(up_idx, idx)    => write!(f, "PUSH_LOCAL {}, {}", up_idx, idx),
            Self::PushArgument(up_idx, idx) => write!(f, "PUSH_ARGUMENT {}, {}", up_idx, idx),
            Self::PushField(idx)            => write!(f, "PUSH_FIELD {}", idx),
            Self::PushBlock(idx)            => write!(f, "PUSH_BLOCK {}", idx),
            Self::PushConstant(idx)         => write!(f, "PUSH_CONSTANT {}", idx),
            Self::PushConstant0                 => write!(f, "PUSH_CONSTANT_0"),
            Self::PushConstant1                 => write!(f, "PUSH_CONSTANT_1"),
            Self::PushConstant2                 => write!(f, "PUSH_CONSTANT_2"),
            Self::PushGlobal(idx)         => write!(f, "PUSH_GLOBAL {}", idx),
            Self::Push0                         => write!(f, "PUSH_0"),
            Self::Push1                         => write!(f, "PUSH_1"),
            Self::PushNil                       => write!(f, "PUSH_NIL"),
            Self::Pop                           => write!(f, "POP"),
            Self::PopLocal(up_idx, idx)     => write!(f, "POP_LOCAL {}, {}", up_idx, idx),
            Self::PopArgument(up_idx, idx)  => write!(f, "POP_ARGUMENT {}, {}", up_idx, idx),
            Self::PopField(idx)             => write!(f, "POP_FIELD {}", idx),
            Self::Send1(idx)                 => write!(f, "SEND_1 {}", idx),
            Self::Send2(idx)                 => write!(f, "SEND_2 {}", idx),
            Self::Send3(idx)                 => write!(f, "SEND_3 {}", idx),
            Self::SendN(idx)                 => write!(f, "SEND_N {}", idx),
            Self::SuperSend1(idx)            => write!(f, "SUPER_SEND_1 {}", idx),
            Self::SuperSend2(idx)            => write!(f, "SUPER_SEND_2 {}", idx),
            Self::SuperSend3(idx)            => write!(f, "SUPER_SEND_3 {}", idx),
            Self::SuperSendN(idx)            => write!(f, "SUPER_SEND_N {}", idx),
            Self::ReturnLocal               => write!(f, "RETURN_LOCAL", ),
            Self::ReturnNonLocal            => write!(f, "RETURN_NON_LOCAL", ),
        }
    }
}