endbasic_core/
exec.rs

1// EndBASIC
2// Copyright 2020 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//! Execution engine for EndBASIC programs.
17
18use crate::ast::*;
19use crate::bytecode::*;
20use crate::compiler;
21use crate::parser;
22use crate::reader::LineCol;
23use crate::syms::CallResult;
24use crate::syms::SymbolKey;
25use crate::syms::{CallError, Callable, CallableMetadata, Symbol, Symbols};
26use crate::value;
27use crate::value::double_to_integer;
28use async_channel::{Receiver, Sender, TryRecvError};
29use std::future::Future;
30use std::io;
31use std::pin::Pin;
32use std::rc::Rc;
33
34/// Execution errors.
35#[derive(Debug, thiserror::Error)]
36pub enum Error {
37    /// Compilation error during execution.
38    #[error("{0}")]
39    CompilerError(#[from] compiler::Error),
40
41    /// Evaluation error during execution.
42    #[error("{}:{}: {}", .0.line, .0.col, .1)]
43    EvalError(LineCol, String),
44
45    /// I/O error during execution.
46    #[error("{0}")]
47    IoError(#[from] io::Error),
48
49    /// Hack to support errors that arise from within a program that is `RUN`.
50    // TODO(jmmv): Consider unifying `CallError` with `exec::Error`.
51    #[error("{0}")]
52    NestedError(String),
53
54    /// Parsing error during execution.
55    #[error("{0}")]
56    ParseError(#[from] parser::Error),
57
58    /// Syntax error.
59    #[error("{}:{}: {}", .0.line, .0.col, .1)]
60    SyntaxError(LineCol, String),
61
62    /// Value computation error during execution.
63    #[error("{0}")]
64    ValueError(value::Error),
65}
66
67impl Error {
68    /// Annotates a call evaluation error with the command's metadata.
69    // TODO(jmmv): This is a hack to support the transition to a better Command abstraction within
70    // Symbols and exists to minimize the amount of impacted tests.  Should be removed and/or
71    // somehow unified with the equivalent function in eval::Error.
72    fn from_call_error(md: &CallableMetadata, e: CallError, pos: LineCol) -> Self {
73        match e {
74            CallError::ArgumentError(pos2, e) => Self::SyntaxError(
75                pos,
76                format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
77            ),
78            CallError::EvalError(pos2, e) => {
79                if !md.is_function() {
80                    Self::EvalError(pos2, e)
81                } else {
82                    Self::EvalError(
83                        pos,
84                        format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
85                    )
86                }
87            }
88            CallError::InternalError(pos2, e) => Self::SyntaxError(
89                pos,
90                format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
91            ),
92            CallError::IoError(e) => Self::IoError(io::Error::new(
93                e.kind(),
94                format!("{}:{}: In call to {}: {}", pos.line, pos.col, md.name(), e),
95            )),
96            CallError::NestedError(e) => Self::NestedError(e),
97            CallError::SyntaxError if md.syntax().is_empty() => {
98                Self::SyntaxError(pos, format!("In call to {}: expected no arguments", md.name()))
99            }
100            CallError::SyntaxError => Self::SyntaxError(
101                pos,
102                format!("In call to {}: expected {}", md.name(), md.syntax()),
103            ),
104        }
105    }
106
107    /// Annotates a value computation error with a position.
108    fn from_value_error(e: value::Error, pos: LineCol) -> Self {
109        Self::EvalError(pos, e.message)
110    }
111
112    /// Creates a position-less value computation error.
113    // TODO(jmmv): This only exists because of some public operations in `Machine` that operate
114    // "out of band".  We should probably remove those, put them elsewhere, and/or make them return
115    // a different error type rather than weakening what we store in `Error`.
116    fn from_value_error_without_pos(e: value::Error) -> Self {
117        Self::ValueError(e)
118    }
119
120    /// Returns true if this type of error can be caught by `ON ERROR`.
121    fn is_catchable(&self) -> bool {
122        match self {
123            Error::CompilerError(_) => false,
124            Error::EvalError(..) => true,
125            Error::IoError(_) => true,
126            Error::NestedError(_) => false,
127            Error::ParseError(_) => false,
128            Error::SyntaxError(..) => true,
129            Error::ValueError(_) => false,
130        }
131    }
132}
133
134/// Result for execution return values.
135pub type Result<T> = std::result::Result<T, Error>;
136
137/// Instantiates a new `Err(Error::SyntaxError(...))` from a message.  Syntactic sugar.
138fn new_syntax_error<T, S: Into<String>>(pos: LineCol, message: S) -> Result<T> {
139    Err(Error::SyntaxError(pos, message.into()))
140}
141
142/// Signals that can be delivered to the machine.
143#[derive(Clone, Debug, Eq, PartialEq)]
144pub enum Signal {
145    /// Asks the machine to stop execution of the currently-running program.
146    Break,
147}
148
149/// Describes how the machine stopped execution while it was running a script via `exec()`.
150#[derive(Clone, Debug, Eq, PartialEq)]
151#[must_use]
152pub enum StopReason {
153    /// Execution terminates because the machine reached the end of the input.
154    Eof,
155
156    /// Execution terminated because the machine was asked to terminate with `END`.
157    Exited(u8),
158
159    /// Execution terminated because the machine received a break signal.
160    Break,
161}
162
163impl StopReason {
164    /// Converts the stop reason into a process exit code.
165    pub fn as_exit_code(&self) -> i32 {
166        match self {
167            StopReason::Eof => 0,
168            StopReason::Exited(i) => *i as i32,
169            StopReason::Break => {
170                // This mimics the behavior of typical Unix shells, which translate a signal to a
171                // numerical exit code, but this is not accurate.  First, because a CTRL+C sequence
172                // should be exposed as a SIGINT signal to whichever process is waiting for us, and
173                // second because this is not meaningful on Windows.  But for now this will do.
174                const SIGINT: i32 = 2;
175                128 + SIGINT
176            }
177        }
178    }
179}
180
181/// Trait for objects that maintain state that can be reset to defaults.
182pub trait Clearable {
183    /// Resets any state held by the object to default values.  `syms` contain the symbols of the
184    /// machine before they are cleared, in case some state is held in them too.
185    fn reset_state(&self, syms: &mut Symbols);
186}
187
188/// Type of the function used by the execution loop to yield execution.
189pub type YieldNowFn = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + 'static>>>;
190
191/// Tags used in the value stack to identify the type of their corresponding value.
192pub enum ValueTag {
193    /// Represents that there is no next value to consume.  Can only appear for command invocations.
194    Missing = 0,
195
196    /// Represents that the next value to consume is a boolean.
197    Boolean = 1,
198
199    /// Represents that the next value to consume is a double.
200    Double = 2,
201
202    /// Represents that the next value to consume is an integer
203    Integer = 3,
204
205    /// Represents that the next value to consume is a string.
206    Text = 4,
207}
208
209impl From<ExprType> for ValueTag {
210    fn from(value: ExprType) -> Self {
211        match value {
212            ExprType::Boolean => ValueTag::Boolean,
213            ExprType::Double => ValueTag::Double,
214            ExprType::Integer => ValueTag::Integer,
215            ExprType::Text => ValueTag::Text,
216        }
217    }
218}
219
220/// Representation of the runtime stack.
221#[derive(Default)]
222struct Stack {
223    values: Vec<(Value, LineCol)>,
224}
225
226#[cfg(test)]
227impl<V: Into<Vec<(Value, LineCol)>>> From<V> for Stack {
228    fn from(values: V) -> Self {
229        Self { values: values.into() }
230    }
231}
232
233impl Stack {
234    /// Discards the given number of elements from the top of the stack.
235    fn discard(&mut self, n: usize) {
236        self.values.truncate(self.values.len() - n)
237    }
238
239    /// Returns the number of elements in the stack.
240    fn len(&mut self) -> usize {
241        self.values.len()
242    }
243
244    /// Pops the top of the stack.
245    fn pop(&mut self) -> Option<(Value, LineCol)> {
246        self.values.pop()
247    }
248
249    /// Pushes a new value onto the stack.
250    fn push(&mut self, value: (Value, LineCol)) {
251        self.values.push(value)
252    }
253
254    /// Pops the top of the stack as a boolean value.
255    fn pop_boolean(&mut self) -> bool {
256        self.pop_boolean_with_pos().0
257    }
258
259    /// Pops the top of the stack as a boolean value.
260    //
261    // TODO(jmmv): Remove this variant once the stack values do not carry position
262    // information any longer.
263    fn pop_boolean_with_pos(&mut self) -> (bool, LineCol) {
264        match self.values.pop() {
265            Some((Value::Boolean(b), pos)) => (b, pos),
266            Some((_, _)) => panic!("Type mismatch"),
267            _ => panic!("Not enough arguments to pop"),
268        }
269    }
270
271    /// Pushes a boolean onto the stack.
272    fn push_boolean(&mut self, b: bool, pos: LineCol) {
273        self.values.push((Value::Boolean(b), pos));
274    }
275
276    /// Pops the top of the stack as a double value.
277    #[allow(unused)]
278    fn pop_double(&mut self) -> f64 {
279        self.pop_double_with_pos().0
280    }
281
282    /// Pops the top of the stack as a double value.
283    //
284    // TODO(jmmv): Remove this variant once the stack values do not carry position
285    // information any longer.
286    fn pop_double_with_pos(&mut self) -> (f64, LineCol) {
287        match self.values.pop() {
288            Some((Value::Double(d), pos)) => (d, pos),
289            Some((_, _)) => panic!("Type mismatch"),
290            _ => panic!("Not enough arguments to pop"),
291        }
292    }
293
294    /// Pushes a double onto the stack.
295    fn push_double(&mut self, d: f64, pos: LineCol) {
296        self.values.push((Value::Double(d), pos));
297    }
298
299    /// Pops the top of the stack as an integer value.
300    fn pop_integer(&mut self) -> i32 {
301        self.pop_integer_with_pos().0
302    }
303
304    /// Pops the top of the stack as an integer value.
305    //
306    // TODO(jmmv): Remove this variant once the stack values do not carry position
307    // information any longer.
308    fn pop_integer_with_pos(&mut self) -> (i32, LineCol) {
309        match self.values.pop() {
310            Some((Value::Integer(i), pos)) => (i, pos),
311            Some((_, _)) => panic!("Type mismatch"),
312            _ => panic!("Not enough arguments to pop"),
313        }
314    }
315
316    /// Pushes an integer onto the stack.
317    fn push_integer(&mut self, i: i32, pos: LineCol) {
318        self.values.push((Value::Integer(i), pos));
319    }
320
321    /// Pops the top of the stack as a string value.
322    #[allow(unused)]
323    fn pop_string(&mut self) -> String {
324        self.pop_string_with_pos().0
325    }
326
327    /// Pops the top of the stack as a string value.
328    //
329    // TODO(jmmv): Remove this variant once the stack values do not carry position
330    // information any longer.
331    fn pop_string_with_pos(&mut self) -> (String, LineCol) {
332        match self.values.pop() {
333            Some((Value::Text(s), pos)) => (s, pos),
334            Some((_, _)) => panic!("Type mismatch"),
335            _ => panic!("Not enough arguments to pop"),
336        }
337    }
338
339    /// Pushes a string onto the stack.
340    fn push_string(&mut self, s: String, pos: LineCol) {
341        self.values.push((Value::Text(s), pos));
342    }
343
344    /// Pops the top of the stack as a variable reference.
345    #[allow(unused)]
346    fn pop_varref(&mut self) -> (SymbolKey, ExprType) {
347        let (key, etype, _pos) = self.pop_varref_with_pos();
348        (key, etype)
349    }
350
351    /// Pops the top of the stack as a variable reference.
352    //
353    // TODO(jmmv): Remove this variant once the stack values do not carry position
354    // information any longer.
355    fn pop_varref_with_pos(&mut self) -> (SymbolKey, ExprType, LineCol) {
356        match self.values.pop() {
357            Some((Value::VarRef(key, etype), pos)) => (key, etype, pos),
358            Some((_, _)) => panic!("Type mismatch"),
359            _ => panic!("Not enough arguments to pop"),
360        }
361    }
362
363    /// Pushes a variable reference onto the stack.
364    fn push_varref(&mut self, key: SymbolKey, etype: ExprType, pos: LineCol) {
365        self.values.push((Value::VarRef(key, etype), pos));
366    }
367
368    /// Peeks into the top of the stack.
369    fn top(&self) -> Option<&(Value, LineCol)> {
370        self.values.last()
371    }
372}
373
374/// Provides controlled access to the parameters passed to a callable.
375pub struct Scope<'s> {
376    stack: &'s mut Stack,
377    nargs: usize,
378    fref_pos: LineCol,
379}
380
381impl<'s> Drop for Scope<'s> {
382    fn drop(&mut self) {
383        self.stack.discard(self.nargs);
384    }
385}
386
387impl<'s> Scope<'s> {
388    /// Creates a new scope that wraps `nargs` arguments at the top of `stack`.
389    fn new(stack: &'s mut Stack, nargs: usize, fref_pos: LineCol) -> Self {
390        debug_assert!(nargs <= stack.len());
391        Self { stack, nargs, fref_pos }
392    }
393
394    fn drain(&mut self) {
395        self.stack.discard(self.nargs);
396        self.nargs = 0;
397    }
398
399    /// Returns the number of arguments that can still be consumed.
400    pub fn nargs(&self) -> usize {
401        self.nargs
402    }
403
404    /// Pops the top of the stack as a boolean value.
405    pub fn pop_boolean(&mut self) -> bool {
406        self.pop_boolean_with_pos().0
407    }
408
409    /// Pops the top of the stack as a boolean value.
410    //
411    // TODO(jmmv): Remove this variant once the stack values do not carry position
412    // information any longer.
413    pub fn pop_boolean_with_pos(&mut self) -> (bool, LineCol) {
414        debug_assert!(self.nargs > 0, "Not enough arguments in scope");
415        self.nargs -= 1;
416        self.stack.pop_boolean_with_pos()
417    }
418
419    /// Pops the top of the stack as a double value.
420    pub fn pop_double(&mut self) -> f64 {
421        self.pop_double_with_pos().0
422    }
423
424    /// Pops the top of the stack as a double value.
425    //
426    // TODO(jmmv): Remove this variant once the stack values do not carry position
427    // information any longer.
428    pub fn pop_double_with_pos(&mut self) -> (f64, LineCol) {
429        debug_assert!(self.nargs > 0, "Not enough arguments in scope");
430        self.nargs -= 1;
431        self.stack.pop_double_with_pos()
432    }
433
434    /// Pops the top of the stack as an integer value.
435    pub fn pop_integer(&mut self) -> i32 {
436        self.pop_integer_with_pos().0
437    }
438
439    /// Pops the top of the stack as an integer value.
440    //
441    // TODO(jmmv): Remove this variant once the stack values do not carry position
442    // information any longer.
443    pub fn pop_integer_with_pos(&mut self) -> (i32, LineCol) {
444        debug_assert!(self.nargs > 0, "Not enough arguments in scope");
445        self.nargs -= 1;
446        self.stack.pop_integer_with_pos()
447    }
448
449    /// Pops the top of the stack as a separator tag.
450    pub fn pop_sep_tag(&mut self) -> ArgSep {
451        const END_I32: i32 = ArgSep::End as i32;
452        const SHORT_I32: i32 = ArgSep::Short as i32;
453        const LONG_I32: i32 = ArgSep::Long as i32;
454        const AS_I32: i32 = ArgSep::As as i32;
455
456        match self.pop_integer() {
457            END_I32 => ArgSep::End,
458            SHORT_I32 => ArgSep::Short,
459            LONG_I32 => ArgSep::Long,
460            AS_I32 => ArgSep::As,
461            _ => unreachable!(),
462        }
463    }
464
465    /// Pops the top of the stack as a string value.
466    pub fn pop_string(&mut self) -> String {
467        self.pop_string_with_pos().0
468    }
469
470    /// Pops the top of the stack as a string value.
471    //
472    // TODO(jmmv): Remove this variant once the stack values do not carry position
473    // information any longer.
474    pub fn pop_string_with_pos(&mut self) -> (String, LineCol) {
475        debug_assert!(self.nargs > 0, "Not enough arguments in scope");
476        self.nargs -= 1;
477        self.stack.pop_string_with_pos()
478    }
479
480    /// Pops the top of the stack as a value tag.
481    pub fn pop_value_tag(&mut self) -> ValueTag {
482        const MISSING_I32: i32 = ValueTag::Missing as i32;
483        const BOOLEAN_I32: i32 = ValueTag::Boolean as i32;
484        const DOUBLE_I32: i32 = ValueTag::Double as i32;
485        const INTEGER_I32: i32 = ValueTag::Integer as i32;
486        const TEXT_I32: i32 = ValueTag::Text as i32;
487
488        match self.pop_integer() {
489            MISSING_I32 => ValueTag::Missing,
490            BOOLEAN_I32 => ValueTag::Boolean,
491            DOUBLE_I32 => ValueTag::Double,
492            INTEGER_I32 => ValueTag::Integer,
493            TEXT_I32 => ValueTag::Text,
494            _ => unreachable!(),
495        }
496    }
497
498    /// Pops the top of the stack as a variable reference.
499    pub fn pop_varref(&mut self) -> (SymbolKey, ExprType) {
500        let (key, etype, _pos) = self.pop_varref_with_pos();
501        (key, etype)
502    }
503
504    /// Pops the top of the stack as a variable reference.
505    //
506    // TODO(jmmv): Remove this variant once the stack values do not carry position
507    // information any longer.
508    pub fn pop_varref_with_pos(&mut self) -> (SymbolKey, ExprType, LineCol) {
509        debug_assert!(self.nargs > 0, "Not enough arguments in scope");
510        self.nargs -= 1;
511        self.stack.pop_varref_with_pos()
512    }
513
514    /// Sets the return value of this function to `value`.
515    pub fn return_any(mut self, value: Value) -> CallResult {
516        self.drain();
517        self.stack.push((value, self.fref_pos));
518        Ok(())
519    }
520
521    /// Sets the return value of this function to the boolean `value`.
522    pub fn return_boolean(mut self, value: bool) -> CallResult {
523        self.drain();
524        self.stack.push((Value::Boolean(value), self.fref_pos));
525        Ok(())
526    }
527
528    /// Sets the return value of this function to the double `value`.
529    pub fn return_double(mut self, value: f64) -> CallResult {
530        self.drain();
531        self.stack.push((Value::Double(value), self.fref_pos));
532        Ok(())
533    }
534
535    /// Sets the return value of this function to the integer `value`.
536    pub fn return_integer(mut self, value: i32) -> CallResult {
537        self.drain();
538        self.stack.push((Value::Integer(value), self.fref_pos));
539        Ok(())
540    }
541
542    /// Sets the return value of this function to the string `value`.
543    pub fn return_string<S: Into<String>>(mut self, value: S) -> CallResult {
544        self.drain();
545        self.stack.push((Value::Text(value.into()), self.fref_pos));
546        Ok(())
547    }
548}
549
550/// Machine state for the execution of an individual chunk of code.
551struct Context {
552    pc: Address,
553    addr_stack: Vec<Address>,
554    value_stack: Stack,
555    err_handler: ErrorHandlerISpan,
556}
557
558impl Default for Context {
559    fn default() -> Self {
560        Self {
561            pc: 0,
562            addr_stack: vec![],
563            value_stack: Stack::default(),
564            err_handler: ErrorHandlerISpan::None,
565        }
566    }
567}
568
569/// Executes an EndBASIC program and tracks its state.
570pub struct Machine {
571    symbols: Symbols,
572    clearables: Vec<Box<dyn Clearable>>,
573    yield_now_fn: Option<YieldNowFn>,
574    signals_chan: (Sender<Signal>, Receiver<Signal>),
575    check_stop: bool,
576    stop_reason: Option<StopReason>,
577    last_error: Option<String>,
578    data: Vec<Option<Value>>,
579}
580
581impl Default for Machine {
582    fn default() -> Self {
583        Self::with_signals_chan_and_yield_now_fn(async_channel::unbounded(), None)
584    }
585}
586
587impl Machine {
588    /// Constructs a new empty machine with the given signals communication channel.
589    pub fn with_signals_chan(signals: (Sender<Signal>, Receiver<Signal>)) -> Self {
590        Self::with_signals_chan_and_yield_now_fn(signals, None)
591    }
592
593    /// Constructs a new empty machine with the given signals communication channel and yielding
594    /// function.
595    pub fn with_signals_chan_and_yield_now_fn(
596        signals: (Sender<Signal>, Receiver<Signal>),
597        yield_now_fn: Option<YieldNowFn>,
598    ) -> Self {
599        Self {
600            symbols: Symbols::default(),
601            clearables: vec![],
602            yield_now_fn,
603            signals_chan: signals,
604            check_stop: false,
605            stop_reason: None,
606            last_error: None,
607            data: vec![],
608        }
609    }
610
611    /// Registers the given clearable.
612    ///
613    /// In the common case, functions and commands hold a reference to the out-of-machine state
614    /// they interact with.  This state is invisible from here, but we may need to have access
615    /// to it to reset it as part of the `clear` operation.  In those cases, such state must be
616    /// registered via this hook.
617    pub fn add_clearable(&mut self, clearable: Box<dyn Clearable>) {
618        self.clearables.push(clearable);
619    }
620
621    /// Registers the given builtin callable, which must not yet be registered.
622    pub fn add_callable(&mut self, callable: Rc<dyn Callable>) {
623        self.symbols.add_callable(callable)
624    }
625
626    /// Obtains a channel via which to send signals to the machine during execution.
627    pub fn get_signals_tx(&self) -> Sender<Signal> {
628        self.signals_chan.0.clone()
629    }
630
631    /// Resets the state of the machine by clearing all variable.
632    pub fn clear(&mut self) {
633        for clearable in self.clearables.as_slice() {
634            clearable.reset_state(&mut self.symbols);
635        }
636        self.symbols.clear();
637        self.last_error = None;
638    }
639
640    /// Returns the last execution error.
641    pub fn last_error(&self) -> Option<&str> {
642        self.last_error.as_deref()
643    }
644
645    /// Obtains immutable access to the data values available during the *current* execution.
646    pub fn get_data(&self) -> &[Option<Value>] {
647        &self.data
648    }
649
650    /// Obtains immutable access to the state of the symbols.
651    pub fn get_symbols(&self) -> &Symbols {
652        &self.symbols
653    }
654
655    /// Obtains mutable access to the state of the symbols.
656    pub fn get_mut_symbols(&mut self) -> &mut Symbols {
657        &mut self.symbols
658    }
659
660    /// Retrieves the variable `name` as a boolean.  Fails if it is some other type or if it's not
661    /// defined.
662    pub fn get_var_as_bool(&self, name: &str) -> Result<bool> {
663        match self
664            .symbols
665            .get_var(&VarRef::new(name, Some(ExprType::Boolean)))
666            .map_err(Error::from_value_error_without_pos)?
667        {
668            Value::Boolean(b) => Ok(*b),
669            _ => panic!("Invalid type check in get()"),
670        }
671    }
672
673    /// Retrieves the variable `name` as an integer.  Fails if it is some other type or if it's not
674    /// defined.
675    pub fn get_var_as_int(&self, name: &str) -> Result<i32> {
676        match self
677            .symbols
678            .get_var(&VarRef::new(name, Some(ExprType::Integer)))
679            .map_err(Error::from_value_error_without_pos)?
680        {
681            Value::Integer(i) => Ok(*i),
682            _ => panic!("Invalid type check in get()"),
683        }
684    }
685
686    /// Retrieves the variable `name` as a string.  Fails if it is some other type or if it's not
687    /// defined.
688    pub fn get_var_as_string(&self, name: &str) -> Result<&str> {
689        match self
690            .symbols
691            .get_var(&VarRef::new(name, Some(ExprType::Text)))
692            .map_err(Error::from_value_error_without_pos)?
693        {
694            Value::Text(s) => Ok(s),
695            _ => panic!("Invalid type check in get()"),
696        }
697    }
698
699    /// Returns true if execution should stop because we have hit a stop condition.
700    async fn should_stop(&mut self) -> bool {
701        if let Some(yield_now) = self.yield_now_fn.as_ref() {
702            (yield_now)().await;
703        }
704
705        match self.signals_chan.1.try_recv() {
706            Ok(Signal::Break) => {
707                self.check_stop = true;
708                self.stop_reason = Some(StopReason::Break)
709            }
710            Err(TryRecvError::Empty) => (),
711            Err(TryRecvError::Closed) => panic!("Channel unexpectedly closed"),
712        }
713
714        self.stop_reason.is_some()
715    }
716
717    /// Handles an array assignment.
718    fn assign_array(
719        &mut self,
720        context: &mut Context,
721        key: &SymbolKey,
722        vref_pos: LineCol,
723        nargs: usize,
724    ) -> Result<()> {
725        let mut ds = Vec::with_capacity(nargs);
726        for _ in 0..nargs {
727            let i = context.value_stack.pop_integer();
728            ds.push(i);
729        }
730
731        let (value, _pos) = context.value_stack.pop().unwrap();
732
733        match self.symbols.load_mut(key) {
734            Some(Symbol::Array(array)) => {
735                array.assign(&ds, value).map_err(|e| Error::from_value_error(e, vref_pos))?;
736                Ok(())
737            }
738            _ => unreachable!("Array existence and type checking has been done at compile time"),
739        }
740    }
741
742    /// Handles a builtin call.
743    async fn builtin_call(
744        &mut self,
745        context: &mut Context,
746        name: &SymbolKey,
747        bref_pos: LineCol,
748        nargs: usize,
749    ) -> Result<()> {
750        let b = match self.symbols.load(name) {
751            Some(Symbol::Callable(b)) => b,
752            _ => panic!("Command existence and type checking happen at compile time"),
753        };
754
755        let metadata = b.metadata();
756        debug_assert!(!metadata.is_function());
757
758        let scope = Scope::new(&mut context.value_stack, nargs, bref_pos);
759
760        let b = b.clone();
761        b.exec(scope, self).await.map_err(|e| Error::from_call_error(b.metadata(), e, bref_pos))?;
762        Ok(())
763    }
764
765    /// Handles an array definition.  The array must not yet exist, and the name may not overlap
766    /// function or variable names.
767    fn dim_array(&mut self, context: &mut Context, span: &DimArrayISpan) -> Result<()> {
768        let mut ds = Vec::with_capacity(span.dimensions);
769        for _ in 0..span.dimensions {
770            let (i, pos) = context.value_stack.pop_integer_with_pos();
771            if i <= 0 {
772                return new_syntax_error(pos, "Dimensions in DIM array must be positive");
773            }
774            ds.push(i as usize);
775        }
776        if span.shared {
777            self.symbols.dim_shared_array(span.name.clone(), span.subtype, ds);
778        } else {
779            self.symbols.dim_array(span.name.clone(), span.subtype, ds);
780        }
781        Ok(())
782    }
783
784    /// Consumes any pending signals so that they don't interfere with an upcoming execution.
785    pub fn drain_signals(&mut self) {
786        while self.signals_chan.1.try_recv().is_ok() {
787            // Do nothing.
788        }
789    }
790
791    /// Tells the machine to stop execution at the next statement boundary.
792    fn end(&mut self, context: &mut Context, has_code: bool) -> Result<()> {
793        let code = if has_code {
794            let (code, code_pos) = context.value_stack.pop_integer_with_pos();
795            if code < 0 {
796                return new_syntax_error(
797                    code_pos,
798                    "Exit code must be a positive integer".to_owned(),
799                );
800            }
801            if code >= 128 {
802                return new_syntax_error(
803                    code_pos,
804                    "Exit code cannot be larger than 127".to_owned(),
805                );
806            }
807            code as u8
808        } else {
809            0
810        };
811        self.check_stop = true;
812        self.stop_reason = Some(StopReason::Exited(code));
813        Ok(())
814    }
815
816    /// Handles a unary logical operator that cannot fail.
817    fn exec_logical_op1<F: Fn(bool) -> bool>(context: &mut Context, op: F, pos: LineCol) {
818        let rhs = context.value_stack.pop_boolean();
819        context.value_stack.push_boolean(op(rhs), pos);
820    }
821
822    /// Handles a binary logical operator that cannot fail.
823    fn exec_logical_op2<F: Fn(bool, bool) -> bool>(context: &mut Context, op: F, pos: LineCol) {
824        let rhs = context.value_stack.pop_boolean();
825        let lhs = context.value_stack.pop_boolean();
826        context.value_stack.push_boolean(op(lhs, rhs), pos);
827    }
828
829    /// Handles a unary bitwise operator that cannot fail.
830    fn exec_bitwise_op1<F: Fn(i32) -> i32>(context: &mut Context, op: F, pos: LineCol) {
831        let rhs = context.value_stack.pop_integer();
832        context.value_stack.push_integer(op(rhs), pos);
833    }
834
835    /// Handles a binary bitwise operator that cannot fail.
836    fn exec_bitwise_op2<F: Fn(i32, i32) -> i32>(context: &mut Context, op: F, pos: LineCol) {
837        let rhs = context.value_stack.pop_integer();
838        let lhs = context.value_stack.pop_integer();
839        context.value_stack.push_integer(op(lhs, rhs), pos);
840    }
841
842    /// Handles a binary bitwise operator that can fail.
843    fn exec_bitwise_op2_err<F: Fn(i32, i32) -> value::Result<i32>>(
844        context: &mut Context,
845        op: F,
846        pos: LineCol,
847    ) -> Result<()> {
848        let rhs = context.value_stack.pop_integer();
849        let lhs = context.value_stack.pop_integer();
850        let result = op(lhs, rhs).map_err(|e| Error::from_value_error(e, pos))?;
851        context.value_stack.push_integer(result, pos);
852        Ok(())
853    }
854
855    /// Handles a binary equality operator for booleans that cannot fail.
856    fn exec_equality_boolean_op2<F: Fn(bool, bool) -> bool>(
857        context: &mut Context,
858        op: F,
859        pos: LineCol,
860    ) {
861        let rhs = context.value_stack.pop_boolean();
862        let lhs = context.value_stack.pop_boolean();
863        context.value_stack.push_boolean(op(lhs, rhs), pos);
864    }
865
866    /// Handles a binary equality operator for doubles that cannot fail.
867    fn exec_equality_double_op2<F: Fn(f64, f64) -> bool>(
868        context: &mut Context,
869        op: F,
870        pos: LineCol,
871    ) {
872        let rhs = context.value_stack.pop_double();
873        let lhs = context.value_stack.pop_double();
874        context.value_stack.push_boolean(op(lhs, rhs), pos);
875    }
876
877    /// Handles a binary equality operator for integers that cannot fail.
878    fn exec_equality_integer_op2<F: Fn(i32, i32) -> bool>(
879        context: &mut Context,
880        op: F,
881        pos: LineCol,
882    ) {
883        let rhs = context.value_stack.pop_integer();
884        let lhs = context.value_stack.pop_integer();
885        context.value_stack.push_boolean(op(lhs, rhs), pos);
886    }
887
888    /// Handles a binary equality operator for strings that cannot fail.
889    fn exec_equality_string_op2<F: Fn(&str, &str) -> bool>(
890        context: &mut Context,
891        op: F,
892        pos: LineCol,
893    ) {
894        let rhs = context.value_stack.pop_string();
895        let lhs = context.value_stack.pop_string();
896        context.value_stack.push_boolean(op(&lhs, &rhs), pos);
897    }
898
899    /// Handles a unary arithmetic operator for doubles that cannot fail.
900    fn exec_arithmetic_double_op1<F: Fn(f64) -> f64>(context: &mut Context, op: F, pos: LineCol) {
901        let rhs = context.value_stack.pop_double();
902        context.value_stack.push_double(op(rhs), pos);
903    }
904
905    /// Handles a binary arithmetic operator for doubles that cannot fail.
906    fn exec_arithmetic_double_op2<F: Fn(f64, f64) -> f64>(
907        context: &mut Context,
908        op: F,
909        pos: LineCol,
910    ) {
911        let rhs = context.value_stack.pop_double();
912        let lhs = context.value_stack.pop_double();
913        context.value_stack.push_double(op(lhs, rhs), pos);
914    }
915
916    /// Handles a unary arithmetic operator for doubles that can fail.
917    fn exec_arithmetic_integer_op1<F: Fn(i32) -> value::Result<i32>>(
918        context: &mut Context,
919        op: F,
920        pos: LineCol,
921    ) -> Result<()> {
922        let rhs = context.value_stack.pop_integer();
923        let result = op(rhs).map_err(|e| Error::from_value_error(e, pos))?;
924        context.value_stack.push_integer(result, pos);
925        Ok(())
926    }
927
928    /// Handles a binary arithmetic operator for doubles that can fail.
929    fn exec_arithmetic_integer_op2<F: Fn(i32, i32) -> value::Result<i32>>(
930        context: &mut Context,
931        op: F,
932        pos: LineCol,
933    ) -> Result<()> {
934        let rhs = context.value_stack.pop_integer();
935        let lhs = context.value_stack.pop_integer();
936        let result = op(lhs, rhs).map_err(|e| Error::from_value_error(e, pos))?;
937        context.value_stack.push_integer(result, pos);
938        Ok(())
939    }
940
941    /// Handles a binary arithmetic operator for doubles that cannot fail.
942    fn exec_arithmetic_string_op2<F: Fn(&str, &str) -> String>(
943        context: &mut Context,
944        op: F,
945        pos: LineCol,
946    ) {
947        let rhs = context.value_stack.pop_string();
948        let lhs = context.value_stack.pop_string();
949        context.value_stack.push_string(op(&lhs, &rhs), pos);
950    }
951
952    /// Evaluates the subscripts of an array reference.
953    fn get_array_args(&self, context: &mut Context, nargs: usize) -> Result<Vec<i32>> {
954        let mut subscripts = Vec::with_capacity(nargs);
955        for _ in 0..nargs {
956            let i = context.value_stack.pop_integer();
957            subscripts.push(i);
958        }
959        Ok(subscripts)
960    }
961
962    /// Evaluates a function call specified by `fref` and arguments `args` on the function `f`.
963    async fn do_function_call(
964        &mut self,
965        context: &mut Context,
966        return_type: ExprType,
967        fref_pos: LineCol,
968        nargs: usize,
969        f: Rc<dyn Callable>,
970    ) -> Result<()> {
971        let metadata = f.metadata();
972        debug_assert_eq!(return_type, metadata.return_type().unwrap());
973
974        let scope = Scope::new(&mut context.value_stack, nargs, fref_pos);
975        f.exec(scope, self).await.map_err(|e| Error::from_call_error(metadata, e, fref_pos))?;
976        if cfg!(debug_assertions) {
977            match context.value_stack.top() {
978                Some((value, _pos)) => {
979                    debug_assert_eq!(
980                        return_type,
981                        value.as_exprtype(),
982                        "Value returned by function is incompatible with its type definition",
983                    )
984                }
985                None => unreachable!("Functions must return one value"),
986            }
987        }
988        Ok(())
989    }
990
991    /// Handles an array reference.
992    fn array_ref(
993        &mut self,
994        context: &mut Context,
995        key: &SymbolKey,
996        vref_pos: LineCol,
997        nargs: usize,
998    ) -> Result<()> {
999        let subscripts = self.get_array_args(context, nargs)?;
1000        match self.symbols.load(key) {
1001            Some(Symbol::Array(array)) => {
1002                let value = array
1003                    .index(&subscripts)
1004                    .cloned()
1005                    .map_err(|e| Error::from_value_error(e, vref_pos))?;
1006                context.value_stack.push((value, vref_pos));
1007                Ok(())
1008            }
1009            Some(_) => unreachable!("Array type checking has been done at compile time"),
1010            None => Err(Error::EvalError(vref_pos, format!("{} is not defined", key))),
1011        }
1012    }
1013
1014    /// Handles a function call.
1015    async fn function_call(
1016        &mut self,
1017        context: &mut Context,
1018        name: &SymbolKey,
1019        return_type: ExprType,
1020        fref_pos: LineCol,
1021        nargs: usize,
1022    ) -> Result<()> {
1023        match self.symbols.load(name) {
1024            Some(Symbol::Callable(f)) => {
1025                if !f.metadata().is_function() {
1026                    return Err(Error::EvalError(
1027                        fref_pos,
1028                        format!("{} is not an array nor a function", f.metadata().name()),
1029                    ));
1030                }
1031                let f = f.clone();
1032                self.do_function_call(context, return_type, fref_pos, nargs, f).await
1033            }
1034            _ => unreachable!("Function existence and type checking has been done at compile time"),
1035        }
1036    }
1037
1038    /// Evaluates a call to an argless function.
1039    async fn argless_function_call(
1040        &mut self,
1041        context: &mut Context,
1042        fname: &SymbolKey,
1043        ftype: ExprType,
1044        fpos: LineCol,
1045        f: Rc<dyn Callable>,
1046    ) -> Result<()> {
1047        let metadata = f.metadata();
1048        let scope = Scope::new(&mut context.value_stack, 0, fpos);
1049        f.exec(scope, self).await.map_err(|e| Error::from_call_error(metadata, e, fpos))?;
1050        if cfg!(debug_assertions) {
1051            match context.value_stack.top() {
1052                Some((value, _pos)) => {
1053                    let fref_checker = VarRef::new(fname.to_string(), Some(ftype));
1054                    debug_assert!(
1055                        fref_checker.accepts(value.as_exprtype()),
1056                        "Value returned by function is incompatible with its type definition",
1057                    )
1058                }
1059                None => unreachable!("Functions must return one value"),
1060            }
1061        }
1062        Ok(())
1063    }
1064
1065    /// Loads the value of a symbol.
1066    fn load(&self, key: &SymbolKey, pos: LineCol) -> Result<&Value> {
1067        match self.symbols.load(key) {
1068            Some(Symbol::Variable(v)) => Ok(v),
1069            Some(_) => unreachable!("Variable type checking has been done at compile time"),
1070            None => new_syntax_error(pos, format!("Undefined variable {}", key)),
1071        }
1072    }
1073
1074    /// Helper for `exec_one` that only worries about execution of a single instruction.
1075    ///
1076    /// Errors are handled on the caller side depending on the `ON ERROR` handling policy that is
1077    /// currently configured.
1078    async fn exec_safe(&mut self, context: &mut Context, instrs: &[Instruction]) -> Result<()> {
1079        let instr = &instrs[context.pc];
1080        match instr {
1081            Instruction::LogicalAnd(pos) => {
1082                Machine::exec_logical_op2(context, |lhs, rhs| lhs && rhs, *pos);
1083                context.pc += 1;
1084            }
1085
1086            Instruction::LogicalOr(pos) => {
1087                Machine::exec_logical_op2(context, |lhs, rhs| lhs || rhs, *pos);
1088                context.pc += 1;
1089            }
1090
1091            Instruction::LogicalXor(pos) => {
1092                Machine::exec_logical_op2(context, |lhs, rhs| lhs ^ rhs, *pos);
1093                context.pc += 1;
1094            }
1095
1096            Instruction::LogicalNot(pos) => {
1097                Machine::exec_logical_op1(context, |rhs| !rhs, *pos);
1098                context.pc += 1;
1099            }
1100
1101            Instruction::BitwiseAnd(pos) => {
1102                Machine::exec_bitwise_op2(context, |lhs, rhs| lhs & rhs, *pos);
1103                context.pc += 1;
1104            }
1105
1106            Instruction::BitwiseOr(pos) => {
1107                Machine::exec_bitwise_op2(context, |lhs, rhs| lhs | rhs, *pos);
1108                context.pc += 1;
1109            }
1110
1111            Instruction::BitwiseXor(pos) => {
1112                Machine::exec_bitwise_op2(context, |lhs, rhs| lhs ^ rhs, *pos);
1113                context.pc += 1;
1114            }
1115
1116            Instruction::BitwiseNot(pos) => {
1117                Machine::exec_bitwise_op1(context, |rhs| !rhs, *pos);
1118                context.pc += 1;
1119            }
1120
1121            Instruction::ShiftLeft(pos) => {
1122                Machine::exec_bitwise_op2_err(context, value::bitwise_shl, *pos)?;
1123                context.pc += 1;
1124            }
1125
1126            Instruction::ShiftRight(pos) => {
1127                Machine::exec_bitwise_op2_err(context, value::bitwise_shr, *pos)?;
1128                context.pc += 1;
1129            }
1130
1131            Instruction::EqualBooleans(pos) => {
1132                Machine::exec_equality_boolean_op2(context, |lhs, rhs| lhs == rhs, *pos);
1133                context.pc += 1;
1134            }
1135
1136            Instruction::NotEqualBooleans(pos) => {
1137                Machine::exec_equality_boolean_op2(context, |lhs, rhs| lhs != rhs, *pos);
1138                context.pc += 1;
1139            }
1140
1141            Instruction::EqualDoubles(pos) => {
1142                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs == rhs, *pos);
1143                context.pc += 1;
1144            }
1145
1146            Instruction::NotEqualDoubles(pos) => {
1147                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs != rhs, *pos);
1148                context.pc += 1;
1149            }
1150
1151            Instruction::LessDoubles(pos) => {
1152                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs < rhs, *pos);
1153                context.pc += 1;
1154            }
1155
1156            Instruction::LessEqualDoubles(pos) => {
1157                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs <= rhs, *pos);
1158                context.pc += 1;
1159            }
1160
1161            Instruction::GreaterDoubles(pos) => {
1162                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs > rhs, *pos);
1163                context.pc += 1;
1164            }
1165
1166            Instruction::GreaterEqualDoubles(pos) => {
1167                Machine::exec_equality_double_op2(context, |lhs, rhs| lhs >= rhs, *pos);
1168                context.pc += 1;
1169            }
1170
1171            Instruction::EqualIntegers(pos) => {
1172                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs == rhs, *pos);
1173                context.pc += 1;
1174            }
1175
1176            Instruction::NotEqualIntegers(pos) => {
1177                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs != rhs, *pos);
1178                context.pc += 1;
1179            }
1180
1181            Instruction::LessIntegers(pos) => {
1182                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs < rhs, *pos);
1183                context.pc += 1;
1184            }
1185
1186            Instruction::LessEqualIntegers(pos) => {
1187                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs <= rhs, *pos);
1188                context.pc += 1;
1189            }
1190
1191            Instruction::GreaterIntegers(pos) => {
1192                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs > rhs, *pos);
1193                context.pc += 1;
1194            }
1195
1196            Instruction::GreaterEqualIntegers(pos) => {
1197                Machine::exec_equality_integer_op2(context, |lhs, rhs| lhs >= rhs, *pos);
1198                context.pc += 1;
1199            }
1200
1201            Instruction::EqualStrings(pos) => {
1202                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs == rhs, *pos);
1203                context.pc += 1;
1204            }
1205
1206            Instruction::NotEqualStrings(pos) => {
1207                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs != rhs, *pos);
1208                context.pc += 1;
1209            }
1210
1211            Instruction::LessStrings(pos) => {
1212                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs < rhs, *pos);
1213                context.pc += 1;
1214            }
1215
1216            Instruction::LessEqualStrings(pos) => {
1217                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs <= rhs, *pos);
1218                context.pc += 1;
1219            }
1220
1221            Instruction::GreaterStrings(pos) => {
1222                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs > rhs, *pos);
1223                context.pc += 1;
1224            }
1225
1226            Instruction::GreaterEqualStrings(pos) => {
1227                Machine::exec_equality_string_op2(context, |lhs, rhs| lhs >= rhs, *pos);
1228                context.pc += 1;
1229            }
1230
1231            Instruction::AddDoubles(pos) => {
1232                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs + rhs, *pos);
1233                context.pc += 1;
1234            }
1235
1236            Instruction::SubtractDoubles(pos) => {
1237                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs - rhs, *pos);
1238                context.pc += 1;
1239            }
1240
1241            Instruction::MultiplyDoubles(pos) => {
1242                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs * rhs, *pos);
1243                context.pc += 1;
1244            }
1245
1246            Instruction::DivideDoubles(pos) => {
1247                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs / rhs, *pos);
1248                context.pc += 1;
1249            }
1250
1251            Instruction::ModuloDoubles(pos) => {
1252                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs % rhs, *pos);
1253                context.pc += 1;
1254            }
1255
1256            Instruction::PowerDoubles(pos) => {
1257                Machine::exec_arithmetic_double_op2(context, |lhs, rhs| lhs.powf(rhs), *pos);
1258                context.pc += 1;
1259            }
1260
1261            Instruction::NegateDouble(pos) => {
1262                Machine::exec_arithmetic_double_op1(context, |rhs| -rhs, *pos);
1263                context.pc += 1;
1264            }
1265
1266            Instruction::AddIntegers(pos) => {
1267                Machine::exec_arithmetic_integer_op2(context, value::add_integer, *pos)?;
1268                context.pc += 1;
1269            }
1270
1271            Instruction::SubtractIntegers(pos) => {
1272                Machine::exec_arithmetic_integer_op2(context, value::sub_integer, *pos)?;
1273                context.pc += 1;
1274            }
1275
1276            Instruction::MultiplyIntegers(pos) => {
1277                Machine::exec_arithmetic_integer_op2(context, value::mul_integer, *pos)?;
1278                context.pc += 1;
1279            }
1280
1281            Instruction::DivideIntegers(pos) => {
1282                Machine::exec_arithmetic_integer_op2(context, value::div_integer, *pos)?;
1283                context.pc += 1;
1284            }
1285
1286            Instruction::ModuloIntegers(pos) => {
1287                Machine::exec_arithmetic_integer_op2(context, value::modulo_integer, *pos)?;
1288                context.pc += 1;
1289            }
1290
1291            Instruction::PowerIntegers(pos) => {
1292                Machine::exec_arithmetic_integer_op2(context, value::pow_integer, *pos)?;
1293                context.pc += 1;
1294            }
1295
1296            Instruction::NegateInteger(pos) => {
1297                Machine::exec_arithmetic_integer_op1(context, value::neg_integer, *pos)?;
1298                context.pc += 1;
1299            }
1300
1301            Instruction::ConcatStrings(pos) => {
1302                Machine::exec_arithmetic_string_op2(context, |lhs, rhs| lhs.to_owned() + rhs, *pos);
1303                context.pc += 1;
1304            }
1305
1306            Instruction::Assign(key) => {
1307                let (value, _pos) = context.value_stack.pop().unwrap();
1308                self.symbols.assign(key, value);
1309                context.pc += 1;
1310            }
1311
1312            Instruction::ArrayAssignment(name, vref_pos, nargs) => {
1313                self.assign_array(context, name, *vref_pos, *nargs)?;
1314                context.pc += 1;
1315            }
1316
1317            Instruction::ArrayLoad(name, pos, nargs) => {
1318                self.array_ref(context, name, *pos, *nargs)?;
1319                context.pc += 1;
1320            }
1321
1322            Instruction::BuiltinCall(name, bref_pos, nargs) => {
1323                self.builtin_call(context, name, *bref_pos, *nargs).await?;
1324                context.pc += 1;
1325            }
1326
1327            Instruction::Call(span) => {
1328                context.addr_stack.push(context.pc + 1);
1329                context.pc = span.addr;
1330            }
1331
1332            Instruction::DoubleToInteger => {
1333                let (d, pos) = context.value_stack.pop_double_with_pos();
1334                let i =
1335                    double_to_integer(d.round()).map_err(|e| Error::from_value_error(e, pos))?;
1336                context.value_stack.push_integer(i, pos);
1337                context.pc += 1;
1338            }
1339
1340            Instruction::FunctionCall(name, return_type, pos, nargs) => {
1341                self.function_call(context, name, *return_type, *pos, *nargs).await?;
1342                context.pc += 1;
1343            }
1344
1345            Instruction::Dim(span) => {
1346                if span.shared {
1347                    self.symbols.dim_shared(span.name.clone(), span.vtype);
1348                } else {
1349                    self.symbols.dim(span.name.clone(), span.vtype);
1350                }
1351                context.pc += 1;
1352            }
1353
1354            Instruction::DimArray(span) => {
1355                self.dim_array(context, span)?;
1356                context.pc += 1;
1357            }
1358
1359            Instruction::End(has_code) => {
1360                self.end(context, *has_code)?;
1361            }
1362
1363            Instruction::EnterScope => {
1364                self.symbols.enter_scope();
1365                context.pc += 1;
1366            }
1367
1368            Instruction::IntegerToDouble => {
1369                let (i, pos) = context.value_stack.pop_integer_with_pos();
1370                context.value_stack.push_double(i as f64, pos);
1371                context.pc += 1;
1372            }
1373
1374            Instruction::Jump(span) => {
1375                if span.addr <= context.pc {
1376                    self.check_stop = true;
1377                }
1378                context.pc = span.addr;
1379            }
1380
1381            Instruction::JumpIfDefined(span) => {
1382                if self.symbols.load(&span.var).is_some() {
1383                    if span.addr <= context.pc {
1384                        self.check_stop = true;
1385                    }
1386                    context.pc = span.addr;
1387                } else {
1388                    context.pc += 1;
1389                }
1390            }
1391
1392            Instruction::JumpIfTrue(addr) => {
1393                let cond = context.value_stack.pop_boolean();
1394                if cond {
1395                    if *addr <= context.pc {
1396                        self.check_stop = true;
1397                    }
1398                    context.pc = *addr;
1399                } else {
1400                    context.pc += 1;
1401                }
1402            }
1403
1404            Instruction::JumpIfNotTrue(addr) => {
1405                let cond = context.value_stack.pop_boolean();
1406                if cond {
1407                    context.pc += 1;
1408                } else {
1409                    if *addr <= context.pc {
1410                        self.check_stop = true;
1411                    }
1412                    context.pc = *addr;
1413                }
1414            }
1415
1416            Instruction::LeaveScope => {
1417                self.symbols.leave_scope();
1418                context.pc += 1;
1419            }
1420
1421            Instruction::LoadBoolean(key, pos) => {
1422                let b = match self.load(key, *pos)? {
1423                    Value::Boolean(b) => b,
1424                    _ => unreachable!("Types are validated at compilation time"),
1425                };
1426                context.value_stack.push_boolean(*b, *pos);
1427                context.pc += 1;
1428            }
1429
1430            Instruction::LoadDouble(key, pos) => {
1431                let d = match self.load(key, *pos)? {
1432                    Value::Double(d) => d,
1433                    _ => unreachable!("Types are validated at compilation time"),
1434                };
1435                context.value_stack.push_double(*d, *pos);
1436                context.pc += 1;
1437            }
1438
1439            Instruction::LoadInteger(key, pos) => {
1440                let i = match self.load(key, *pos)? {
1441                    Value::Integer(i) => i,
1442                    _ => unreachable!("Types are validated at compilation time"),
1443                };
1444                context.value_stack.push_integer(*i, *pos);
1445                context.pc += 1;
1446            }
1447
1448            Instruction::LoadString(key, pos) => {
1449                let s = match self.load(key, *pos)? {
1450                    Value::Text(s) => s,
1451                    _ => unreachable!("Types are validated at compilation time"),
1452                };
1453                context.value_stack.push_string(s.clone(), *pos);
1454                context.pc += 1;
1455            }
1456
1457            Instruction::LoadRef(key, etype, pos) => {
1458                let sym = self.symbols.load(key);
1459                if let Some(Symbol::Callable(f)) = sym {
1460                    if f.metadata().is_argless() {
1461                        let f = f.clone();
1462                        self.argless_function_call(context, key, *etype, *pos, f).await?;
1463                        context.pc += 1;
1464                        return Ok(());
1465                    }
1466                };
1467
1468                context.value_stack.push_varref(key.clone(), *etype, *pos);
1469                context.pc += 1;
1470            }
1471
1472            Instruction::Nop => {
1473                context.pc += 1;
1474            }
1475
1476            Instruction::PushBoolean(value, pos) => {
1477                context.value_stack.push((Value::Boolean(*value), *pos));
1478                context.pc += 1;
1479            }
1480
1481            Instruction::PushDouble(value, pos) => {
1482                context.value_stack.push((Value::Double(*value), *pos));
1483                context.pc += 1;
1484            }
1485
1486            Instruction::PushInteger(value, pos) => {
1487                context.value_stack.push((Value::Integer(*value), *pos));
1488                context.pc += 1;
1489            }
1490
1491            Instruction::PushString(value, pos) => {
1492                context.value_stack.push((Value::Text(value.clone()), *pos));
1493                context.pc += 1;
1494            }
1495
1496            Instruction::Return(pos) => match context.addr_stack.pop() {
1497                Some(addr) => {
1498                    self.check_stop = true;
1499                    context.pc = addr
1500                }
1501                None => return new_syntax_error(*pos, "No address to return to".to_owned()),
1502            },
1503
1504            Instruction::SetErrorHandler(span) => {
1505                context.err_handler = *span;
1506                context.pc += 1;
1507            }
1508
1509            Instruction::Unset(span) => {
1510                self.symbols.unset(&span.name).expect("Should only unset variables that were set");
1511                context.pc += 1;
1512            }
1513        }
1514
1515        Ok(())
1516    }
1517
1518    /// Executes a single instruction in `instrs` as pointed to by the program counter in `context`.
1519    async fn exec_one(&mut self, context: &mut Context, instrs: &[Instruction]) -> Result<()> {
1520        let mut result = self.exec_safe(context, instrs).await;
1521        if let Err(e) = result.as_ref() {
1522            if e.is_catchable() {
1523                self.last_error = Some(format!("{}", e));
1524
1525                match context.err_handler {
1526                    ErrorHandlerISpan::Jump(addr) => {
1527                        context.pc = addr;
1528                        result = Ok(());
1529                    }
1530                    ErrorHandlerISpan::None => (),
1531                    ErrorHandlerISpan::ResumeNext => {
1532                        if instrs[context.pc].is_statement() {
1533                            context.pc += 1;
1534                        } else {
1535                            loop {
1536                                context.pc += 1;
1537                                if context.pc >= instrs.len() {
1538                                    break;
1539                                } else if instrs[context.pc].is_statement() {
1540                                    context.pc += 1;
1541                                    break;
1542                                }
1543                            }
1544                        }
1545                        result = Ok(());
1546                    }
1547                }
1548            }
1549        }
1550        result
1551    }
1552
1553    /// Executes a program extracted from the `input` readable.
1554    ///
1555    /// Note that this does not consume `self`.  As a result, it is possible to execute multiple
1556    /// different programs on the same machine, all sharing state.
1557    pub async fn exec(&mut self, input: &mut dyn io::Read) -> Result<StopReason> {
1558        debug_assert!(!self.check_stop);
1559        debug_assert!(self.stop_reason.is_none());
1560
1561        // TODO(jmmv): It should be possible to make the parser return statements one at a time and
1562        // stream them to the compiler, instead of buffering everything in a vector.
1563        let stmts = parser::parse(input)?;
1564        let image = compiler::compile(stmts, &self.symbols)?;
1565
1566        assert!(self.data.is_empty());
1567        self.data = image.data;
1568
1569        let mut context = Context::default();
1570        let mut result = Ok(());
1571        while result.is_ok() && context.pc < image.instrs.len() {
1572            debug_assert!(self.stop_reason.is_none() || self.check_stop);
1573            if self.check_stop && image.instrs[context.pc].is_statement() {
1574                if self.should_stop().await {
1575                    break;
1576                }
1577                self.check_stop = false;
1578            }
1579
1580            result = self.exec_one(&mut context, &image.instrs).await;
1581        }
1582
1583        self.data.clear();
1584        result?;
1585
1586        self.check_stop = false;
1587        Ok(self.stop_reason.take().unwrap_or(StopReason::Eof))
1588    }
1589}
1590
1591#[cfg(test)]
1592mod tests {
1593    use super::*;
1594    use crate::testutils::*;
1595    use futures_lite::future::block_on;
1596    use std::cell::RefCell;
1597    use std::rc::Rc;
1598
1599    /// A clearable that tracks whether it has been called.
1600    struct MockClearable {
1601        cleared: Rc<RefCell<bool>>,
1602    }
1603
1604    impl Clearable for MockClearable {
1605        fn reset_state(&self, syms: &mut Symbols) {
1606            // Make sure we can see the symbols before they are cleared.
1607            assert!(syms.get_var(&VarRef::new("a", Some(ExprType::Boolean))).is_ok());
1608            assert!(syms.get_var(&VarRef::new("b", Some(ExprType::Integer))).is_ok());
1609
1610            *self.cleared.borrow_mut() = true;
1611        }
1612    }
1613
1614    #[test]
1615    fn test_stack_len() {
1616        let mut stack = Stack::from([(Value::Integer(3), LineCol { line: 1, col: 2 })]);
1617        assert_eq!(1, stack.len());
1618    }
1619
1620    #[test]
1621    fn test_stack_push_pop() {
1622        let mut stack = Stack::from([]);
1623        stack.push((Value::Integer(9), LineCol { line: 1, col: 2 }));
1624        assert_eq!(Some((Value::Integer(9), LineCol { line: 1, col: 2 })), stack.pop());
1625        assert_eq!(None, stack.pop());
1626    }
1627
1628    #[test]
1629    fn test_stack_pop_types() {
1630        let mut stack = Stack::from([
1631            (Value::Boolean(false), LineCol { line: 1, col: 2 }),
1632            (Value::Double(1.2), LineCol { line: 1, col: 2 }),
1633            (Value::Integer(2), LineCol { line: 1, col: 2 }),
1634            (Value::Text("foo".to_owned()), LineCol { line: 1, col: 2 }),
1635            (Value::VarRef(SymbolKey::from("foo"), ExprType::Integer), LineCol { line: 1, col: 2 }),
1636        ]);
1637        assert_eq!((SymbolKey::from("foo"), ExprType::Integer), stack.pop_varref());
1638        assert_eq!("foo", stack.pop_string());
1639        assert_eq!(2, stack.pop_integer());
1640        assert_eq!(1.2, stack.pop_double());
1641        assert!(!stack.pop_boolean());
1642    }
1643
1644    #[test]
1645    fn test_stack_pop_types_with_pos() {
1646        let mut stack = Stack::from([
1647            (Value::Boolean(false), LineCol { line: 1, col: 2 }),
1648            (Value::Double(1.2), LineCol { line: 3, col: 4 }),
1649            (Value::Integer(2), LineCol { line: 5, col: 6 }),
1650            (Value::Text("foo".to_owned()), LineCol { line: 7, col: 8 }),
1651            (
1652                Value::VarRef(SymbolKey::from("foo"), ExprType::Integer),
1653                LineCol { line: 9, col: 10 },
1654            ),
1655        ]);
1656        assert_eq!(
1657            (SymbolKey::from("foo"), ExprType::Integer, LineCol { line: 9, col: 10 }),
1658            stack.pop_varref_with_pos()
1659        );
1660        assert_eq!(("foo".to_owned(), LineCol { line: 7, col: 8 }), stack.pop_string_with_pos());
1661        assert_eq!((2, LineCol { line: 5, col: 6 }), stack.pop_integer_with_pos());
1662        assert_eq!((1.2, LineCol { line: 3, col: 4 }), stack.pop_double_with_pos());
1663        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1664    }
1665
1666    #[test]
1667    fn test_stack_push_types() {
1668        let mut stack = Stack::from([]);
1669        stack.push_boolean(false, LineCol { line: 1, col: 2 });
1670        stack.push_double(1.2, LineCol { line: 1, col: 2 });
1671        stack.push_integer(2, LineCol { line: 1, col: 2 });
1672        stack.push_string("foo".to_owned(), LineCol { line: 1, col: 2 });
1673        stack.push_varref(SymbolKey::from("foo"), ExprType::Integer, LineCol { line: 1, col: 2 });
1674
1675        let exp_values = vec![
1676            (Value::Boolean(false), LineCol { line: 1, col: 2 }),
1677            (Value::Double(1.2), LineCol { line: 1, col: 2 }),
1678            (Value::Integer(2), LineCol { line: 1, col: 2 }),
1679            (Value::Text("foo".to_owned()), LineCol { line: 1, col: 2 }),
1680            (Value::VarRef(SymbolKey::from("foo"), ExprType::Integer), LineCol { line: 1, col: 2 }),
1681        ];
1682        assert_eq!(exp_values, stack.values);
1683    }
1684
1685    #[test]
1686    fn test_scope_and_stack_empty() {
1687        let mut stack = Stack::from([]);
1688        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1689        drop(scope);
1690        assert_eq!(0, stack.len());
1691    }
1692
1693    #[test]
1694    fn test_scope_no_args() {
1695        let mut stack = Stack::from([(Value::Integer(3), LineCol { line: 1, col: 2 })]);
1696        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1697        drop(scope);
1698        assert_eq!(1, stack.len());
1699    }
1700
1701    #[test]
1702    fn test_scope_pop_remaining_on_drop() {
1703        let mut stack = Stack::from([
1704            (Value::Integer(3), LineCol { line: 1, col: 2 }),
1705            (Value::Integer(1), LineCol { line: 1, col: 2 }),
1706            (Value::Integer(2), LineCol { line: 1, col: 2 }),
1707            (Value::Integer(4), LineCol { line: 1, col: 2 }),
1708        ]);
1709        let mut scope = Scope::new(&mut stack, 3, LineCol { line: 50, col: 60 });
1710        assert_eq!(3, scope.nargs());
1711        assert_eq!(4, scope.pop_integer());
1712        assert_eq!(2, scope.nargs());
1713        assert_eq!(2, scope.pop_integer());
1714        assert_eq!(1, scope.nargs());
1715        drop(scope);
1716        assert_eq!(1, stack.len());
1717        assert_eq!((Value::Integer(3), LineCol { line: 1, col: 2 }), stack.pop().unwrap());
1718    }
1719
1720    #[test]
1721    fn test_scope_pop_types() {
1722        let mut stack = Stack::from([
1723            (Value::Boolean(false), LineCol { line: 1, col: 2 }),
1724            (Value::Double(1.2), LineCol { line: 1, col: 2 }),
1725            (Value::Integer(2), LineCol { line: 1, col: 2 }),
1726            (Value::Text("foo".to_owned()), LineCol { line: 1, col: 2 }),
1727            (Value::VarRef(SymbolKey::from("foo"), ExprType::Integer), LineCol { line: 1, col: 2 }),
1728        ]);
1729        let mut scope = Scope::new(&mut stack, 5, LineCol { line: 50, col: 60 });
1730        assert_eq!((SymbolKey::from("foo"), ExprType::Integer), scope.pop_varref());
1731        assert_eq!("foo", scope.pop_string());
1732        assert_eq!(2, scope.pop_integer());
1733        assert_eq!(1.2, scope.pop_double());
1734        assert!(!scope.pop_boolean());
1735    }
1736
1737    #[test]
1738    fn test_scope_pop_types_with_pos() {
1739        let mut stack = Stack::from([
1740            (Value::Boolean(false), LineCol { line: 1, col: 2 }),
1741            (Value::Double(1.2), LineCol { line: 3, col: 4 }),
1742            (Value::Integer(2), LineCol { line: 5, col: 6 }),
1743            (Value::Text("foo".to_owned()), LineCol { line: 7, col: 8 }),
1744            (
1745                Value::VarRef(SymbolKey::from("foo"), ExprType::Integer),
1746                LineCol { line: 9, col: 10 },
1747            ),
1748        ]);
1749        let mut scope = Scope::new(&mut stack, 5, LineCol { line: 50, col: 60 });
1750        assert_eq!(
1751            (SymbolKey::from("foo"), ExprType::Integer, LineCol { line: 9, col: 10 }),
1752            scope.pop_varref_with_pos()
1753        );
1754        assert_eq!(("foo".to_owned(), LineCol { line: 7, col: 8 }), scope.pop_string_with_pos());
1755        assert_eq!((2, LineCol { line: 5, col: 6 }), scope.pop_integer_with_pos());
1756        assert_eq!((1.2, LineCol { line: 3, col: 4 }), scope.pop_double_with_pos());
1757        assert_eq!((false, LineCol { line: 1, col: 2 }), scope.pop_boolean_with_pos());
1758    }
1759
1760    #[test]
1761    fn test_scope_return_any() {
1762        let mut stack = Stack::from([(Value::Boolean(false), LineCol { line: 1, col: 2 })]);
1763        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1764        assert!(scope.return_any(Value::Boolean(true)).is_ok());
1765        assert_eq!((true, LineCol { line: 50, col: 60 }), stack.pop_boolean_with_pos());
1766        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1767    }
1768
1769    #[test]
1770    fn test_scope_return_boolean() {
1771        let mut stack = Stack::from([(Value::Boolean(false), LineCol { line: 1, col: 2 })]);
1772        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1773        assert!(scope.return_boolean(true).is_ok());
1774        assert_eq!((true, LineCol { line: 50, col: 60 }), stack.pop_boolean_with_pos());
1775        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1776    }
1777
1778    #[test]
1779    fn test_scope_return_double() {
1780        let mut stack = Stack::from([(Value::Boolean(false), LineCol { line: 1, col: 2 })]);
1781        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1782        assert!(scope.return_double(4.5).is_ok());
1783        assert_eq!((4.5, LineCol { line: 50, col: 60 }), stack.pop_double_with_pos());
1784        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1785    }
1786
1787    #[test]
1788    fn test_scope_return_integer() {
1789        let mut stack = Stack::from([(Value::Boolean(false), LineCol { line: 1, col: 2 })]);
1790        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1791        assert!(scope.return_integer(7).is_ok());
1792        assert_eq!((7, LineCol { line: 50, col: 60 }), stack.pop_integer_with_pos());
1793        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1794    }
1795
1796    #[test]
1797    fn test_scope_return_string() {
1798        let mut stack = Stack::from([(Value::Boolean(false), LineCol { line: 1, col: 2 })]);
1799        let scope = Scope::new(&mut stack, 0, LineCol { line: 50, col: 60 });
1800        assert!(scope.return_string("foo").is_ok());
1801        assert_eq!(("foo".to_owned(), LineCol { line: 50, col: 60 }), stack.pop_string_with_pos());
1802        assert_eq!((false, LineCol { line: 1, col: 2 }), stack.pop_boolean_with_pos());
1803    }
1804
1805    #[test]
1806    fn test_clear() {
1807        let mut machine = Machine::default();
1808
1809        let cleared = Rc::from(RefCell::from(false));
1810        let clearable = Box::from(MockClearable { cleared: cleared.clone() });
1811        machine.add_clearable(clearable);
1812
1813        assert_eq!(
1814            StopReason::Eof,
1815            block_on(machine.exec(&mut b"a = TRUE: b = 1".as_ref())).expect("Execution failed")
1816        );
1817        assert!(machine.get_var_as_bool("a").is_ok());
1818        assert!(machine.get_var_as_int("b").is_ok());
1819        assert!(!*cleared.borrow());
1820        machine.clear();
1821        assert!(machine.get_var_as_bool("a").is_err());
1822        assert!(machine.get_var_as_int("b").is_err());
1823        assert!(*cleared.borrow());
1824    }
1825
1826    #[test]
1827    fn test_get_data() {
1828        let captured_data = Rc::from(RefCell::from(vec![]));
1829        let mut machine = Machine::default();
1830        machine.add_callable(GetDataCommand::new(captured_data.clone()));
1831
1832        assert!(machine.get_data().is_empty());
1833
1834        assert_eq!(
1835            StopReason::Eof,
1836            block_on(machine.exec(&mut b"DATA 3: GETDATA".as_ref())).unwrap()
1837        );
1838        assert!(machine.get_data().is_empty());
1839        assert_eq!(&[Some(Value::Integer(3))], captured_data.borrow().as_slice());
1840
1841        assert_eq!(
1842            StopReason::Eof,
1843            block_on(
1844                machine.exec(
1845                    &mut b"
1846                        GETDATA
1847                        IF FALSE THEN: DATA 5: ELSE: DATA 6: END IF
1848                        WHILE FALSE: DATA 1: WEND
1849                        FOR i = 0 TO 0: DATA 0: NEXT
1850                    "
1851                    .as_ref()
1852                )
1853            )
1854            .unwrap()
1855        );
1856        assert!(machine.get_data().is_empty());
1857        assert_eq!(
1858            &[
1859                Some(Value::Integer(5)),
1860                Some(Value::Integer(6)),
1861                Some(Value::Integer(1)),
1862                Some(Value::Integer(0))
1863            ],
1864            captured_data.borrow().as_slice()
1865        );
1866    }
1867
1868    #[test]
1869    fn test_get_data_is_empty_after_execution() {
1870        let mut machine = Machine::default();
1871
1872        assert_eq!(StopReason::Eof, block_on(machine.exec(&mut b"DATA 3".as_ref())).unwrap());
1873        assert!(machine.get_data().is_empty());
1874
1875        block_on(machine.exec(&mut b"DATA 3: abc".as_ref())).unwrap_err();
1876        assert!(machine.get_data().is_empty());
1877
1878        block_on(machine.exec(&mut b"DATA 3: GOTO @foo".as_ref())).unwrap_err();
1879        assert!(machine.get_data().is_empty());
1880    }
1881
1882    #[test]
1883    fn test_get_var_as_bool() {
1884        let mut machine = Machine::default();
1885        assert_eq!(
1886            StopReason::Eof,
1887            block_on(machine.exec(&mut b"a = TRUE: b = 1".as_ref())).expect("Execution failed")
1888        );
1889        assert!(machine.get_var_as_bool("a").expect("Failed to query a"));
1890        assert_eq!(
1891            "Incompatible types in b? reference",
1892            format!("{}", machine.get_var_as_bool("b").expect_err("Querying b succeeded"))
1893        );
1894        assert_eq!(
1895            "Undefined variable c",
1896            format!("{}", machine.get_var_as_bool("c").expect_err("Querying c succeeded"))
1897        );
1898    }
1899
1900    #[test]
1901    fn test_get_var_as_int() {
1902        let mut machine = Machine::default();
1903        assert_eq!(
1904            StopReason::Eof,
1905            block_on(machine.exec(&mut b"a = 1: b = \"foo\"".as_ref())).expect("Execution failed")
1906        );
1907        assert_eq!(1, machine.get_var_as_int("a").expect("Failed to query a"));
1908        assert_eq!(
1909            "Incompatible types in b% reference",
1910            format!("{}", machine.get_var_as_int("b").expect_err("Querying b succeeded"))
1911        );
1912        assert_eq!(
1913            "Undefined variable c",
1914            format!("{}", machine.get_var_as_int("c").expect_err("Querying c succeeded"))
1915        );
1916    }
1917
1918    #[test]
1919    fn test_get_var_as_string() {
1920        let mut machine = Machine::default();
1921        assert_eq!(
1922            StopReason::Eof,
1923            block_on(machine.exec(&mut b"a = \"foo\": b = FALSE".as_ref()))
1924                .expect("Execution failed")
1925        );
1926        assert_eq!("foo", machine.get_var_as_string("a").expect("Failed to query a"));
1927        assert_eq!(
1928            "Incompatible types in b$ reference",
1929            format!("{}", machine.get_var_as_string("b").expect_err("Querying b succeeded"))
1930        );
1931        assert_eq!(
1932            "Undefined variable c",
1933            format!("{}", machine.get_var_as_string("c").expect_err("Querying c succeeded"))
1934        );
1935    }
1936
1937    /// Runs the `input` code on a new test machine.
1938    ///
1939    /// `golden_in` is the sequence of values to yield by `IN`.
1940    /// `expected_out` is updated with the sequence of calls to `OUT`.
1941    fn run(
1942        input: &str,
1943        golden_in: &'static [&'static str],
1944        captured_out: Rc<RefCell<Vec<String>>>,
1945    ) -> Result<StopReason> {
1946        let mut machine = Machine::default();
1947        machine.add_callable(ArglessFunction::new(Value::Integer(1234)));
1948        machine.add_callable(ClearCommand::new());
1949        machine.add_callable(CountFunction::new());
1950        machine.add_callable(LastErrorFunction::new());
1951        machine.add_callable(InCommand::new(Box::from(RefCell::from(golden_in.iter()))));
1952        machine.add_callable(OutCommand::new(captured_out.clone()));
1953        machine.add_callable(OutfFunction::new(captured_out));
1954        machine.add_callable(RaiseCommand::new());
1955        machine.add_callable(RaisefFunction::new());
1956        machine.add_callable(SumFunction::new());
1957        machine.add_callable(TypeCheckFunction::new(Value::Integer(5)));
1958        block_on(machine.exec(&mut input.as_bytes()))
1959    }
1960
1961    /// Runs the `input` code on a new test machine and verifies its output.
1962    ///
1963    /// `golden_in` is the sequence of values to yield by `IN`.
1964    /// `expected_out` is the sequence of expected calls to `OUT`.
1965    fn do_ok_test(
1966        input: &str,
1967        golden_in: &'static [&'static str],
1968        expected_out: &'static [&'static str],
1969    ) {
1970        let captured_out = Rc::from(RefCell::from(vec![]));
1971        assert_eq!(
1972            StopReason::Eof,
1973            run(input, golden_in, captured_out.clone()).expect("Execution failed")
1974        );
1975        assert_eq!(expected_out, captured_out.borrow().as_slice());
1976    }
1977
1978    /// Runs the `input` code on a new test machine and verifies that it fails with `expected_err`.
1979    ///
1980    /// Given that the code has side-effects until it fails, this follows the same process as
1981    /// `do_ok_test` regarding `golden_in` and `expected_out`.
1982    fn do_error_test(
1983        input: &str,
1984        golden_in: &'static [&'static str],
1985        expected_out: &'static [&'static str],
1986        expected_err: &str,
1987    ) {
1988        let captured_out = Rc::from(RefCell::from(vec![]));
1989        let err = run(input, golden_in, captured_out.clone()).expect_err("Execution did not fail");
1990        assert_eq!(expected_err, format!("{}", err));
1991        assert_eq!(expected_out, captured_out.borrow().as_slice());
1992    }
1993
1994    /// Runs the `input` code on a new machine and verifies that it fails with `expected_err`.
1995    ///
1996    /// This is a syntactic wrapper over `do_error_test` to simplify those tests that are not
1997    /// expected to request any input nor generate any output.
1998    fn do_simple_error_test(input: &str, expected_err: &str) {
1999        do_error_test(input, &[], &[], expected_err);
2000    }
2001
2002    #[test]
2003    fn test_array_assignment_ok() {
2004        do_ok_test("DIM a(3)\na(1) = 5 + 1\nOUT a(0); a(1); a(2)", &[], &["0 6 0"]);
2005        do_ok_test("DIM a(3) AS STRING\na$(1) = \"x\"\nOUT a(0); a(1); a$(2)", &[], &[" x "]);
2006        do_ok_test(
2007            "DIM a(3, 8, 2) AS BOOLEAN\na(3 - 3, 2 * 2, 1) = TRUE\nOUT a(0, 4, 1)",
2008            &[],
2009            &["TRUE"],
2010        );
2011    }
2012
2013    #[test]
2014    fn test_array_assignment_ok_casting() {
2015        do_ok_test("DIM a(1)\na(0) = 3.6\nOUT a(0)", &[], &["4"]);
2016        do_ok_test("DIM a(1) AS INTEGER\na(0) = 3.6\nOUT a(0)", &[], &["4"]);
2017        do_ok_test("DIM a(1) AS DOUBLE\na(0) = 3\nOUT a(0)", &[], &["3"]);
2018    }
2019
2020    #[test]
2021    fn test_array_assignment_ok_case_insensitive() {
2022        do_ok_test("DIM a(3)\nA(1) = 5\na(2) = 1\nOUT A(0); a(1); A(2)", &[], &["0 5 1"]);
2023    }
2024
2025    #[test]
2026    fn test_array_assignment_errors() {
2027        do_simple_error_test("a() = 3\n", "1:1: Cannot index undefined array a");
2028        do_simple_error_test("a = 3\na(0) = 3\n", "2:1: Cannot index non-array a");
2029        do_simple_error_test(
2030            "DIM a(2)\na() = 3\n",
2031            "2:1: Cannot index array with 0 subscripts; need 1",
2032        );
2033        do_simple_error_test("DIM a(1)\na(-1) = 3\n", "2:1: Subscript -1 cannot be negative");
2034        do_simple_error_test(
2035            "DIM a(1, 2)\na(1, TRUE) = 3\n",
2036            "2:6: Array index must be INTEGER, not BOOLEAN",
2037        );
2038        do_simple_error_test("DIM a(2)\na$(1) = 3", "2:1: Incompatible types in a$ reference");
2039    }
2040
2041    #[test]
2042    fn test_assignment_ok_types() {
2043        do_ok_test("a = TRUE\nOUT a; a?", &[], &["TRUE TRUE"]);
2044        do_ok_test("a? = FALSE\nOUT a; a?", &[], &["FALSE FALSE"]);
2045
2046        do_ok_test("a = 3.5\nOUT a; a#", &[], &["3.5 3.5"]);
2047        do_ok_test("a# = 3.5\nOUT a; a#", &[], &["3.5 3.5"]);
2048
2049        do_ok_test("a = 3\nOUT a; a%", &[], &["3 3"]);
2050        do_ok_test("a% = 3\nOUT a; a%", &[], &["3 3"]);
2051
2052        do_ok_test("a = \"some text\"\nOUT a; a$", &[], &["some text some text"]);
2053        do_ok_test("a$ = \"some text\"\nOUT a; a$", &[], &["some text some text"]);
2054
2055        do_ok_test("a = 1\na = a + 1\nOUT a", &[], &["2"]);
2056    }
2057
2058    #[test]
2059    fn test_assignment_ok_casting() {
2060        do_ok_test("a = 5.2\nOUT a; a#", &[], &["5.2 5.2"]);
2061        do_ok_test("a% = 5.2\nOUT a; a%", &[], &["5 5"]);
2062
2063        do_ok_test("a = 3 + 5.2\nOUT a; a#", &[], &["8.2 8.2"]);
2064        do_ok_test("a = 3.7 + 5.2\nOUT a; a#", &[], &["8.9 8.9"]);
2065
2066        do_ok_test("a% = 3 + 5.2\nOUT a; a%", &[], &["8 8"]);
2067        do_ok_test("a% = 3.7 + 5.2\nOUT a; a%", &[], &["9 9"]);
2068
2069        do_ok_test("a# = 3\nOUT a; a#", &[], &["3 3"]);
2070        do_ok_test("a# = 2.8 + 3\nOUT a; a#", &[], &["5.8 5.8"]);
2071    }
2072
2073    #[test]
2074    fn test_assignment_ok_case_insensitive() {
2075        do_ok_test("foo = 32\nOUT FOO", &[], &["32"]);
2076    }
2077
2078    #[test]
2079    fn test_assignment_array_access_with_varref() {
2080        do_ok_test("DIM a(1)\na(0) = 123\ni = 0\nr = a(i)\nOUT r", &[], &["123"]);
2081    }
2082
2083    #[test]
2084    fn test_assignment_argless_function_call() {
2085        do_ok_test("a = SUM(3, COUNT, 5)\nOUT a", &[], &["9"]);
2086    }
2087
2088    #[test]
2089    fn test_assignment_errors() {
2090        do_simple_error_test("a =\n", "1:4: Missing expression in assignment");
2091        do_simple_error_test("a = b\n", "1:5: Undefined variable b");
2092        do_simple_error_test(
2093            "a = 3\na = TRUE\n",
2094            "2:1: Cannot assign value of type BOOLEAN to variable of type INTEGER",
2095        );
2096        do_simple_error_test(
2097            "a? = 3",
2098            "1:1: Cannot assign value of type INTEGER to variable of type BOOLEAN",
2099        );
2100    }
2101
2102    #[test]
2103    fn test_dim_ok() {
2104        do_ok_test("DIM foo\nDIM bar AS BOOLEAN\nOUT foo%; bar?", &[], &["0 FALSE"]);
2105    }
2106
2107    #[test]
2108    fn test_dim_errors() {
2109        do_simple_error_test("DIM i\nDIM i", "2:5: Cannot DIM already-defined symbol i");
2110        do_simple_error_test("DIM i\nDIM I", "2:5: Cannot DIM already-defined symbol I");
2111        do_simple_error_test("i = 0\nDIM i", "2:5: Cannot DIM already-defined symbol i");
2112    }
2113
2114    #[test]
2115    fn test_dim_array_ok() {
2116        do_ok_test(
2117            "DIM foo(3, 4)\nDIM bar(1) AS BOOLEAN\nOUT foo%(2, 2); bar?(0)",
2118            &[],
2119            &["0 FALSE"],
2120        );
2121    }
2122
2123    #[test]
2124    fn test_dim_array_varref_ok() {
2125        do_ok_test("i = 3\nDIM foo(i)\nOUT foo%(2)", &[], &["0"]);
2126    }
2127
2128    #[test]
2129    fn test_dim_array_errors() {
2130        do_simple_error_test("DIM i()", "1:6: Arrays require at least one dimension");
2131        do_simple_error_test("DIM i(FALSE)", "1:7: BOOLEAN is not a number");
2132        do_simple_error_test("DIM i(-3)", "1:7: Dimensions in DIM array must be positive");
2133        do_simple_error_test("DIM i\nDIM i(3)", "2:5: Cannot DIM already-defined symbol i");
2134    }
2135
2136    #[test]
2137    fn test_end_no_code() {
2138        let captured_out = Rc::from(RefCell::from(vec![]));
2139        assert_eq!(
2140            StopReason::Exited(5),
2141            run("OUT 1\nEND 5\nOUT 2", &[], captured_out.clone()).expect("Execution failed")
2142        );
2143        assert_eq!(&["1"], captured_out.borrow().as_slice());
2144    }
2145
2146    fn do_end_with_code_test(code: u8) {
2147        let captured_out = Rc::from(RefCell::from(vec![]));
2148        assert_eq!(
2149            StopReason::Exited(code),
2150            run(&format!("OUT 1: END {}: OUT 2", code), &[], captured_out.clone())
2151                .expect("Execution failed")
2152        );
2153        assert_eq!(&["1"], captured_out.borrow().as_slice());
2154
2155        let captured_out = Rc::from(RefCell::from(vec![]));
2156        assert_eq!(
2157            StopReason::Exited(code),
2158            run(&format!("OUT 1: END {}.2: OUT 2", code), &[], captured_out.clone())
2159                .expect("Execution failed")
2160        );
2161        assert_eq!(&["1"], captured_out.borrow().as_slice());
2162    }
2163
2164    #[test]
2165    fn text_end_with_code() {
2166        do_end_with_code_test(0);
2167        do_end_with_code_test(1);
2168        do_end_with_code_test(42);
2169        do_end_with_code_test(127);
2170    }
2171
2172    #[test]
2173    fn test_end_errors() {
2174        do_simple_error_test("END 1, 2", "1:6: Expected newline but found ,");
2175        do_simple_error_test("END \"b\"", "1:5: STRING is not a number");
2176        do_simple_error_test("END -3", "1:5: Exit code must be a positive integer");
2177        do_simple_error_test("END 128", "1:5: Exit code cannot be larger than 127");
2178    }
2179
2180    #[test]
2181    fn test_end_if() {
2182        let captured_out = Rc::from(RefCell::from(vec![]));
2183        let input = r#"
2184            IF TRUE THEN
2185                OUT OUTF(1, 100)
2186                END
2187                OUT OUTF(2, 200)
2188            ELSEIF OUTF(3, 300) = 0 THEN
2189                OUT OUTF(4, 400)
2190            ELSE
2191                OUT OUTF(5, 500)
2192            END IF
2193            "#;
2194        assert_eq!(StopReason::Exited(0), run(input, &[], captured_out.clone()).unwrap());
2195        assert_eq!(&["100", "1"], captured_out.borrow().as_slice());
2196    }
2197
2198    #[test]
2199    fn test_end_for() {
2200        let captured_out = Rc::from(RefCell::from(vec![]));
2201        let input = r#"FOR i = 1 TO OUTF(10, i * 100): IF i = 3 THEN: END: END IF: OUT i: NEXT"#;
2202        assert_eq!(StopReason::Exited(0), run(input, &[], captured_out.clone()).unwrap());
2203        assert_eq!(&["100", "1", "200", "2", "300"], captured_out.borrow().as_slice());
2204    }
2205
2206    #[test]
2207    fn test_end_while() {
2208        let captured_out = Rc::from(RefCell::from(vec![]));
2209        let input = r#"i = 1: WHILE i < OUTF(10, i * 100): IF i = 4 THEN: END: END IF: OUT i: i = i + 1: WEND"#;
2210        assert_eq!(StopReason::Exited(0), run(input, &[], captured_out.clone()).unwrap());
2211        assert_eq!(&["100", "1", "200", "2", "300", "3", "400"], captured_out.borrow().as_slice());
2212    }
2213
2214    #[test]
2215    fn test_end_nested() {
2216        let captured_out = Rc::from(RefCell::from(vec![]));
2217        assert_eq!(
2218            StopReason::Exited(42),
2219            run(
2220                "FOR a = 0 TO 10\nOUT a\nIF a = 3 THEN\nEND 42\nOUT \"no\"\nEND IF\nNEXT",
2221                &[],
2222                captured_out.clone()
2223            )
2224            .expect("Execution failed")
2225        );
2226        assert_eq!(&["0", "1", "2", "3"], captured_out.borrow().as_slice());
2227    }
2228
2229    #[test]
2230    fn test_end_can_resume() {
2231        let captured_out = Rc::from(RefCell::from(vec![]));
2232        let mut machine = Machine::default();
2233        machine.add_callable(OutCommand::new(captured_out.clone()));
2234        machine.add_callable(SumFunction::new());
2235
2236        assert_eq!(
2237            StopReason::Exited(10),
2238            block_on(machine.exec(&mut "OUT 1\nEND 10\nOUT 2".as_bytes()))
2239                .expect("Execution failed")
2240        );
2241        assert_eq!(&["1"], captured_out.borrow().as_slice());
2242
2243        captured_out.borrow_mut().clear();
2244        assert_eq!(
2245            StopReason::Exited(11),
2246            block_on(machine.exec(&mut "OUT 2\nEND 11\nOUT 3".as_bytes()))
2247                .expect("Execution failed")
2248        );
2249        assert_eq!(&["2"], captured_out.borrow().as_slice());
2250    }
2251
2252    #[tokio::test]
2253    async fn test_signals_stop() {
2254        let mut machine = Machine::default();
2255        let signals_tx = machine.get_signals_tx();
2256
2257        // This is a best-effort test because we are trying to exercise a condition for which we
2258        // have no synchronization primitives.
2259        for _ in 0..10 {
2260            let input = &mut "WHILE TRUE: WEND".as_bytes();
2261            machine.drain_signals();
2262            let future = machine.exec(input);
2263
2264            // There is no guarantee that the tight loop inside the machine is running immediately
2265            // at this point (particularly because we run with just one thread in this test), thus
2266            // we may send the signal prematurely.  We would still get a `Break` stop reason because
2267            // we would detect the signal *before* entering the loop, but that would defeat the
2268            // purpose of this test.  Give a chance to the machine code to run first and get stuck.
2269            tokio::task::yield_now().await;
2270
2271            signals_tx.send(Signal::Break).await.unwrap();
2272            let result = future.await;
2273            assert_eq!(StopReason::Break, result.unwrap());
2274        }
2275    }
2276
2277    async fn do_no_check_stop_test(code: &str) {
2278        let (tx, rx) = async_channel::unbounded();
2279        let mut machine = Machine::with_signals_chan_and_yield_now_fn((tx.clone(), rx), None);
2280
2281        tx.send(Signal::Break).await.unwrap();
2282
2283        let input = &mut code.as_bytes();
2284        assert_eq!(StopReason::Eof, machine.exec(input).await.unwrap());
2285
2286        assert_eq!(1, tx.len());
2287    }
2288
2289    #[tokio::test]
2290    async fn test_goto_forward_does_not_check_stop() {
2291        do_no_check_stop_test("GOTO @after: a = 1: @after").await;
2292    }
2293
2294    #[tokio::test]
2295    async fn test_if_taken_does_not_check_stop() {
2296        do_no_check_stop_test("a = 3: IF a = 3 THEN b = 0 ELSE b = 1: a = 7").await;
2297    }
2298
2299    #[tokio::test]
2300    async fn test_if_not_taken_does_not_check_stop() {
2301        do_no_check_stop_test("a = 3: IF a = 5 THEN b = 0 ELSE b = 1: a = 7").await;
2302    }
2303
2304    async fn do_check_stop_test(code: &str) {
2305        let (tx, rx) = async_channel::unbounded();
2306        let mut machine = Machine::with_signals_chan_and_yield_now_fn((tx.clone(), rx), None);
2307
2308        tx.send(Signal::Break).await.unwrap();
2309
2310        let input = &mut code.as_bytes();
2311        assert_eq!(StopReason::Break, machine.exec(input).await.unwrap());
2312
2313        assert_eq!(0, tx.len());
2314    }
2315
2316    #[tokio::test]
2317    async fn goto_checks_stop() {
2318        do_check_stop_test("@here: GOTO @here").await;
2319        do_check_stop_test("@before: a = 1: GOTO @before").await;
2320    }
2321
2322    #[tokio::test]
2323    async fn gosub_checks_stop() {
2324        do_check_stop_test("GOTO @skip: @sub: a = 1: RETURN: @skip: GOSUB @sub: a = 1").await;
2325    }
2326
2327    #[tokio::test]
2328    async fn do_checks_stop() {
2329        do_check_stop_test("DO: LOOP").await;
2330        do_check_stop_test("DO: a = 1: LOOP").await;
2331
2332        do_check_stop_test("DO UNTIL FALSE: LOOP").await;
2333        do_check_stop_test("DO UNTIL FALSE: a = 1: LOOP").await;
2334
2335        do_check_stop_test("DO WHILE TRUE: LOOP").await;
2336        do_check_stop_test("DO WHILE TRUE: a = 1: LOOP").await;
2337
2338        do_check_stop_test("DO: LOOP UNTIL FALSE").await;
2339        do_check_stop_test("DO: a = 1: LOOP UNTIL FALSE").await;
2340
2341        do_check_stop_test("DO: LOOP WHILE TRUE").await;
2342        do_check_stop_test("DO: a = 1: LOOP WHILE TRUE").await;
2343    }
2344
2345    #[tokio::test]
2346    async fn for_checks_stop() {
2347        do_check_stop_test("FOR a = 1 TO 10: NEXT").await;
2348        do_check_stop_test("FOR a = 1 TO 10: B = 2: NEXT").await;
2349    }
2350
2351    #[tokio::test]
2352    async fn while_checks_stop() {
2353        do_check_stop_test("WHILE TRUE: WEND").await;
2354        do_check_stop_test("WHILE TRUE: a = 1: WEND").await;
2355    }
2356
2357    #[test]
2358    fn test_do_infinite_ok() {
2359        let code = r#"
2360            IN n
2361            DO
2362                IF n = 0 THEN: EXIT DO: END IF
2363                OUT "n is"; n
2364                n = n - 1
2365            LOOP
2366        "#;
2367        do_ok_test(code, &["0"], &[]);
2368        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
2369    }
2370
2371    #[test]
2372    fn test_do_pre_until_ok() {
2373        let code = r#"
2374            IN n
2375            DO UNTIL n = 0
2376                OUT "n is"; n
2377                n = n - 1
2378            LOOP
2379        "#;
2380        do_ok_test(code, &["0"], &[]);
2381        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
2382
2383        do_ok_test("DO UNTIL TRUE\nOUT 1\nLOOP", &[], &[]);
2384    }
2385
2386    #[test]
2387    fn test_do_pre_while_ok() {
2388        let code = r#"
2389            IN n
2390            DO WHILE n > 0
2391                OUT "n is"; n
2392                n = n - 1
2393            LOOP
2394        "#;
2395        do_ok_test(code, &["0"], &[]);
2396        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
2397
2398        do_ok_test("DO WHILE FALSE\nOUT 1\nLOOP", &[], &[]);
2399    }
2400
2401    #[test]
2402    fn test_do_post_until_ok() {
2403        let code = r#"
2404            IN n
2405            DO
2406                OUT "n is"; n
2407                n = n - 1
2408            LOOP UNTIL n = 0
2409        "#;
2410        do_ok_test(code, &["1"], &["n is 1"]);
2411        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
2412
2413        do_ok_test("DO\nOUT 1\nLOOP UNTIL TRUE", &[], &["1"]);
2414    }
2415
2416    #[test]
2417    fn test_do_post_while_ok() {
2418        let code = r#"
2419            IN n
2420            DO
2421                OUT "n is"; n
2422                n = n - 1
2423            LOOP WHILE n > 0
2424        "#;
2425        do_ok_test(code, &["1"], &["n is 1"]);
2426        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
2427
2428        do_ok_test("DO\nOUT 1\nLOOP WHILE FALSE", &[], &["1"]);
2429    }
2430
2431    #[test]
2432    fn test_do_errors() {
2433        do_simple_error_test("DO WHILE 2\nLOOP", "1:10: DO requires a boolean condition");
2434    }
2435
2436    #[test]
2437    fn test_exit_do() {
2438        do_ok_test(
2439            r#"
2440            i = 5
2441            DO WHILE i > 0
2442                j = 2
2443                DO UNTIL j = 0
2444                    OUT i; j
2445                    IF i = 3 AND j = 2 THEN: EXIT DO: END IF
2446                    j = j - 1
2447                LOOP
2448                IF i = 2 THEN: EXIT DO: END IF
2449                i = i - 1
2450            LOOP
2451            "#,
2452            &[],
2453            &["5 2", "5 1", "4 2", "4 1", "3 2", "2 2", "2 1"],
2454        );
2455    }
2456
2457    #[test]
2458    fn test_exit_do_sequential() {
2459        do_ok_test(
2460            r#"
2461            i = 2
2462            DO WHILE i > 0
2463                OUT "First"; i
2464                i = i - 1
2465            LOOP
2466            i = 2
2467            DO WHILE i > 0
2468                OUT "Second"; i
2469                i = i - 1
2470            LOOP
2471            "#,
2472            &[],
2473            &["First 2", "First 1", "Second 2", "Second 1"],
2474        );
2475    }
2476
2477    #[test]
2478    fn test_exit_do_nested_indirectly() {
2479        do_ok_test(
2480            r#"
2481            i = 5
2482            DO WHILE i > 0
2483                GOSUB @another
2484                IF i = 2 THEN: EXIT DO: END IF
2485                i = i - 1
2486            LOOP
2487            GOTO @end
2488            @another
2489            j = 2
2490            DO UNTIL j = 0
2491                OUT i; j
2492                IF i = 3 AND j = 2 THEN: EXIT DO: END IF
2493                j = j - 1
2494            LOOP
2495            RETURN
2496            @end
2497            "#,
2498            &[],
2499            &["5 2", "5 1", "4 2", "4 1", "3 2", "2 2", "2 1"],
2500        );
2501    }
2502
2503    #[test]
2504    fn test_expr_load_not_assigned() {
2505        do_error_test(
2506            r#"
2507            GOTO @skip
2508            a = 0
2509            @skip
2510            OUT "running"
2511            OUT a + 1
2512            OUT "not reached"
2513            "#,
2514            &[],
2515            &["running"],
2516            "6:17: Undefined variable A",
2517        )
2518    }
2519
2520    #[test]
2521    fn test_expr_array_load_not_assigned() {
2522        do_error_test(
2523            r#"
2524            GOTO @skip
2525            DIM a(3)
2526            @skip
2527            OUT "running"
2528            OUT a(0) + 1
2529            OUT "not reached"
2530            "#,
2531            &[],
2532            &["running"],
2533            "6:17: A is not defined",
2534        )
2535    }
2536
2537    #[test]
2538    fn test_if_ok() {
2539        let code = r#"
2540            IN n
2541            IF n = 3 THEN
2542                OUT "match"
2543            END IF
2544            IF n <> 3 THEN
2545                OUT "no match"
2546            END IF
2547        "#;
2548        do_ok_test(code, &["3"], &["match"]);
2549        do_ok_test(code, &["5"], &["no match"]);
2550
2551        let code = r#"
2552            IN n
2553            IF n = 1 THEN
2554                OUT "first"
2555            ELSEIF n = 2 THEN
2556                OUT "second"
2557            ELSEIF n = 3 THEN
2558                OUT "third"
2559            ELSE
2560                OUT "fourth"
2561            END IF
2562        "#;
2563        do_ok_test(code, &["1"], &["first"]);
2564        do_ok_test(code, &["2"], &["second"]);
2565        do_ok_test(code, &["3"], &["third"]);
2566        do_ok_test(code, &["4"], &["fourth"]);
2567    }
2568
2569    #[test]
2570    fn test_if_not_executed_on_malformed_branch() {
2571        let code = r#"
2572            IN n
2573            IF n = 3 THEN
2574                OUT "match"
2575            ELSEIF "foo" THEN 'Invalid expression type but not evaluated.
2576                OUT "no match"
2577            END IF
2578        "#;
2579        do_error_test(code, &["5"], &[], "5:20: IF/ELSEIF require a boolean condition");
2580    }
2581
2582    #[test]
2583    fn test_if_errors() {
2584        do_simple_error_test("IF TRUE THEN END IF", "1:14: END IF without IF");
2585        do_simple_error_test(
2586            "IF TRUE THEN\nELSE IF TRUE THEN\nEND IF",
2587            "2:6: Expecting newline after ELSE",
2588        );
2589        do_simple_error_test("IF TRUE\nEND IF\nOUT 3", "1:8: No THEN in IF statement");
2590
2591        do_simple_error_test("IF 2\nEND IF", "1:5: No THEN in IF statement");
2592        do_simple_error_test("IF 2 THEN\nEND IF", "1:4: IF/ELSEIF require a boolean condition");
2593        do_simple_error_test(
2594            "IF FALSE THEN\nELSEIF 2 THEN\nEND IF",
2595            "2:8: IF/ELSEIF require a boolean condition",
2596        );
2597    }
2598
2599    #[test]
2600    fn test_for_incrementing() {
2601        do_ok_test("FOR a = 0 TO 0: OUT a: NEXT", &[], &["0"]);
2602        do_ok_test("FOR a = 0 TO 3: OUT a: NEXT", &[], &["0", "1", "2", "3"]);
2603        do_ok_test("FOR a = 1.3 TO 3: OUT a: NEXT", &[], &["1.3", "2.3"]);
2604        do_ok_test("FOR a = 1.3 TO 3.3: OUT a: NEXT", &[], &["1.3", "2.3", "3.3"]);
2605        do_ok_test("FOR a = 1 TO 3.3: OUT a: NEXT", &[], &["1", "2", "3"]);
2606        do_ok_test("FOR a = 1 TO 3.7: OUT a: NEXT", &[], &["1", "2", "3"]);
2607        do_ok_test("FOR a# = 1 TO 2: OUT a: NEXT", &[], &["1", "2"]);
2608        do_ok_test("FOR a = 1.1 TO 2.1: b% = (a * 10): OUT b: NEXT", &[], &["11", "21"]);
2609        do_ok_test("FOR a# = 1.1 TO 2.1: b% = (a * 10): OUT b: NEXT", &[], &["11", "21"]);
2610    }
2611
2612    #[test]
2613    fn test_for_incrementing_with_step() {
2614        do_ok_test("FOR a = 0 TO 0 STEP 3: OUT a: NEXT", &[], &["0"]);
2615        do_ok_test("FOR a = 0 TO 2 STEP 3: OUT a: NEXT", &[], &["0"]);
2616        do_ok_test("FOR a = 0 TO 3 STEP 3: OUT a: NEXT", &[], &["0", "3"]);
2617        do_ok_test("FOR a = 0 TO 10 STEP 3: OUT a: NEXT", &[], &["0", "3", "6", "9"]);
2618        do_ok_test("FOR a = 1 TO 2 STEP 0.4: b% = (a * 10): OUT b: NEXT", &[], &["10", "14", "18"]);
2619        do_ok_test(
2620            "FOR a# = 1 TO 2 STEP 0.4: b% = (a * 10): OUT b: NEXT",
2621            &[],
2622            &["10", "14", "18"],
2623        );
2624    }
2625
2626    #[test]
2627    fn test_for_decrementing_with_step() {
2628        do_ok_test("FOR a = 0 TO 0 STEP -2: OUT a: NEXT", &[], &["0"]);
2629        do_ok_test("FOR a = 2 TO 0 STEP -2: OUT a: NEXT", &[], &["2", "0"]);
2630        do_ok_test("FOR a = -2 TO -6 STEP -2: OUT a: NEXT", &[], &["-2", "-4", "-6"]);
2631        do_ok_test("FOR a = 10 TO 1 STEP -2: OUT a: NEXT", &[], &["10", "8", "6", "4", "2"]);
2632        do_ok_test(
2633            "FOR a# = 2 TO 1 STEP -0.4: b% = (a * 10): OUT b: NEXT",
2634            &[],
2635            &["20", "16", "12"],
2636        );
2637    }
2638
2639    #[test]
2640    fn test_for_doubles_on_integer_iterator() {
2641        // This tests a corner case where using a DOUBLE step value on a variable that is declared
2642        // as an INTEGER results in an infinite loop due to rounding.  We could error our in this
2643        // case (or force the iterator to be a DOUBLE if it is not yet defined), but I'm not yet
2644        // sure if there would be legitimate reasons for someone to want this.
2645        do_ok_test(
2646            r#"
2647            i = 0
2648            DIM a AS INTEGER
2649            FOR a = 1.0 TO 2.0 STEP 0.4
2650                i = i + 1
2651                IF i = 100 THEN
2652                    GOTO @out
2653                END IF
2654            NEXT
2655            @out: OUT i
2656            "#,
2657            &[],
2658            &["100"],
2659        );
2660    }
2661
2662    #[test]
2663    fn test_for_already_done() {
2664        do_ok_test("FOR i = 10 TO 9\nOUT i\nNEXT", &[], &[]);
2665        do_ok_test("FOR i = 9 TO 10 STEP -1\nOUT i\nNEXT", &[], &[]);
2666    }
2667
2668    #[test]
2669    fn test_for_iterator_is_visible_after_next() {
2670        let code = r#"
2671            FOR something = 1 TO 10 STEP 8
2672            NEXT
2673            OUT something
2674        "#;
2675        do_ok_test(code, &[], &["17"]);
2676    }
2677
2678    #[test]
2679    fn test_for_iterator_can_be_modified() {
2680        let code = r#"
2681            FOR something = 1 TO 5
2682                OUT something
2683                something = something + 1
2684            NEXT
2685        "#;
2686        do_ok_test(code, &[], &["1", "3", "5"]);
2687    }
2688
2689    #[test]
2690    fn test_for_errors() {
2691        do_simple_error_test("FOR\nNEXT", "1:4: No iterator name in FOR statement");
2692        do_simple_error_test("FOR a = 1 TO 10\nEND IF", "2:1: END IF without IF");
2693
2694        do_simple_error_test(
2695            "FOR i = \"a\" TO 3\nNEXT",
2696            "1:13: Cannot compare STRING and INTEGER with <=",
2697        );
2698        do_simple_error_test(
2699            "FOR i = 1 TO \"a\"\nNEXT",
2700            "1:11: Cannot compare INTEGER and STRING with <=",
2701        );
2702
2703        do_simple_error_test(
2704            "FOR i = \"b\" TO 7 STEP -8\nNEXT",
2705            "1:13: Cannot compare STRING and INTEGER with >=",
2706        );
2707        do_simple_error_test(
2708            "FOR i = 1 TO \"b\" STEP -8\nNEXT",
2709            "1:11: Cannot compare INTEGER and STRING with >=",
2710        );
2711    }
2712
2713    #[test]
2714    fn test_function_call_ok() {
2715        do_ok_test("x = 3\nOUT SUM(x, Sum%(4, 5), 1, sum())", &[], &["13"]);
2716    }
2717
2718    #[test]
2719    fn test_function_call_argless() {
2720        do_ok_test("x = FALSE: OUT x: y = ARGLESS: OUT y", &[], &["FALSE", "1234"]);
2721    }
2722
2723    #[test]
2724    fn test_function_call_errors() {
2725        do_simple_error_test("TYPE_CHECK", "1:1: TYPE_CHECK is not a command");
2726        do_simple_error_test("SUM(3)", "1:1: SUM is not a command");
2727        do_simple_error_test("OUT CLEAR", "1:5: CLEAR is not an array nor a function");
2728        do_simple_error_test("OUT CLEAR()", "1:5: CLEAR is not an array nor a function");
2729        do_simple_error_test("OUT OUT()", "1:5: OUT is not an array nor a function");
2730        do_simple_error_test("OUT OUT(3)", "1:5: OUT is not an array nor a function");
2731        do_simple_error_test("OUT SUM?()", "1:5: Incompatible type annotation in SUM? reference");
2732        do_simple_error_test(
2733            "OUT TYPE_CHECK()",
2734            "1:5: In call to TYPE_CHECK: expected no arguments nor parenthesis",
2735        );
2736    }
2737
2738    #[test]
2739    fn test_gosub_and_return() {
2740        do_ok_test(
2741            r#"
2742                i = 10
2743                GOSUB @sub
2744                GOSUB 20
2745                GOTO @end
2746                @sub 20: OUT i: i = i + 1: RETURN
2747                @end
2748            "#,
2749            &[],
2750            &["10", "11"],
2751        );
2752    }
2753
2754    #[test]
2755    fn test_gosub_and_return_nested() {
2756        do_ok_test(
2757            r#"
2758                GOTO @main
2759                @sub1: OUT 1: GOSUB @sub2: OUT 2: RETURN
2760                @sub2: OUT 3: RETURN
2761                @main
2762                GOSUB @sub1
2763            "#,
2764            &[],
2765            &["1", "3", "2"],
2766        );
2767    }
2768
2769    #[test]
2770    fn test_gosub_and_return_from_other() {
2771        do_ok_test(
2772            r#"
2773                GOTO @main
2774                @sub1: OUT 1: GOTO @sub2: RETURN
2775                @sub2: OUT 3: RETURN
2776                @main
2777                GOSUB @sub1
2778            "#,
2779            &[],
2780            &["1", "3"],
2781        );
2782    }
2783
2784    #[test]
2785    fn test_gosub_without_return() {
2786        do_ok_test(r#"GOSUB @sub: @sub: OUT 1"#, &[], &["1"]);
2787    }
2788
2789    #[test]
2790    fn test_gosub_and_return_errors() {
2791        do_simple_error_test("GOSUB @foo", "1:7: Unknown label foo");
2792        do_simple_error_test("RETURN", "1:1: No address to return to");
2793        do_simple_error_test("GOTO @foo\n@foo: RETURN", "2:7: No address to return to");
2794    }
2795
2796    #[test]
2797    fn test_goto_top_level_go_forward() {
2798        do_ok_test("OUT 1: GOTO @skip: OUT 2: @skip: OUT 3", &[], &["1", "3"]);
2799    }
2800
2801    #[test]
2802    fn test_goto_top_level_go_backward() {
2803        do_ok_test(
2804            "OUT 1: GOTO @skip: @before: OUT 2: GOTO @end: @skip: OUT 3: GOTO @before: @end",
2805            &[],
2806            &["1", "3", "2"],
2807        );
2808    }
2809
2810    #[test]
2811    fn test_goto_nested_can_go_up() {
2812        do_ok_test(
2813            "IF TRUE THEN: FOR i = 1 TO 10: OUT i: GOTO @out: NEXT: OUT 99: @out: OUT 100: END IF",
2814            &[],
2815            &["1", "100"],
2816        );
2817
2818        do_ok_test(
2819            "IF TRUE THEN: WHILE TRUE: OUT 1: GOTO @out: WEND: OUT 99: END IF: @out: OUT 100",
2820            &[],
2821            &["1", "100"],
2822        );
2823    }
2824
2825    #[test]
2826    fn test_goto_nested_can_go_down() {
2827        do_ok_test(
2828            "IF TRUE THEN: GOTO @sibling: OUT 1: END IF: IF TRUE THEN: @sibling: OUT 2: END IF",
2829            &[],
2830            &["2"],
2831        );
2832    }
2833
2834    #[test]
2835    fn test_goto_as_last_statement() {
2836        let captured_out = Rc::from(RefCell::from(vec![]));
2837        assert_eq!(
2838            StopReason::Exited(5),
2839            run(
2840                "i = 0: @a: IF i = 5 THEN: END i: END IF: i = i + 1: GOTO @a",
2841                &[],
2842                captured_out.clone()
2843            )
2844            .expect("Execution failed")
2845        );
2846        assert!(captured_out.borrow().is_empty());
2847    }
2848
2849    #[test]
2850    fn test_goto_middle_of_line() {
2851        do_ok_test("GOTO 20\nOUT 1: 20 OUT 2", &[], &["2"]);
2852    }
2853
2854    #[test]
2855    fn test_goto_errors() {
2856        do_simple_error_test("GOTO 10", "1:6: Unknown label 10");
2857        do_simple_error_test("GOTO @foo", "1:6: Unknown label foo");
2858    }
2859
2860    #[test]
2861    fn test_label_ok() {
2862        do_ok_test("OUT 1: 10: 20 OUT 2", &[], &["1", "2"]);
2863        do_ok_test("OUT 1: @foo: OUT 2", &[], &["1", "2"]);
2864    }
2865
2866    #[test]
2867    fn test_label_avoid_redefinition() {
2868        do_ok_test(
2869            "i = 0: @x: @y: i = i + 1: IF i = 2 THEN: GOTO @end: END IF: GOTO @x: @end",
2870            &[],
2871            &[],
2872        );
2873    }
2874
2875    #[test]
2876    fn test_label_duplicate() {
2877        do_simple_error_test("@foo: IF TRUE THEN: @foo: END IF", "1:21: Duplicate label foo");
2878        do_simple_error_test(
2879            "IF TRUE THEN: @foo: END IF: IF TRUE THEN: @foo: END IF",
2880            "1:43: Duplicate label foo",
2881        );
2882
2883        do_simple_error_test("@foo: @bar: @foo", "1:13: Duplicate label foo");
2884
2885        do_simple_error_test(
2886            r#"
2887            i = 0
2888            @a
2889                @b
2890                    @c
2891                        i = i + 1
2892                        IF i = 1 THEN: GOTO @b: END IF
2893                        @a
2894                        IF i = 2 THEN: GOTO @c: END IF
2895                        IF i = 3 THEN: GOTO @out: END IF
2896            @out
2897            "#,
2898            "8:25: Duplicate label a",
2899        );
2900    }
2901
2902    #[test]
2903    fn test_on_error_goto_line() {
2904        do_ok_test(
2905            r#"
2906            ON ERROR GOTO 100
2907            OUT 1
2908            OUT RAISEF("syntax")
2909            OUT 2
2910            100 OUT LAST_ERROR
2911            "#,
2912            &[],
2913            &["1", "4:17: In call to RAISEF: expected arg$"],
2914        );
2915    }
2916
2917    #[test]
2918    fn test_on_error_goto_label() {
2919        do_ok_test(
2920            r#"
2921            ON ERROR GOTO @foo
2922            OUT 1
2923            OUT RAISEF("syntax")
2924            OUT 2
2925            @foo
2926            OUT LAST_ERROR
2927            "#,
2928            &[],
2929            &["1", "4:17: In call to RAISEF: expected arg$"],
2930        );
2931    }
2932
2933    #[test]
2934    fn test_on_error_reset() {
2935        do_error_test(
2936            r#"
2937            ON ERROR GOTO @foo
2938            OUT 1
2939            OUT RAISEF("syntax")
2940            @foo
2941            ON ERROR GOTO 0
2942            OUT 2
2943            OUT RAISEF("syntax")
2944            "#,
2945            &[],
2946            &["1", "2"],
2947            "8:17: In call to RAISEF: expected arg$",
2948        );
2949    }
2950
2951    #[test]
2952    fn test_on_error_resume_next_line_function_failure() {
2953        do_ok_test(
2954            r#"
2955            ON ERROR RESUME NEXT
2956            OUT 1
2957            OUT RAISEF("syntax")
2958            OUT LAST_ERROR
2959            "#,
2960            &[],
2961            &["1", "4:17: In call to RAISEF: expected arg$"],
2962        );
2963    }
2964
2965    #[test]
2966    fn test_on_error_resume_next_line_command_failure() {
2967        do_ok_test(
2968            r#"
2969            ON ERROR RESUME NEXT
2970            OUT 1
2971            RAISE "syntax"
2972            OUT LAST_ERROR
2973            "#,
2974            &[],
2975            &["1", "4:13: In call to RAISE: expected arg$"],
2976        );
2977    }
2978
2979    #[test]
2980    fn test_on_error_resume_next_statement_function_failure() {
2981        do_ok_test(
2982            r#"
2983            ON ERROR RESUME NEXT
2984            OUT 1: OUT RAISEF("syntax"): OUT LAST_ERROR
2985            "#,
2986            &[],
2987            &["1", "3:24: In call to RAISEF: expected arg$"],
2988        );
2989    }
2990
2991    #[test]
2992    fn test_on_error_resume_next_statement_command_failure() {
2993        do_ok_test(
2994            r#"
2995            ON ERROR RESUME NEXT
2996            OUT 1: RAISE "syntax": OUT LAST_ERROR
2997            "#,
2998            &[],
2999            &["1", "3:20: In call to RAISE: expected arg$"],
3000        );
3001    }
3002
3003    #[test]
3004    fn test_on_error_types() {
3005        do_ok_test(
3006            r#"ON ERROR RESUME NEXT: OUT RAISEF("argument"): OUT LAST_ERROR"#,
3007            &[],
3008            &["1:27: In call to RAISEF: 1:34: Bad argument"],
3009        );
3010
3011        do_ok_test(
3012            r#"ON ERROR RESUME NEXT: OUT RAISEF("eval"): OUT LAST_ERROR"#,
3013            &[],
3014            &["1:27: In call to RAISEF: 1:34: Some eval error"],
3015        );
3016
3017        do_ok_test(
3018            r#"ON ERROR RESUME NEXT: OUT RAISEF("internal"): OUT LAST_ERROR"#,
3019            &[],
3020            &["1:27: In call to RAISEF: 1:34: Some internal error"],
3021        );
3022
3023        do_ok_test(
3024            r#"ON ERROR RESUME NEXT: OUT RAISEF("io"): OUT LAST_ERROR"#,
3025            &[],
3026            &["1:27: In call to RAISEF: Some I/O error"],
3027        );
3028
3029        do_ok_test(
3030            r#"ON ERROR RESUME NEXT: OUT RAISEF("syntax"): OUT LAST_ERROR"#,
3031            &[],
3032            &["1:27: In call to RAISEF: expected arg$"],
3033        );
3034    }
3035
3036    #[test]
3037    fn test_select_ok() {
3038        let code = r#"
3039            IN n
3040            SELECT CASE n
3041                CASE 1, 3, 5, 7, 9: OUT "Odd"
3042                CASE 0, 2, 4, 6, 8: OUT "Even"
3043                CASE 10 TO 20: OUT "Large"
3044                CASE IS < 0: OUT "Negative"
3045                CASE ELSE: OUT "Too large"
3046            END SELECT
3047        "#;
3048        do_ok_test(code, &["3"], &["Odd"]);
3049        do_ok_test(code, &["5"], &["Odd"]);
3050        do_ok_test(code, &["0"], &["Even"]);
3051        do_ok_test(code, &["8"], &["Even"]);
3052        do_ok_test(code, &["10"], &["Large"]);
3053        do_ok_test(code, &["15"], &["Large"]);
3054        do_ok_test(code, &["20"], &["Large"]);
3055        do_ok_test(code, &["-1"], &["Negative"]);
3056        do_ok_test(code, &["21"], &["Too large"]);
3057        do_ok_test(code, &["10000"], &["Too large"]);
3058    }
3059
3060    #[test]
3061    fn test_select_test_expression_evaluated_only_once() {
3062        let code = r#"
3063            SELECT CASE COUNT
3064                CASE 100: OUT "Not hit"
3065                CASE 200: OUT "Not hit"
3066                CASE ELSE: OUT "Giving up"
3067            END SELECT
3068            OUT COUNT
3069        "#;
3070        do_ok_test(code, &[], &["Giving up", "2"]);
3071    }
3072
3073    #[test]
3074    fn test_select_test_expression_evaluated_once_even_if_no_cases() {
3075        let code = r#"
3076            SELECT CASE COUNT
3077            END SELECT
3078            OUT COUNT
3079        "#;
3080        do_ok_test(code, &[], &["2"]);
3081    }
3082
3083    #[test]
3084    fn test_select_test_expression_evaluated_once_even_if_no_matches() {
3085        let code = r#"
3086            SELECT CASE COUNT
3087                CASE 0: OUT "Not hit"
3088                CASE 3
3089            END SELECT
3090            OUT COUNT
3091        "#;
3092        do_ok_test(code, &[], &["2"]);
3093    }
3094
3095    #[test]
3096    fn test_select_strings() {
3097        let code = r#"
3098            IN s$
3099            SELECT CASE s$
3100                CASE "exact": OUT "Exact match"
3101                CASE IS > "ZZZ": OUT "IS match"
3102                CASE "B" TO "Y": OUT "TO match"
3103            END SELECT
3104        "#;
3105        do_ok_test(code, &["exact"], &["Exact match"]);
3106        do_ok_test(code, &["ZZZ"], &[]);
3107        do_ok_test(code, &["ZZZa"], &["IS match"]);
3108        do_ok_test(code, &["A"], &[]);
3109        do_ok_test(code, &["B"], &["TO match"]);
3110        do_ok_test(code, &["M"], &["TO match"]);
3111        do_ok_test(code, &["Y"], &["TO match"]);
3112        do_ok_test(code, &["Z"], &[]);
3113    }
3114
3115    #[test]
3116    fn test_select_double_to_integer() {
3117        let code = r#"
3118            IN n#
3119            SELECT CASE n#
3120                CASE 2: OUT "OK 1"
3121                CASE IS > 5: OUT "OK 2"
3122            END SELECT
3123        "#;
3124        do_ok_test(code, &["1.9"], &[]);
3125        do_ok_test(code, &["2.0"], &["OK 1"]);
3126        do_ok_test(code, &["2.1"], &[]);
3127        do_ok_test(code, &["5.0"], &[]);
3128        do_ok_test(code, &["5.1"], &["OK 2"]);
3129    }
3130
3131    #[test]
3132    fn test_select_integer_to_double() {
3133        let code = r#"
3134            IN n%
3135            SELECT CASE n%
3136                CASE 2.0: OUT "OK 1"
3137                CASE 5.0, -1.0: OUT "OK 2"
3138                CASE 10.2 TO 11.8: OUT "OK 3"
3139            END SELECT
3140        "#;
3141        do_ok_test(code, &["2"], &["OK 1"]);
3142        do_ok_test(code, &["5"], &["OK 2"]);
3143        do_ok_test(code, &["-1"], &["OK 2"]);
3144        do_ok_test(code, &["10"], &[]);
3145        do_ok_test(code, &["11"], &["OK 3"]);
3146        do_ok_test(code, &["12"], &[]);
3147    }
3148
3149    #[test]
3150    fn test_select_no_test_var_leaks() {
3151        let code = r#"
3152            i = 0
3153            SELECT CASE 5 + 1
3154                CASE 6: i = i + 3
3155            END SELECT
3156
3157            SELECT CASE TRUE
3158                CASE TRUE: i = i + 1
3159            END SELECT
3160        "#;
3161
3162        let mut machine = Machine::default();
3163        assert_eq!(StopReason::Eof, block_on(machine.exec(&mut code.as_bytes())).unwrap());
3164        assert_eq!(1, machine.get_symbols().locals().len());
3165        assert_eq!(4, machine.get_var_as_int("I").unwrap());
3166    }
3167
3168    #[test]
3169    fn test_select_clear_does_not_cause_internal_error() {
3170        let code = r#"
3171            SELECT CASE 4
3172                CASE 4: CLEAR
3173            END SELECT
3174        "#;
3175
3176        let mut machine = Machine::default();
3177        machine.add_callable(ClearCommand::new());
3178        assert_eq!(StopReason::Eof, block_on(machine.exec(&mut code.as_bytes())).unwrap());
3179    }
3180
3181    #[test]
3182    fn test_select_nested() {
3183        let code = r#"
3184            i = 5
3185            SELECT CASE i
3186                CASE 5
3187                    OUT "OK 1"
3188                    i = 6
3189                    SELECT CASE i
3190                        CASE 6
3191                            OUT "OK 2"
3192                    END SELECT
3193                CASE 6
3194                    OUT "Not OK"
3195            END SELECT
3196        "#;
3197        do_ok_test(code, &[], &["OK 1", "OK 2"]);
3198    }
3199
3200    #[test]
3201    fn test_select_nested_indirectly() {
3202        let code = r#"
3203            i = 5
3204            SELECT CASE i
3205                CASE 5
3206                    OUT "OK 1"
3207                    GOSUB @another
3208                CASE 6
3209                    OUT "Not OK"
3210            END SELECT
3211            GOTO @end
3212            @another
3213            i = 6
3214            SELECT CASE i
3215                CASE 6
3216                    OUT "OK 2"
3217            END SELECT
3218            RETURN
3219            @end
3220        "#;
3221        do_ok_test(code, &[], &["OK 1", "OK 2"]);
3222    }
3223
3224    #[test]
3225    fn test_select_errors() {
3226        do_simple_error_test(
3227            "SELECT CASE\nEND SELECT",
3228            "1:12: No expression in SELECT CASE statement",
3229        );
3230        do_simple_error_test("SELECT CASE\nEND IF", "1:1: SELECT without END SELECT");
3231        do_simple_error_test("\n\n\nSELECT CASE 2\n", "4:1: SELECT without END SELECT");
3232
3233        do_simple_error_test(
3234            "SELECT CASE 2\nCASE FALSE\nEND SELECT",
3235            "2:6: Cannot compare INTEGER and BOOLEAN with =",
3236        );
3237    }
3238
3239    #[test]
3240    fn test_while_ok() {
3241        let code = r#"
3242            IN n
3243            WHILE n > 0
3244                OUT "n is"; n
3245                n = n - 1
3246            WEND
3247        "#;
3248        do_ok_test(code, &["0"], &[]);
3249        do_ok_test(code, &["3"], &["n is 3", "n is 2", "n is 1"]);
3250
3251        do_ok_test("WHILE FALSE\nOUT 1\nWEND", &[], &[]);
3252    }
3253
3254    #[test]
3255    fn test_while_errors() {
3256        do_simple_error_test("WHILE\nWEND", "1:6: No expression in WHILE statement");
3257        do_simple_error_test("WHILE\nEND IF", "1:6: No expression in WHILE statement");
3258
3259        do_simple_error_test("\n\n\nWHILE 2\n", "4:1: WHILE without WEND");
3260        do_simple_error_test("WHILE 3\nEND", "1:1: WHILE without WEND");
3261        do_simple_error_test("WHILE 3\nEND IF", "2:1: END IF without IF");
3262        do_simple_error_test("WHILE 2\nWEND", "1:7: WHILE requires a boolean condition");
3263    }
3264
3265    #[test]
3266    fn test_misc_comments_and_spaces() {
3267        let code = r#"
3268            REM This is the start of the program.
3269
3270            OUT "Hello" 'Some remark here.
3271
3272            IF TRUE THEN
3273
3274                OUT "Bye" 'And another remark here after a blank line.
3275            END IF
3276        "#;
3277        do_ok_test(code, &[], &["Hello", "Bye"]);
3278    }
3279
3280    #[test]
3281    fn test_top_level_syntax_errors_prevent_execution() {
3282        do_simple_error_test("+ b", "1:1: Unexpected + in statement");
3283        do_error_test(r#"OUT "a": + b: OUT "b""#, &[], &[], "1:10: Unexpected + in statement");
3284    }
3285
3286    #[test]
3287    fn test_inner_level_syntax_errors_prevent_execution() {
3288        do_simple_error_test("+ b", "1:1: Unexpected + in statement");
3289        do_error_test(
3290            r#"OUT "a": IF TRUE THEN: + b: END IF: OUT "b""#,
3291            &[],
3292            &[],
3293            "1:24: Unexpected + in statement",
3294        );
3295    }
3296
3297    #[test]
3298    fn test_top_level_semantic_errors_allow_execution() {
3299        do_simple_error_test(r#"OUT RAISEF("io")"#, "1:5: In call to RAISEF: Some I/O error");
3300        do_error_test(
3301            r#"OUT "a": OUT RAISEF("io"): OUT "b""#,
3302            &[],
3303            &["a"],
3304            "1:14: In call to RAISEF: Some I/O error",
3305        );
3306    }
3307
3308    #[test]
3309    fn test_top_level_compilation_errors_abort_execution() {
3310        do_simple_error_test("FOO BAR", "1:1: Unknown builtin FOO");
3311        do_error_test(r#"OUT "a": FOO BAR: OUT "b""#, &[], &[], "1:10: Unknown builtin FOO");
3312    }
3313
3314    #[test]
3315    fn test_inner_level_semantic_errors_allow_execution() {
3316        do_simple_error_test(
3317            r#"IF TRUE THEN: OUT RAISEF("io"): END IF"#,
3318            "1:19: In call to RAISEF: Some I/O error",
3319        );
3320        do_error_test(
3321            r#"OUT "a": IF TRUE THEN: OUT RAISEF("io"): END IF: OUT "b""#,
3322            &[],
3323            &["a"],
3324            "1:28: In call to RAISEF: Some I/O error",
3325        );
3326    }
3327
3328    #[test]
3329    fn test_inner_level_compilation_errors_abort_execution() {
3330        do_simple_error_test(r#"IF TRUE THEN: FOO BAR: END IF"#, "1:15: Unknown builtin FOO");
3331        do_error_test(
3332            r#"OUT "a": IF TRUE THEN: FOO BAR: END IF: OUT "b""#,
3333            &[],
3334            &[],
3335            "1:24: Unknown builtin FOO",
3336        );
3337    }
3338
3339    #[test]
3340    fn test_exec_shares_state() {
3341        let mut machine = Machine::default();
3342        assert_eq!(
3343            StopReason::Eof,
3344            block_on(machine.exec(&mut b"a = 10".as_ref())).expect("Execution failed")
3345        );
3346        assert_eq!(
3347            StopReason::Eof,
3348            block_on(machine.exec(&mut b"b = a".as_ref())).expect("Execution failed")
3349        );
3350    }
3351
3352    #[test]
3353    fn test_user_functions_default_return_values() {
3354        let code = r#"
3355            FUNCTION unannotated: END FUNCTION
3356            FUNCTION annotated_boolean?: END FUNCTION
3357            FUNCTION annotated_double#: END FUNCTION
3358            FUNCTION annotated_integer%: END FUNCTION
3359            FUNCTION annotated_string$: END FUNCTION
3360            OUT unannotated; unannotated%
3361            OUT annotated_boolean; annotated_boolean?
3362            OUT annotated_double; annotated_double#
3363            OUT annotated_integer; annotated_integer%
3364            OUT annotated_string; annotated_string$
3365        "#;
3366        do_ok_test(code, &[], &["0 0", "FALSE FALSE", "0 0", "0 0", " "]);
3367    }
3368
3369    #[test]
3370    fn test_user_functions_set_return_values() {
3371        let code = r#"
3372            FUNCTION unannotated: unannotated = 5: END FUNCTION
3373            FUNCTION annotated_boolean?: annotated_boolean = TRUE: END FUNCTION
3374            FUNCTION annotated_double#: annotated_double = 5.3: END FUNCTION
3375            FUNCTION annotated_integer%: annotated_integer = 8: END FUNCTION
3376            FUNCTION annotated_string$: annotated_string = "foo": END FUNCTION
3377            OUT unannotated; unannotated%
3378            OUT annotated_boolean; annotated_boolean?
3379            OUT annotated_double; annotated_double#
3380            OUT annotated_integer; annotated_integer%
3381            OUT annotated_string; annotated_string$
3382        "#;
3383        do_ok_test(code, &[], &["5 5", "TRUE TRUE", "5.3 5.3", "8 8", "foo foo"]);
3384    }
3385
3386    #[test]
3387    fn test_user_functions_mix_set_return_and_code() {
3388        let code = r#"
3389            FUNCTION f
3390                OUT "before"
3391                f = 3
3392                OUT "middle"
3393                f = 5
3394                OUT "after"
3395            END FUNCTION
3396            OUT f
3397        "#;
3398        do_ok_test(code, &[], &["before", "middle", "after", "5"]);
3399    }
3400
3401    #[test]
3402    fn test_user_functions_return_type_inconsistent() {
3403        let code = r#"
3404            FUNCTION f$: f = 3: END FUNCTION
3405        "#;
3406        do_error_test(
3407            code,
3408            &[],
3409            &[],
3410            "2:26: Cannot assign value of type INTEGER to variable of type STRING",
3411        );
3412    }
3413
3414    #[test]
3415    fn test_user_functions_call_type_inconsistent() {
3416        let code = r#"
3417            FUNCTION f$: END FUNCTION
3418            OUT f%
3419        "#;
3420        do_error_test(code, &[], &[], "3:17: Incompatible type annotation in f% reference");
3421    }
3422
3423    #[test]
3424    fn test_user_functions_local_namespace() {
3425        let code = r#"
3426            v1 = 3
3427            FUNCTION f1
3428                v1 = 5
3429                v2 = 7
3430            END FUNCTION
3431            FUNCTION f2
3432                v1$ = "foo"
3433                v2$ = "bar"
3434            END FUNCTION
3435            v2 = TRUE
3436            OUT f1; f2; v1
3437        "#;
3438        do_ok_test(code, &[], &["0 0 3"]);
3439    }
3440
3441    #[test]
3442    fn test_user_functions_global_namespace() {
3443        let code = r#"
3444            DIM SHARED v1 AS DOUBLE
3445            v1 = 8.7
3446            FUNCTION f1
3447                v1 = 9.2
3448            END FUNCTION
3449            FUNCTION f2#
3450                f2 = v1 + 1
3451            END FUNCTION
3452            OUT f1; f2; v1
3453        "#;
3454        do_ok_test(code, &[], &["0 9.7 8.7"]);
3455    }
3456
3457    #[test]
3458    fn test_user_functions_annotated_argument_types() {
3459        let code = r#"
3460            FUNCTION f(b?, d#, i%, s$)
3461                OUT b; d#; i; s$
3462            END FUNCTION
3463            OUT f(TRUE, 1.2, 3, "hi")
3464        "#;
3465        do_ok_test(code, &[], &["TRUE 1.2 3 hi", "0"]);
3466    }
3467
3468    #[test]
3469    fn test_user_functions_as_argument_types() {
3470        let code = r#"
3471            FUNCTION f(b AS BOOLEAN, d AS DOUBLE, i AS INTEGER, s AS STRING)
3472                OUT b?; d; i%; s
3473            END FUNCTION
3474            OUT f(TRUE, 1.2, 3, "hi")
3475        "#;
3476        do_ok_test(code, &[], &["TRUE 1.2 3 hi", "0"]);
3477    }
3478
3479    #[test]
3480    fn test_user_functions_argument_evaluation_order() {
3481        let code = r#"
3482            DIM SHARED g
3483            FUNCTION f(x, y)
3484                g = g + 1
3485                f = x + y
3486            END FUNCTION
3487            g = 0
3488            OUT f(1, 2); g
3489            g = 0
3490            OUT g; f(1, 2)
3491        "#;
3492        do_ok_test(code, &[], &["3 0", "1 3"]);
3493    }
3494
3495    #[test]
3496    fn test_user_functions_recursion() {
3497        let code = r#"
3498            DIM SHARED calls
3499            FUNCTION factorial(n)
3500                IF n = 1 THEN factorial = 1 ELSE factorial = n * factorial(n - 1)
3501                calls = calls + 1
3502            END FUNCTION
3503            OUT calls; factorial(5)
3504        "#;
3505        do_ok_test(code, &[], &["5 120"]);
3506    }
3507
3508    #[test]
3509    fn test_user_functions_syntax_error() {
3510        let code = r#"
3511            FUNCTION foo(n)
3512                OUT 5
3513            END FUNCTION
3514            OUT foo(3, 4)
3515        "#;
3516        do_error_test(code, &[], &[], "5:17: In call to FOO: expected n%");
3517    }
3518
3519    #[test]
3520    fn test_user_functions_call_as_command() {
3521        let code = r#"
3522            FUNCTION f: OUT "foo": END FUNCTION
3523            f
3524        "#;
3525        do_error_test(code, &[], &[], "3:13: F is not a command");
3526    }
3527
3528    #[test]
3529    fn test_user_subs_recursion() {
3530        let code = r#"
3531            DIM SHARED counter
3532            SUB count_down(prefix$)
3533                OUT prefix; counter
3534                IF counter > 1 THEN
3535                    counter = counter - 1
3536                    count_down prefix
3537                END IF
3538            END SUB
3539            counter = 3
3540            count_down "counter is"
3541        "#;
3542        do_ok_test(code, &[], &["counter is 3", "counter is 2", "counter is 1"]);
3543    }
3544
3545    #[test]
3546    fn test_user_subs_return_type_not_allowed() {
3547        let code = r#"
3548            SUB f$: f = 3: END SUB
3549        "#;
3550        do_error_test(
3551            code,
3552            &[],
3553            &[],
3554            "2:17: SUBs cannot return a value so type annotations are not allowed",
3555        );
3556    }
3557
3558    #[test]
3559    fn test_user_subs_call_as_function() {
3560        let code = r#"
3561            SUB f: OUT "foo": END SUB
3562            OUT f
3563        "#;
3564        do_error_test(code, &[], &[], "3:17: f is not an array nor a function");
3565    }
3566
3567    #[test]
3568    fn test_user_subs_syntax_error() {
3569        let code = r#"
3570            SUB foo(n)
3571                OUT 5
3572            END SUB
3573            foo 3, 4
3574        "#;
3575        do_error_test(code, &[], &[], "5:13: In call to FOO: expected n%");
3576    }
3577}