endbasic_core/
bytecode.rs

1// EndBASIC
2// Copyright 2022 Julio Merino
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License.  You may obtain a copy
6// of the License at:
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13// License for the specific language governing permissions and limitations
14// under the License.
15
16//! Low-level representation of an EndBASIC program for execution.
17
18use crate::ast::{ExprType, Value};
19use crate::reader::LineCol;
20use crate::syms::SymbolKey;
21
22/// Convenience type to represent a program address.
23pub type Address = usize;
24
25/// Components of a variable definition.
26#[derive(Debug, PartialEq)]
27#[cfg_attr(test, derive(Clone))]
28pub struct DimISpan {
29    /// Name of the variable to define.
30    pub name: SymbolKey,
31
32    /// Whether the variable is global or not.
33    pub shared: bool,
34
35    /// Type of the variable to be defined.
36    pub vtype: ExprType,
37}
38
39/// Components of an array definition.
40#[derive(Debug, PartialEq)]
41#[cfg_attr(test, derive(Clone))]
42pub struct DimArrayISpan {
43    /// Name of the array to define.
44    pub name: SymbolKey,
45
46    /// Position of the name.
47    pub name_pos: LineCol,
48
49    /// Whether the array is global or not.
50    pub shared: bool,
51
52    /// Number of values in the stack representing the dimensions of the array.
53    pub dimensions: usize,
54
55    /// Type of the array to be defined.
56    pub subtype: ExprType,
57
58    /// Position of the subtype.
59    pub subtype_pos: LineCol,
60}
61
62/// Components of an unconditional jump instruction.
63#[derive(Debug, Eq, PartialEq)]
64pub struct JumpISpan {
65    /// The address to jump to.
66    pub addr: Address,
67}
68
69/// Components of a conditional jump that depends on whether a variable is defined.
70#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
71pub struct JumpIfDefinedISpan {
72    /// The variable to check for nonexistence.
73    pub var: SymbolKey,
74
75    /// The address to jump to.
76    pub addr: Address,
77}
78
79/// Components of a change to the error handler.
80#[derive(Clone, Copy)]
81#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
82pub enum ErrorHandlerISpan {
83    /// Jumps to the included address on error.
84    Jump(Address),
85
86    /// Sets the error handler to the default.
87    None,
88
89    /// Sets the error handler to resume execution at to the next instruction.
90    ResumeNext,
91}
92
93/// Components of a request to unset a variable.
94#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
95pub struct UnsetISpan {
96    /// Name of the variable to unset.
97    pub name: SymbolKey,
98
99    /// Position of where this instruction was requested.
100    pub pos: LineCol,
101}
102
103/// Representation of all possible instructions in the bytecode.
104#[cfg_attr(test, derive(Debug, PartialEq))]
105pub enum Instruction {
106    /// Represents a binary logical "and" operation.
107    LogicalAnd(LineCol),
108
109    /// Represents a binary logical "or" operation.
110    LogicalOr(LineCol),
111
112    /// Represents a binary logical "xor" operation.
113    LogicalXor(LineCol),
114
115    /// Represents a unary logical "not" operation.
116    LogicalNot(LineCol),
117
118    /// Represents a binary bitwise "and" operation.
119    BitwiseAnd(LineCol),
120
121    /// Represents a binary bitwise "or" operation.
122    BitwiseOr(LineCol),
123
124    /// Represents a binary bitwise "xor" operation.
125    BitwiseXor(LineCol),
126
127    /// Represents a unary bitwise "not" operation.
128    BitwiseNot(LineCol),
129
130    /// Represents a left bitshift.
131    ShiftLeft(LineCol),
132
133    /// Represents a right bitshift.
134    ShiftRight(LineCol),
135
136    /// Represents an equality comparison for booleans.
137    EqualBooleans(LineCol),
138
139    /// Represents an inequality comparison for booleans.
140    NotEqualBooleans(LineCol),
141
142    /// Represents an equality comparison for doubles.
143    EqualDoubles(LineCol),
144
145    /// Represents an inequality comparison for doubles.
146    NotEqualDoubles(LineCol),
147
148    /// Represents a less-than comparison for doubles.
149    LessDoubles(LineCol),
150
151    /// Represents a less-or-equal comparison for doubles.
152    LessEqualDoubles(LineCol),
153
154    /// Represents a greater-than comparison for doubles.
155    GreaterDoubles(LineCol),
156
157    /// Represents a greater-or-equal comparison for doubles.
158    GreaterEqualDoubles(LineCol),
159
160    /// Represents an equality comparison for integers.
161    EqualIntegers(LineCol),
162
163    /// Represents an inequality comparison for integers.
164    NotEqualIntegers(LineCol),
165
166    /// Represents a less-than comparison for integers.
167    LessIntegers(LineCol),
168
169    /// Represents a less-or-equal comparison for integers.
170    LessEqualIntegers(LineCol),
171
172    /// Represents a greater-than comparison for integers.
173    GreaterIntegers(LineCol),
174
175    /// Represents a greater-or-equal comparison for integers.
176    GreaterEqualIntegers(LineCol),
177
178    /// Represents an equality comparison for strings.
179    EqualStrings(LineCol),
180
181    /// Represents an inequality comparison for strings.
182    NotEqualStrings(LineCol),
183
184    /// Represents a less-than comparison for strings.
185    LessStrings(LineCol),
186
187    /// Represents a less-or-equal comparison for strings.
188    LessEqualStrings(LineCol),
189
190    /// Represents a greater-than comparison for strings.
191    GreaterStrings(LineCol),
192
193    /// Represents a greater-or-equal comparison for strings.
194    GreaterEqualStrings(LineCol),
195
196    /// Represents an arithmetic addition operation for doubles.
197    AddDoubles(LineCol),
198
199    /// Represents an arithmetic subtraction operation for doubles.
200    SubtractDoubles(LineCol),
201
202    /// Represents an arithmetic multiplication operation for doubles.
203    MultiplyDoubles(LineCol),
204
205    /// Represents an arithmetic division operation for doubles.
206    DivideDoubles(LineCol),
207
208    /// Represents an arithmetic modulo operation for doubles.
209    ModuloDoubles(LineCol),
210
211    /// Represents an arithmetic power operation for doubles.
212    PowerDoubles(LineCol),
213
214    /// Represents an arithmetic sign flip operation for a double.
215    NegateDouble(LineCol),
216
217    /// Represents an arithmetic addition operation for integers.
218    AddIntegers(LineCol),
219
220    /// Represents an arithmetic subtraction operation for integers.
221    SubtractIntegers(LineCol),
222
223    /// Represents an arithmetic multiplication operation for integers.
224    MultiplyIntegers(LineCol),
225
226    /// Represents an arithmetic division operation for integers.
227    DivideIntegers(LineCol),
228
229    /// Represents an arithmetic modulo operation for integers.
230    ModuloIntegers(LineCol),
231
232    /// Represents an arithmetic power operation for integers.
233    PowerIntegers(LineCol),
234
235    /// Represents an arithmetic sign flip operation for an integer.
236    NegateInteger(LineCol),
237
238    /// Represents the concatenation of strings.
239    ConcatStrings(LineCol),
240
241    /// Represents an assignment to an element of an array with the given number of subscripts.
242    ArrayAssignment(SymbolKey, LineCol, usize),
243
244    /// Represents a load of an array's element into the stack.
245    ArrayLoad(SymbolKey, LineCol, usize),
246
247    /// Represents an assignment of a value to a variable.
248    Assign(SymbolKey),
249
250    /// Represents a call to a builtin command such as `PRINT` with the given number of arguments.
251    ///
252    /// The arguments in the stack are interspersed with the separators used to separate them from.
253    /// each other, because those separators have meaning.
254    BuiltinCall(SymbolKey, LineCol, usize),
255
256    /// Represents an unconditional call to a location that will return.
257    Call(JumpISpan),
258
259    /// Represents a call to the given function with the given number of arguments.
260    FunctionCall(SymbolKey, ExprType, LineCol, usize),
261
262    /// Represents a variable definition.
263    Dim(DimISpan),
264
265    /// Represents an array definition.
266    DimArray(DimArrayISpan),
267
268    /// Represents a request to terminate the program.  If the boolean is true, the exit ode is
269    /// at the top of the stack.
270    End(bool),
271
272    /// Represents a request to create a new scope for symbols.
273    EnterScope,
274
275    /// Represents a conversion of a float to an integer with rounding.
276    DoubleToInteger,
277
278    /// Represents a conversion of an integer to a float.
279    IntegerToDouble,
280
281    /// Represents an unconditional jump.
282    Jump(JumpISpan),
283
284    /// Represents an conditional jump that jumps if the variable is defined.
285    JumpIfDefined(JumpIfDefinedISpan),
286
287    /// Represents an conditional jump that jumps if the condition is met.
288    JumpIfTrue(Address),
289
290    /// Represents an conditional jump that jumps if the condition is not met.
291    JumpIfNotTrue(Address),
292
293    /// Represents a request to leave the current scope for symbols.
294    LeaveScope,
295
296    /// Represents a load of a boolean variable's value from main memory into the stack.
297    LoadBoolean(SymbolKey, LineCol),
298
299    /// Represents a load of a double variable's value from main memory into the stack.
300    LoadDouble(SymbolKey, LineCol),
301
302    /// Represents a load of an integer variable's value from main memory into the stack.
303    LoadInteger(SymbolKey, LineCol),
304
305    /// Represents a load of a string variable's value from main memory into the stack.
306    LoadString(SymbolKey, LineCol),
307
308    /// Represents a load of a variable's reference into the stack.
309    LoadRef(SymbolKey, ExprType, LineCol),
310
311    /// Represents an instruction that does nothing.
312    Nop,
313
314    /// Represents a load of a literal boolean value into the top of the stack.
315    PushBoolean(bool, LineCol),
316
317    /// Represents a load of a literal double value into the top of the stack.
318    PushDouble(f64, LineCol),
319
320    /// Represents a load of a literal integer value into the top of the stack.
321    PushInteger(i32, LineCol),
322
323    /// Represents a load of a literal string value into the top of the stack.
324    PushString(String, LineCol),
325
326    /// Represents a return after a call.
327    Return(LineCol),
328
329    /// Represents a change in the error handler state.
330    SetErrorHandler(ErrorHandlerISpan),
331
332    /// Represents a request to unset a variable.
333    Unset(UnsetISpan),
334}
335
336impl Instruction {
337    /// Returns the textual representation of the instruction.
338    pub fn repr(&self) -> (&'static str, Option<String>) {
339        match self {
340            Instruction::BitwiseAnd(_pos) => ("AND%", None),
341            Instruction::BitwiseOr(_pos) => ("OR%", None),
342            Instruction::BitwiseXor(_pos) => ("XOR%", None),
343            Instruction::BitwiseNot(_pos) => ("NOT%", None),
344
345            Instruction::LogicalAnd(_pos) => ("AND?", None),
346            Instruction::LogicalOr(_pos) => ("OR?", None),
347            Instruction::LogicalXor(_pos) => ("XOR?", None),
348            Instruction::LogicalNot(_pos) => ("NOT?", None),
349
350            Instruction::ShiftLeft(_pos) => ("SHL%", None),
351            Instruction::ShiftRight(_pos) => ("SHR%", None),
352
353            Instruction::EqualBooleans(_pos) => ("CMPE?", None),
354            Instruction::NotEqualBooleans(_pos) => ("CMPNE?", None),
355
356            Instruction::EqualDoubles(_pos) => ("CMPE#", None),
357            Instruction::NotEqualDoubles(_pos) => ("CMPNE#", None),
358            Instruction::LessDoubles(_pos) => ("CMPL#", None),
359            Instruction::LessEqualDoubles(_pos) => ("CMPLE#", None),
360            Instruction::GreaterDoubles(_pos) => ("CMPG#", None),
361            Instruction::GreaterEqualDoubles(_pos) => ("CMPGE#", None),
362
363            Instruction::EqualIntegers(_pos) => ("CMPE%", None),
364            Instruction::NotEqualIntegers(_pos) => ("CMPNE%", None),
365            Instruction::LessIntegers(_pos) => ("CMPL%", None),
366            Instruction::LessEqualIntegers(_pos) => ("CMPLE%", None),
367            Instruction::GreaterIntegers(_pos) => ("CMPG%", None),
368            Instruction::GreaterEqualIntegers(_pos) => ("CMPGE%", None),
369
370            Instruction::EqualStrings(_pos) => ("CMPE$", None),
371            Instruction::NotEqualStrings(_pos) => ("CMPNE$", None),
372            Instruction::LessStrings(_pos) => ("CMPL$", None),
373            Instruction::LessEqualStrings(_pos) => ("CMPLE$", None),
374            Instruction::GreaterStrings(_pos) => ("CMPG$", None),
375            Instruction::GreaterEqualStrings(_pos) => ("CMPGE$", None),
376
377            Instruction::AddDoubles(_pos) => ("ADD#", None),
378            Instruction::SubtractDoubles(_pos) => ("SUB#", None),
379            Instruction::MultiplyDoubles(_pos) => ("MUL#", None),
380            Instruction::DivideDoubles(_pos) => ("DIV#", None),
381            Instruction::ModuloDoubles(_pos) => ("MOD#", None),
382            Instruction::PowerDoubles(_pos) => ("POW#", None),
383            Instruction::NegateDouble(_pos) => ("NEG#", None),
384
385            Instruction::AddIntegers(_pos) => ("ADD%", None),
386            Instruction::SubtractIntegers(_pos) => ("SUB%", None),
387            Instruction::MultiplyIntegers(_pos) => ("MUL%", None),
388            Instruction::DivideIntegers(_pos) => ("DIV%", None),
389            Instruction::ModuloIntegers(_pos) => ("MOD%", None),
390            Instruction::PowerIntegers(_pos) => ("POW%", None),
391            Instruction::NegateInteger(_pos) => ("NEG%", None),
392
393            Instruction::ConcatStrings(_pos) => ("CONCAT$", None),
394
395            Instruction::ArrayAssignment(key, _pos, nargs) => {
396                ("SETA", Some(format!("{}, {}", key, nargs)))
397            }
398
399            Instruction::ArrayLoad(key, _pos, nargs) => {
400                ("LOADA", Some(format!("{}, {}", key, nargs)))
401            }
402
403            Instruction::Assign(key) => ("SETV", Some(key.to_string())),
404
405            Instruction::BuiltinCall(key, _pos, nargs) => {
406                ("CALLB", Some(format!("{}, {}", key, nargs)))
407            }
408
409            Instruction::Call(span) => ("CALLA", Some(format!("{:04x}", span.addr))),
410
411            Instruction::FunctionCall(key, etype, _pos, nargs) => {
412                let opcode = match etype {
413                    ExprType::Boolean => "CALLF?",
414                    ExprType::Double => "CALLF#",
415                    ExprType::Integer => "CALLF%",
416                    ExprType::Text => "CALLF$",
417                };
418                (opcode, Some(format!("{}, {}", key, nargs)))
419            }
420
421            Instruction::Dim(span) => {
422                let opcode = match (span.shared, span.vtype) {
423                    (false, ExprType::Boolean) => "DIMV?",
424                    (false, ExprType::Double) => "DIMV#",
425                    (false, ExprType::Integer) => "DIMV%",
426                    (false, ExprType::Text) => "DIMV$",
427                    (true, ExprType::Boolean) => "DIMSV?",
428                    (true, ExprType::Double) => "DIMSV#",
429                    (true, ExprType::Integer) => "DIMSV%",
430                    (true, ExprType::Text) => "DIMSV$",
431                };
432                (opcode, Some(format!("{}", span.name)))
433            }
434
435            Instruction::DimArray(span) => {
436                let opcode = match (span.shared, span.subtype) {
437                    (false, ExprType::Boolean) => "DIMA?",
438                    (false, ExprType::Double) => "DIMA#",
439                    (false, ExprType::Integer) => "DIMA%",
440                    (false, ExprType::Text) => "DIMA$",
441                    (true, ExprType::Boolean) => "DIMSA?",
442                    (true, ExprType::Double) => "DIMSA#",
443                    (true, ExprType::Integer) => "DIMSA%",
444                    (true, ExprType::Text) => "DIMSA$",
445                };
446                (opcode, Some(format!("{}, {}", span.name, span.dimensions)))
447            }
448
449            Instruction::End(has_code) => ("END", Some(format!("{}", has_code))),
450
451            Instruction::EnterScope => ("ENTER", None),
452
453            Instruction::DoubleToInteger => ("#TO%", None),
454            Instruction::IntegerToDouble => ("%TO#", None),
455
456            Instruction::Jump(span) => ("JMP", Some(format!("{:04x}", span.addr))),
457            Instruction::JumpIfDefined(span) => {
458                ("JMPVD", Some(format!("{}, {:04x}", span.var, span.addr)))
459            }
460            Instruction::JumpIfTrue(addr) => ("JMPT", Some(format!("{:04x}", addr))),
461            Instruction::JumpIfNotTrue(addr) => ("JMPNT", Some(format!("{:04x}", addr))),
462
463            Instruction::LeaveScope => ("LEAVE", None),
464
465            Instruction::LoadBoolean(key, _pos) => ("LOAD?", Some(key.to_string())),
466            Instruction::LoadDouble(key, _pos) => ("LOAD#", Some(key.to_string())),
467            Instruction::LoadInteger(key, _pos) => ("LOAD%", Some(key.to_string())),
468            Instruction::LoadString(key, _pos) => ("LOAD$", Some(key.to_string())),
469
470            Instruction::LoadRef(key, _etype, _pos) => ("LOADR", Some(key.to_string())),
471
472            Instruction::Nop => ("NOP", None),
473
474            Instruction::PushBoolean(b, _pos) => ("PUSH?", Some(format!("{}", b))),
475            Instruction::PushDouble(d, _pos) => ("PUSH#", Some(format!("{}", d))),
476            Instruction::PushInteger(i, _pos) => ("PUSH%", Some(format!("{}", i))),
477            Instruction::PushString(s, _pos) => ("PUSH$", Some(format!("\"{}\"", s))),
478
479            Instruction::Return(_pos) => ("RET", None),
480
481            Instruction::SetErrorHandler(span) => match span {
482                ErrorHandlerISpan::Jump(addr) => ("SEHA", Some(format!("{:04x}", addr))),
483                ErrorHandlerISpan::None => ("SEHN", None),
484                ErrorHandlerISpan::ResumeNext => ("SEHRN", None),
485            },
486
487            Instruction::Unset(span) => ("UNSETV", Some(format!("{}", span.name))),
488        }
489    }
490
491    /// Returns the position in the source code where this instruction originated.
492    ///
493    /// For some instructions, we cannot tell their location right now, so we return None for those.
494    pub fn pos(&self) -> Option<LineCol> {
495        match self {
496            Instruction::BitwiseAnd(pos) => Some(*pos),
497            Instruction::BitwiseOr(pos) => Some(*pos),
498            Instruction::BitwiseXor(pos) => Some(*pos),
499            Instruction::BitwiseNot(pos) => Some(*pos),
500
501            Instruction::LogicalAnd(pos) => Some(*pos),
502            Instruction::LogicalOr(pos) => Some(*pos),
503            Instruction::LogicalXor(pos) => Some(*pos),
504            Instruction::LogicalNot(pos) => Some(*pos),
505
506            Instruction::ShiftLeft(pos) => Some(*pos),
507            Instruction::ShiftRight(pos) => Some(*pos),
508
509            Instruction::EqualBooleans(pos) => Some(*pos),
510            Instruction::NotEqualBooleans(pos) => Some(*pos),
511
512            Instruction::EqualDoubles(pos) => Some(*pos),
513            Instruction::NotEqualDoubles(pos) => Some(*pos),
514            Instruction::LessDoubles(pos) => Some(*pos),
515            Instruction::LessEqualDoubles(pos) => Some(*pos),
516            Instruction::GreaterDoubles(pos) => Some(*pos),
517            Instruction::GreaterEqualDoubles(pos) => Some(*pos),
518
519            Instruction::EqualIntegers(pos) => Some(*pos),
520            Instruction::NotEqualIntegers(pos) => Some(*pos),
521            Instruction::LessIntegers(pos) => Some(*pos),
522            Instruction::LessEqualIntegers(pos) => Some(*pos),
523            Instruction::GreaterIntegers(pos) => Some(*pos),
524            Instruction::GreaterEqualIntegers(pos) => Some(*pos),
525
526            Instruction::EqualStrings(pos) => Some(*pos),
527            Instruction::NotEqualStrings(pos) => Some(*pos),
528            Instruction::LessStrings(pos) => Some(*pos),
529            Instruction::LessEqualStrings(pos) => Some(*pos),
530            Instruction::GreaterStrings(pos) => Some(*pos),
531            Instruction::GreaterEqualStrings(pos) => Some(*pos),
532
533            Instruction::AddDoubles(pos) => Some(*pos),
534            Instruction::SubtractDoubles(pos) => Some(*pos),
535            Instruction::MultiplyDoubles(pos) => Some(*pos),
536            Instruction::DivideDoubles(pos) => Some(*pos),
537            Instruction::ModuloDoubles(pos) => Some(*pos),
538            Instruction::PowerDoubles(pos) => Some(*pos),
539            Instruction::NegateDouble(pos) => Some(*pos),
540
541            Instruction::AddIntegers(pos) => Some(*pos),
542            Instruction::SubtractIntegers(pos) => Some(*pos),
543            Instruction::MultiplyIntegers(pos) => Some(*pos),
544            Instruction::DivideIntegers(pos) => Some(*pos),
545            Instruction::ModuloIntegers(pos) => Some(*pos),
546            Instruction::PowerIntegers(pos) => Some(*pos),
547            Instruction::NegateInteger(pos) => Some(*pos),
548
549            Instruction::ConcatStrings(pos) => Some(*pos),
550
551            Instruction::ArrayAssignment(_, pos, _) => Some(*pos),
552            Instruction::ArrayLoad(_, pos, _) => Some(*pos),
553            Instruction::Assign(_) => None,
554            Instruction::BuiltinCall(_, pos, _) => Some(*pos),
555            Instruction::Call(_) => None,
556            Instruction::FunctionCall(_, _, pos, _) => Some(*pos),
557            Instruction::Dim(_) => None,
558            Instruction::DimArray(span) => Some(span.name_pos),
559            Instruction::End(_) => None,
560            Instruction::EnterScope => None,
561            Instruction::DoubleToInteger => None,
562            Instruction::IntegerToDouble => None,
563            Instruction::Jump(_) => None,
564            Instruction::JumpIfDefined(_) => None,
565            Instruction::JumpIfTrue(_) => None,
566            Instruction::JumpIfNotTrue(_) => None,
567            Instruction::LeaveScope => None,
568            Instruction::LoadBoolean(_, pos) => Some(*pos),
569            Instruction::LoadDouble(_, pos) => Some(*pos),
570            Instruction::LoadInteger(_, pos) => Some(*pos),
571            Instruction::LoadString(_, pos) => Some(*pos),
572            Instruction::LoadRef(_, _, pos) => Some(*pos),
573            Instruction::Nop => None,
574            Instruction::PushBoolean(_, pos) => Some(*pos),
575            Instruction::PushDouble(_, pos) => Some(*pos),
576            Instruction::PushInteger(_, pos) => Some(*pos),
577            Instruction::PushString(_, pos) => Some(*pos),
578            Instruction::Return(pos) => Some(*pos),
579            Instruction::SetErrorHandler(_) => None,
580            Instruction::Unset(span) => Some(span.pos),
581        }
582    }
583
584    /// Returns true if this instruction represents the execution of a statement.
585    ///
586    /// This is a heuristic to implement `ON ERROR RESUME NEXT`.  It works for now without
587    /// additional tracking, but maybe this needs to change in the future if we add some
588    /// instruction that cannot be disambiguated.
589    pub(crate) fn is_statement(&self) -> bool {
590        match self {
591            Instruction::LogicalAnd(_)
592            | Instruction::LogicalOr(_)
593            | Instruction::LogicalXor(_)
594            | Instruction::LogicalNot(_)
595            | Instruction::BitwiseAnd(_)
596            | Instruction::BitwiseOr(_)
597            | Instruction::BitwiseXor(_)
598            | Instruction::BitwiseNot(_)
599            | Instruction::ArrayLoad(_, _, _)
600            | Instruction::ShiftLeft(_)
601            | Instruction::ShiftRight(_)
602            | Instruction::EqualBooleans(_)
603            | Instruction::NotEqualBooleans(_)
604            | Instruction::EqualDoubles(_)
605            | Instruction::NotEqualDoubles(_)
606            | Instruction::LessDoubles(_)
607            | Instruction::LessEqualDoubles(_)
608            | Instruction::GreaterDoubles(_)
609            | Instruction::GreaterEqualDoubles(_)
610            | Instruction::EqualIntegers(_)
611            | Instruction::NotEqualIntegers(_)
612            | Instruction::LessIntegers(_)
613            | Instruction::LessEqualIntegers(_)
614            | Instruction::GreaterIntegers(_)
615            | Instruction::GreaterEqualIntegers(_)
616            | Instruction::EqualStrings(_)
617            | Instruction::NotEqualStrings(_)
618            | Instruction::LessStrings(_)
619            | Instruction::LessEqualStrings(_)
620            | Instruction::GreaterStrings(_)
621            | Instruction::GreaterEqualStrings(_)
622            | Instruction::AddDoubles(_)
623            | Instruction::SubtractDoubles(_)
624            | Instruction::MultiplyDoubles(_)
625            | Instruction::DivideDoubles(_)
626            | Instruction::ModuloDoubles(_)
627            | Instruction::PowerDoubles(_)
628            | Instruction::NegateDouble(_)
629            | Instruction::AddIntegers(_)
630            | Instruction::SubtractIntegers(_)
631            | Instruction::MultiplyIntegers(_)
632            | Instruction::DivideIntegers(_)
633            | Instruction::ModuloIntegers(_)
634            | Instruction::PowerIntegers(_)
635            | Instruction::NegateInteger(_)
636            | Instruction::ConcatStrings(_)
637            | Instruction::FunctionCall(_, _, _, _)
638            | Instruction::DoubleToInteger
639            | Instruction::IntegerToDouble
640            | Instruction::LoadBoolean(_, _)
641            | Instruction::LoadDouble(_, _)
642            | Instruction::LoadInteger(_, _)
643            | Instruction::LoadString(_, _)
644            | Instruction::LoadRef(_, _, _)
645            | Instruction::PushBoolean(_, _)
646            | Instruction::PushDouble(_, _)
647            | Instruction::PushInteger(_, _)
648            | Instruction::PushString(_, _)
649            | Instruction::EnterScope
650            | Instruction::LeaveScope => false,
651
652            Instruction::ArrayAssignment(_, _, _)
653            | Instruction::Assign(_)
654            | Instruction::BuiltinCall(_, _, _)
655            | Instruction::Call(_)
656            | Instruction::Dim(_)
657            | Instruction::DimArray(_)
658            | Instruction::End(_)
659            | Instruction::Jump(_)
660            | Instruction::JumpIfDefined(_)
661            | Instruction::JumpIfTrue(_)
662            | Instruction::JumpIfNotTrue(_)
663            | Instruction::Nop
664            | Instruction::Return(_)
665            | Instruction::SetErrorHandler(_)
666            | Instruction::Unset(_) => true,
667        }
668    }
669}
670
671/// Representation of a compiled program.
672#[cfg_attr(test, derive(Debug, PartialEq))]
673pub struct Image {
674    /// Collection of instructions in the program.
675    ///
676    /// The indices of this vector correspond to the program counter.
677    pub instrs: Vec<Instruction>,
678
679    /// Collection of data values in the program.
680    pub data: Vec<Option<Value>>,
681}