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