Skip to main content

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