Skip to main content

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