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