1use crate::ast::*;
19use crate::bytecode::*;
20use crate::parser;
21use crate::reader::LineCol;
22use crate::syms::{CallableMetadata, CallableMetadataBuilder, Symbol, SymbolKey, Symbols};
23use std::borrow::Cow;
24use std::collections::HashMap;
25#[cfg(test)]
26use std::collections::HashSet;
27use std::io;
28
29mod args;
30pub use args::*;
31mod exprs;
32use exprs::{compile_expr, compile_expr_as_type};
33
34#[derive(Debug, thiserror::Error)]
36#[allow(missing_docs)] pub enum Error {
38 #[error("{0}: Cannot index array with {1} subscripts; need {2}")]
39 ArrayIndexSubscriptsError(LineCol, usize, usize),
40
41 #[error("{0}: Cannot {1} {2} and {3}")]
42 BinaryOpTypeError(LineCol, &'static str, ExprType, ExprType),
43
44 #[error("{0}: {} expected {}", .1.name(), .1.syntax())]
45 CallableSyntaxError(LineCol, CallableMetadata),
46
47 #[error("{0}: Duplicate label {1}")]
48 DuplicateLabel(LineCol, String),
49
50 #[error("{0}: Cannot assign value of type {1} to variable of type {2}")]
51 IncompatibleTypesInAssignment(LineCol, ExprType, ExprType),
52
53 #[error("{0}: Incompatible type annotation in {1} reference")]
54 IncompatibleTypeAnnotationInReference(LineCol, VarRef),
55
56 #[error("{0}: Cannot index non-array {1}")]
57 IndexNonArray(LineCol, String),
58
59 #[error("{0}: I/O error during compilation: {1}")]
60 IoError(LineCol, io::Error),
61
62 #[error("{0}: EXIT {1} outside of {1}")]
63 MisplacedExit(LineCol, &'static str),
64
65 #[error("{0}: {1} is not a command")]
66 NotACommand(LineCol, VarRef),
67
68 #[error("{0}: {1} is not a number")]
69 NotANumber(LineCol, ExprType),
70
71 #[error("{0}: Requires a reference, not a value")]
72 NotAReference(LineCol),
73
74 #[error("{0}: {1} is not a variable")]
75 NotAVariable(LineCol, VarRef),
76
77 #[error("{0}: {1} is not an array nor a function")]
78 NotArrayOrFunction(LineCol, VarRef),
79
80 #[error("{0}: {1}")]
81 ParseError(LineCol, String),
82
83 #[error("{0}: Cannot define already-defined symbol {1}")]
84 RedefinitionError(LineCol, SymbolKey),
85
86 #[error("{0}: Expected {2} but found {1}")]
87 TypeMismatch(LineCol, ExprType, ExprType),
88
89 #[error("{0}: Cannot {1} {2}")]
90 UnaryOpTypeError(LineCol, &'static str, ExprType),
91
92 #[error("{0}: Undefined symbol {1}")]
93 UndefinedSymbol(LineCol, VarRef),
94
95 #[error("{0}: Unknown label {1}")]
96 UnknownLabel(LineCol, String),
97}
98
99impl From<parser::Error> for Error {
100 fn from(value: parser::Error) -> Self {
101 match value {
102 parser::Error::Bad(pos, message) => Self::ParseError(pos, message),
103 parser::Error::Io(pos, e) => Self::IoError(pos, e),
104 }
105 }
106}
107
108pub type Result<T> = std::result::Result<T, Error>;
110
111#[derive(Clone)]
113enum SymbolPrototype {
114 Array(ExprType, usize),
116
117 BuiltinCallable(CallableMetadata, usize),
120
121 Callable(CallableMetadata),
123
124 Variable(ExprType),
126}
127
128struct SymbolsTable {
142 globals: HashMap<SymbolKey, SymbolPrototype>,
144
145 scopes: Vec<HashMap<SymbolKey, SymbolPrototype>>,
147}
148
149impl Default for SymbolsTable {
150 fn default() -> Self {
151 Self { globals: HashMap::default(), scopes: vec![HashMap::default()] }
152 }
153}
154
155impl From<HashMap<SymbolKey, SymbolPrototype>> for SymbolsTable {
156 fn from(globals: HashMap<SymbolKey, SymbolPrototype>) -> Self {
157 Self { globals, scopes: vec![HashMap::default()] }
158 }
159}
160
161impl From<&Symbols> for SymbolsTable {
162 fn from(syms: &Symbols) -> Self {
163 let globals = {
164 let mut globals = HashMap::default();
165
166 let callables = syms.callables();
167 let mut names = callables.keys().copied().collect::<Vec<&SymbolKey>>();
168 names.sort();
172
173 for (i, name) in names.into_iter().enumerate() {
174 let callable = callables.get(&name).unwrap();
175 let proto = SymbolPrototype::BuiltinCallable(callable.metadata().clone(), i);
176 globals.insert(name.clone(), proto);
177 }
178
179 globals
180 };
181
182 let mut scope = HashMap::default();
183 for (name, symbol) in syms.locals() {
184 let proto = match symbol {
185 Symbol::Array(array) => {
186 SymbolPrototype::Array(array.subtype(), array.dimensions().len())
187 }
188 Symbol::Callable(_) => {
189 unreachable!("Callables must only be global");
190 }
191 Symbol::Variable(var) => SymbolPrototype::Variable(var.as_exprtype()),
192 };
193 scope.insert(name.clone(), proto);
194 }
195
196 Self { globals, scopes: vec![scope] }
197 }
198}
199
200impl SymbolsTable {
201 fn enter_scope(&mut self) {
203 self.scopes.push(HashMap::default());
204 }
205
206 fn leave_scope(&mut self) {
208 let last = self.scopes.pop();
209 assert!(last.is_some(), "Must have at least one scope to pop");
210 assert!(!self.scopes.is_empty(), "Cannot pop the global scope");
211 }
212
213 fn contains_key(&mut self, key: &SymbolKey) -> bool {
215 self.scopes.last().unwrap().contains_key(key) || self.globals.contains_key(key)
216 }
217
218 fn get(&self, key: &SymbolKey) -> Option<&SymbolPrototype> {
220 let proto = self.scopes.last().unwrap().get(key);
221 if proto.is_some() {
222 return proto;
223 }
224
225 self.globals.get(key)
226 }
227
228 fn insert(&mut self, key: SymbolKey, proto: SymbolPrototype) {
231 debug_assert!(!self.globals.contains_key(&key), "Cannot redefine a symbol");
232 let previous = self.scopes.last_mut().unwrap().insert(key, proto);
233 debug_assert!(previous.is_none(), "Cannot redefine a symbol");
234 }
235
236 #[cfg(test)]
239 fn insert_builtin_callable(&mut self, key: SymbolKey, md: CallableMetadata) {
240 let next_upcall_index = self
241 .globals
242 .values()
243 .filter(|proto| matches!(proto, SymbolPrototype::BuiltinCallable(..)))
244 .count();
245
246 debug_assert!(!self.globals.contains_key(&key), "Cannot redefine a symbol");
247 let proto = SymbolPrototype::BuiltinCallable(md, next_upcall_index);
248 let previous = self.globals.insert(key, proto);
249 debug_assert!(previous.is_none(), "Cannot redefine a symbol");
250 }
251
252 fn insert_global(&mut self, key: SymbolKey, proto: SymbolPrototype) {
255 let previous = self.globals.insert(key, proto);
256 debug_assert!(previous.is_none(), "Cannot redefine a symbol");
257 }
258
259 fn remove(&mut self, key: SymbolKey) {
261 let previous = self.scopes.last_mut().unwrap().remove(&key);
262 debug_assert!(previous.is_some(), "Cannot unset a non-existing symbol");
263 }
264
265 #[cfg(test)]
267 fn keys(&self) -> HashSet<&SymbolKey> {
268 let mut keys = HashSet::default();
269 keys.extend(self.globals.keys());
270 keys.extend(self.scopes.last().unwrap().keys());
271 keys
272 }
273
274 fn upcalls(&self) -> Vec<SymbolKey> {
277 let mut builtins = self
278 .globals
279 .iter()
280 .filter_map(|(key, proto)| {
281 if let SymbolPrototype::BuiltinCallable(_md, upcall_index) = proto {
282 Some((upcall_index, key))
283 } else {
284 None
285 }
286 })
287 .collect::<Vec<(&usize, &SymbolKey)>>();
288 builtins.sort_by_key(|(upcall_index, _key)| *upcall_index);
289 builtins.into_iter().map(|(_upcall_index, key)| key.clone()).collect()
290 }
291}
292
293#[cfg_attr(test, derive(Debug, PartialEq))]
295enum Fixup {
296 CallAddr(String, LineCol),
297 Call(VarRef, LineCol),
298 GotoAddr(String, LineCol),
299 OnErrorGotoAddr(String, LineCol),
300}
301
302impl Fixup {
303 fn from_exit(target: String, span: ExitSpan) -> Self {
305 Self::GotoAddr(target, span.pos)
306 }
307
308 fn from_gosub(span: GotoSpan) -> Self {
310 Self::CallAddr(span.target, span.target_pos)
311 }
312
313 fn from_goto(span: GotoSpan) -> Self {
315 Self::GotoAddr(span.target, span.target_pos)
316 }
317
318 fn from_on_error(span: GotoSpan) -> Self {
320 Self::OnErrorGotoAddr(span.target, span.target_pos)
321 }
322}
323
324#[derive(Default)]
326struct Compiler {
327 exit_do_level: (usize, usize),
333
334 exit_for_level: (usize, usize),
340
341 selects: usize,
343
344 labels: HashMap<String, Address>,
346
347 fixups: HashMap<Address, Fixup>,
349
350 instrs: Vec<Instruction>,
352
353 data: Vec<Option<Expr>>,
355
356 symtable: SymbolsTable,
358
359 current_callable: Option<(SymbolKey, bool)>,
364
365 callable_spans: Vec<CallableSpan>,
367}
368
369impl Compiler {
370 fn emit(&mut self, instr: Instruction) -> Address {
372 self.instrs.push(instr);
373 self.instrs.len() - 1
374 }
375
376 fn exit_label_for_callable(name: &SymbolKey) -> String {
383 format!("0{}", name)
384 }
385
386 fn exit_label_for_loop(name: &'static str, level: (usize, usize)) -> String {
394 format!("0{}{}_{}", name, level.0, level.1)
395 }
396
397 fn return_key(name: &SymbolKey) -> SymbolKey {
399 SymbolKey::from(format!("0return_{}", name))
400 }
401
402 fn select_test_var_name(selects: usize) -> String {
408 format!("0select{}", selects)
409 }
410
411 fn maybe_cast(&mut self, target: ExprType, from: ExprType) -> ExprType {
416 match (target, from) {
417 (ExprType::Double, ExprType::Integer) => {
418 self.emit(Instruction::IntegerToDouble);
419 target
420 }
421 (ExprType::Integer, ExprType::Double) => {
422 self.emit(Instruction::DoubleToInteger);
423 target
424 }
425 _ => from,
426 }
427 }
428
429 fn compile_array_indices(
431 &mut self,
432 exp_nargs: usize,
433 args: Vec<Expr>,
434 name_pos: LineCol,
435 ) -> Result<()> {
436 let mut instrs = vec![];
437 match exprs::compile_array_indices(
438 &mut instrs,
439 &mut self.fixups,
440 &self.symtable,
441 exp_nargs,
442 args,
443 name_pos,
444 ) {
445 Ok(result) => {
446 self.instrs.append(&mut instrs);
447 Ok(result)
448 }
449 Err(e) => Err(e),
450 }
451 }
452
453 fn compile_array_assignment(&mut self, span: ArrayAssignmentSpan) -> Result<()> {
455 let key = SymbolKey::from(&span.vref.name);
456 let (atype, dims) = match self.symtable.get(&key) {
457 Some(SymbolPrototype::Array(atype, dims)) => (*atype, *dims),
458 Some(_) => {
459 return Err(Error::IndexNonArray(span.vref_pos, span.vref.name));
460 }
461 None => {
462 return Err(Error::UndefinedSymbol(span.vref_pos, span.vref.clone()));
463 }
464 };
465
466 if !span.vref.accepts(atype) {
467 return Err(Error::IncompatibleTypeAnnotationInReference(span.vref_pos, span.vref));
468 }
469
470 let etype = self.compile_expr(span.expr)?;
471 let etype = self.maybe_cast(atype, etype);
472 if etype != atype {
473 return Err(Error::IncompatibleTypesInAssignment(span.vref_pos, etype, atype));
474 }
475
476 let nargs = span.subscripts.len();
477 self.compile_array_indices(dims, span.subscripts, span.vref_pos)?;
478
479 self.emit(Instruction::ArrayAssignment(key, span.vref_pos, nargs));
480
481 Ok(())
482 }
483
484 fn compile_assignment(&mut self, vref: VarRef, vref_pos: LineCol, expr: Expr) -> Result<()> {
489 let mut key = SymbolKey::from(&vref.name);
490 let etype = self.compile_expr(expr)?;
491
492 if let Some(current_callable) = self.current_callable.as_ref()
493 && key == current_callable.0
494 {
495 key = Compiler::return_key(¤t_callable.0);
496 }
497
498 let vtype = match self.symtable.get(&key) {
499 Some(SymbolPrototype::Variable(vtype)) => *vtype,
500 Some(_) => return Err(Error::RedefinitionError(vref_pos, key)),
501 None => {
502 let key = key.clone();
505 let vtype = vref.ref_type.unwrap_or(etype);
506 self.symtable.insert(key, SymbolPrototype::Variable(vtype));
507 vtype
508 }
509 };
510
511 let etype = self.maybe_cast(vtype, etype);
512 if etype != vtype {
513 return Err(Error::IncompatibleTypesInAssignment(vref_pos, etype, vtype));
514 }
515
516 if let Some(ref_type) = vref.ref_type
517 && ref_type != vtype
518 {
519 return Err(Error::IncompatibleTypeAnnotationInReference(vref_pos, vref));
520 }
521
522 self.emit(Instruction::Assign(key));
523
524 Ok(())
525 }
526
527 fn compile_callable(&mut self, span: CallableSpan) -> Result<()> {
529 let key = SymbolKey::from(&span.name.name);
530 if self.symtable.contains_key(&key) {
531 return Err(Error::RedefinitionError(span.name_pos, key));
532 }
533
534 let mut syntax = vec![];
535 for (i, param) in span.params.iter().enumerate() {
536 let sep = if i == span.params.len() - 1 {
537 ArgSepSyntax::End
538 } else {
539 ArgSepSyntax::Exactly(ArgSep::Long)
540 };
541 syntax.push(SingularArgSyntax::RequiredValue(
542 RequiredValueSyntax {
543 name: Cow::Owned(param.name.to_owned()),
544 vtype: param.ref_type.unwrap_or(ExprType::Integer),
545 },
546 sep,
547 ));
548 }
549
550 let mut builder = CallableMetadataBuilder::new_dynamic(span.name.name.to_owned())
551 .with_dynamic_syntax(vec![(syntax, None)]);
552 if let Some(ctype) = span.name.ref_type {
553 builder = builder.with_return_type(ctype);
554 }
555 self.symtable.insert_global(key, SymbolPrototype::Callable(builder.build()));
556 self.callable_spans.push(span);
557
558 Ok(())
559 }
560
561 fn compile_dim(&mut self, span: DimSpan) -> Result<()> {
563 let key = SymbolKey::from(&span.name);
564 if self.symtable.contains_key(&key) {
565 return Err(Error::RedefinitionError(span.name_pos, key));
566 }
567
568 self.emit(Instruction::Dim(DimISpan {
569 name: key.clone(),
570 shared: span.shared,
571 vtype: span.vtype,
572 }));
573 if span.shared {
574 self.symtable.insert_global(key, SymbolPrototype::Variable(span.vtype));
575 } else {
576 self.symtable.insert(key, SymbolPrototype::Variable(span.vtype));
577 }
578
579 Ok(())
580 }
581
582 fn compile_do(&mut self, span: DoSpan) -> Result<()> {
584 self.exit_do_level.1 += 1;
585
586 let end_pc;
587 match span.guard {
588 DoGuard::Infinite => {
589 let start_pc = self.instrs.len();
590 self.compile_many(span.body)?;
591 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
592 }
593
594 DoGuard::PreUntil(guard) => {
595 let start_pc = self.instrs.len();
596 self.compile_expr_guard(guard)?;
597 let jump_pc = self.emit(Instruction::Nop);
598 self.compile_many(span.body)?;
599 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
600 self.instrs[jump_pc] = Instruction::JumpIfTrue(self.instrs.len());
601 }
602
603 DoGuard::PreWhile(guard) => {
604 let start_pc = self.instrs.len();
605 self.compile_expr_guard(guard)?;
606 let jump_pc = self.emit(Instruction::Nop);
607 self.compile_many(span.body)?;
608 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
609 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.instrs.len());
610 }
611
612 DoGuard::PostUntil(guard) => {
613 let start_pc = self.instrs.len();
614 self.compile_many(span.body)?;
615 self.compile_expr_guard(guard)?;
616 end_pc = self.emit(Instruction::JumpIfNotTrue(start_pc));
617 }
618
619 DoGuard::PostWhile(guard) => {
620 let start_pc = self.instrs.len();
621 self.compile_many(span.body)?;
622 self.compile_expr_guard(guard)?;
623 end_pc = self.emit(Instruction::JumpIfTrue(start_pc));
624 }
625 }
626
627 let existing =
628 self.labels.insert(Compiler::exit_label_for_loop("do", self.exit_do_level), end_pc + 1);
629 assert!(existing.is_none(), "Auto-generated label must be unique");
630 self.exit_do_level.1 -= 1;
631 if self.exit_do_level.1 == 0 {
632 self.exit_do_level.0 += 1;
633 }
634
635 Ok(())
636 }
637
638 fn compile_for(&mut self, span: ForSpan) -> Result<()> {
640 debug_assert!(
641 span.iter.ref_type.is_none()
642 || span.iter.ref_type.unwrap() == ExprType::Double
643 || span.iter.ref_type.unwrap() == ExprType::Integer
644 );
645
646 self.exit_for_level.1 += 1;
647
648 if span.iter_double && span.iter.ref_type.is_none() {
649 let key = SymbolKey::from(&span.iter.name);
650 let skip_pc = self.emit(Instruction::Nop);
651
652 if self.symtable.get(&key).is_none() {
653 self.emit(Instruction::Dim(DimISpan {
654 name: key.clone(),
655 shared: false,
656 vtype: ExprType::Double,
657 }));
658 self.symtable.insert(key.clone(), SymbolPrototype::Variable(ExprType::Double));
659 }
660
661 self.instrs[skip_pc] = Instruction::JumpIfDefined(JumpIfDefinedISpan {
662 var: key,
663 addr: self.instrs.len(),
664 });
665 }
666
667 self.compile_assignment(span.iter.clone(), span.iter_pos, span.start)?;
668
669 let start_pc = self.instrs.len();
670 let end_etype = self.compile_expr(span.end)?;
671 match end_etype {
672 ExprType::Boolean => (),
673 _ => panic!("Synthesized end condition for FOR must yield a boolean"),
674 };
675 let jump_pc = self.emit(Instruction::Nop);
676
677 self.compile_many(span.body)?;
678
679 self.compile_assignment(span.iter.clone(), span.iter_pos, span.next)?;
680
681 let end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
682
683 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.instrs.len());
684
685 let existing = self
686 .labels
687 .insert(Compiler::exit_label_for_loop("for", self.exit_for_level), end_pc + 1);
688 assert!(existing.is_none(), "Auto-generated label must be unique");
689 self.exit_for_level.1 -= 1;
690 if self.exit_for_level.1 == 0 {
691 self.exit_for_level.0 += 1;
692 }
693
694 Ok(())
695 }
696
697 fn compile_if(&mut self, span: IfSpan) -> Result<()> {
699 let mut end_pcs = vec![];
700
701 let mut iter = span.branches.into_iter();
702 let mut next = iter.next();
703 while let Some(branch) = next {
704 let next2 = iter.next();
705
706 self.compile_expr_guard(branch.guard)?;
707 let jump_pc = self.emit(Instruction::Nop);
708 self.compile_many(branch.body)?;
709
710 if next2.is_some() {
711 end_pcs.push(self.instrs.len());
712 self.emit(Instruction::Nop);
713 }
714
715 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.instrs.len());
716
717 next = next2;
718 }
719
720 for end_pc in end_pcs {
721 self.instrs[end_pc] = Instruction::Jump(JumpISpan { addr: self.instrs.len() });
722 }
723
724 Ok(())
725 }
726
727 fn compile_on_error(&mut self, span: OnErrorSpan) {
729 match span {
730 OnErrorSpan::Goto(span) => {
731 let goto_pc = self.emit(Instruction::Nop);
732 self.fixups.insert(goto_pc, Fixup::from_on_error(span));
733 }
734 OnErrorSpan::Reset => {
735 self.emit(Instruction::SetErrorHandler(ErrorHandlerISpan::None));
736 }
737 OnErrorSpan::ResumeNext => {
738 self.emit(Instruction::SetErrorHandler(ErrorHandlerISpan::ResumeNext));
739 }
740 }
741 }
742
743 fn compile_case_guards(test_vref: &VarRef, guards: Vec<CaseGuardSpan>) -> Option<Expr> {
746 let mut expr = None;
747 for guard in guards {
748 let one_expr = match guard {
749 CaseGuardSpan::Is(relop, expr) => {
750 let pos = expr.start_pos();
751 let test_expr = Expr::Symbol(SymbolSpan { vref: test_vref.clone(), pos });
752 let binop = Box::from(BinaryOpSpan { lhs: test_expr, rhs: expr, pos });
753 match relop {
754 CaseRelOp::Equal => Expr::Equal(binop),
755 CaseRelOp::NotEqual => Expr::NotEqual(binop),
756 CaseRelOp::Less => Expr::Less(binop),
757 CaseRelOp::LessEqual => Expr::LessEqual(binop),
758 CaseRelOp::Greater => Expr::Greater(binop),
759 CaseRelOp::GreaterEqual => Expr::GreaterEqual(binop),
760 }
761 }
762
763 CaseGuardSpan::To(from_expr, to_expr) => {
764 let from_pos = from_expr.start_pos();
765 let to_pos = to_expr.start_pos();
766 let test_expr =
767 Expr::Symbol(SymbolSpan { vref: test_vref.clone(), pos: from_pos });
768 Expr::And(Box::from(BinaryOpSpan {
769 lhs: Expr::GreaterEqual(Box::from(BinaryOpSpan {
770 lhs: test_expr.clone(),
771 rhs: from_expr,
772 pos: from_pos,
773 })),
774 rhs: Expr::LessEqual(Box::from(BinaryOpSpan {
775 lhs: test_expr,
776 rhs: to_expr,
777 pos: to_pos,
778 })),
779 pos: from_pos,
780 }))
781 }
782 };
783
784 expr = match expr {
785 None => Some(one_expr),
786 Some(expr) => {
787 let pos = expr.start_pos();
788 Some(Expr::Or(Box::from(BinaryOpSpan { lhs: expr, rhs: one_expr, pos })))
789 }
790 };
791 }
792 expr
793 }
794
795 fn compile_select(&mut self, span: SelectSpan) -> Result<()> {
797 let mut end_pcs = vec![];
798
799 self.selects += 1;
800 let test_vref = VarRef::new(Compiler::select_test_var_name(self.selects), None);
801 self.compile_assignment(test_vref.clone(), span.expr.start_pos(), span.expr)?;
802
803 let mut iter = span.cases.into_iter();
804 let mut next = iter.next();
805 while let Some(case) = next {
806 let next2 = iter.next();
807
808 match Compiler::compile_case_guards(&test_vref, case.guards) {
809 None => {
810 self.compile_many(case.body)?;
811 }
812 Some(guard) => {
813 self.compile_expr_guard(guard)?;
814 let jump_pc = self.emit(Instruction::Nop);
815 self.compile_many(case.body)?;
816
817 if next2.is_some() {
818 end_pcs.push(self.instrs.len());
819 self.emit(Instruction::Nop);
820 }
821
822 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.instrs.len());
823 }
824 }
825
826 next = next2;
827 }
828
829 for end_pc in end_pcs {
830 self.instrs[end_pc] = Instruction::Jump(JumpISpan { addr: self.instrs.len() });
831 }
832
833 let test_key = SymbolKey::from(test_vref.name);
834 self.emit(Instruction::Unset(UnsetISpan { name: test_key.clone(), pos: span.end_pos }));
835 self.symtable.remove(test_key);
836
837 Ok(())
838 }
839
840 fn compile_while(&mut self, span: WhileSpan) -> Result<()> {
842 let start_pc = self.instrs.len();
843 self.compile_expr_guard(span.expr)?;
844 let jump_pc = self.emit(Instruction::Nop);
845
846 self.compile_many(span.body)?;
847
848 self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
849
850 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.instrs.len());
851
852 Ok(())
853 }
854
855 fn compile_expr(&mut self, expr: Expr) -> Result<ExprType> {
858 match compile_expr(&mut self.instrs, &mut self.fixups, &self.symtable, expr, false) {
859 Ok(result) => Ok(result),
860 Err(e) => Err(e),
861 }
862 }
863
864 fn compile_expr_as_type(&mut self, expr: Expr, target: ExprType) -> Result<()> {
867 compile_expr_as_type(&mut self.instrs, &mut self.fixups, &self.symtable, expr, target)?;
868 Ok(())
869 }
870
871 fn compile_expr_guard(&mut self, guard: Expr) -> Result<()> {
874 let pos = guard.start_pos();
875 match self.compile_expr(guard)? {
876 ExprType::Boolean => Ok(()),
877 etype => Err(Error::TypeMismatch(pos, etype, ExprType::Boolean)),
878 }
879 }
880
881 fn compile_one(&mut self, stmt: Statement) -> Result<()> {
883 match stmt {
884 Statement::ArrayAssignment(span) => {
885 self.compile_array_assignment(span)?;
886 }
887
888 Statement::Assignment(span) => {
889 self.compile_assignment(span.vref, span.vref_pos, span.expr)?;
890 }
891
892 Statement::Call(span) => {
893 let key = SymbolKey::from(&span.vref.name);
894 let (md, upcall_index) = match self.symtable.get(&key) {
895 Some(SymbolPrototype::BuiltinCallable(md, upcall_index)) => {
896 if md.is_function() {
897 return Err(Error::NotACommand(span.vref_pos, span.vref));
898 }
899 (md.clone(), Some(*upcall_index))
900 }
901
902 Some(SymbolPrototype::Callable(md)) => {
903 if md.is_function() {
904 return Err(Error::NotACommand(span.vref_pos, span.vref));
905 }
906 (md.clone(), None)
907 }
908
909 Some(_) => {
910 return Err(Error::NotACommand(span.vref_pos, span.vref));
911 }
912
913 None => return Err(Error::UndefinedSymbol(span.vref_pos, span.vref)),
914 };
915
916 let name_pos = span.vref_pos;
917 let nargs = compile_command_args(
918 &md,
919 &mut self.instrs,
920 &mut self.fixups,
921 &mut self.symtable,
922 name_pos,
923 span.args,
924 )?;
925 if let Some(upcall_index) = upcall_index {
926 self.emit(Instruction::BuiltinCall(BuiltinCallISpan {
927 name: key,
928 name_pos: span.vref_pos,
929 upcall_index,
930 nargs,
931 }));
932 } else {
933 let call_pc = self.emit(Instruction::Nop);
934 self.fixups.insert(call_pc, Fixup::Call(span.vref, span.vref_pos));
935 }
936 }
937
938 Statement::Callable(span) => {
939 self.compile_callable(span)?;
940 }
941
942 Statement::Data(mut span) => {
943 self.data.append(&mut span.values);
944 }
945
946 Statement::Dim(span) => {
947 self.compile_dim(span)?;
948 }
949
950 Statement::DimArray(span) => {
951 let key = SymbolKey::from(&span.name);
952 if self.symtable.contains_key(&key) {
953 return Err(Error::RedefinitionError(span.name_pos, key));
954 }
955
956 let nargs = span.dimensions.len();
957 for arg in span.dimensions.into_iter().rev() {
958 self.compile_expr_as_type(arg, ExprType::Integer)?;
959 }
960 self.emit(Instruction::DimArray(DimArrayISpan {
961 name: key.clone(),
962 name_pos: span.name_pos,
963 shared: span.shared,
964 dimensions: nargs,
965 subtype: span.subtype,
966 subtype_pos: span.subtype_pos,
967 }));
968
969 if span.shared {
970 self.symtable.insert_global(key, SymbolPrototype::Array(span.subtype, nargs));
971 } else {
972 self.symtable.insert(key, SymbolPrototype::Array(span.subtype, nargs));
973 }
974 }
975
976 Statement::Do(span) => {
977 self.compile_do(span)?;
978 }
979
980 Statement::End(span) => match span.code {
981 Some(expr) => {
982 self.compile_expr_as_type(expr, ExprType::Integer)?;
983 self.emit(Instruction::End(true));
984 }
985 None => {
986 self.emit(Instruction::End(false));
987 }
988 },
989
990 Statement::ExitDo(span) => {
991 if self.exit_do_level.1 == 0 {
992 return Err(Error::MisplacedExit(span.pos, "DO"));
993 }
994 let exit_do_pc = self.emit(Instruction::Nop);
995 self.fixups.insert(
996 exit_do_pc,
997 Fixup::from_exit(Compiler::exit_label_for_loop("do", self.exit_do_level), span),
998 );
999 }
1000
1001 Statement::ExitFor(span) => {
1002 if self.exit_for_level.1 == 0 {
1003 return Err(Error::MisplacedExit(span.pos, "FOR"));
1004 }
1005 let exit_for_pc = self.emit(Instruction::Nop);
1006 self.fixups.insert(
1007 exit_for_pc,
1008 Fixup::from_exit(
1009 Compiler::exit_label_for_loop("for", self.exit_for_level),
1010 span,
1011 ),
1012 );
1013 }
1014
1015 Statement::ExitFunction(span) => {
1016 let Some(current_callable) = self.current_callable.as_ref() else {
1017 return Err(Error::MisplacedExit(span.pos, "FUNCTION"));
1018 };
1019 if !current_callable.1 {
1020 return Err(Error::MisplacedExit(span.pos, "FUNCTION"));
1021 }
1022
1023 let exit_label = Compiler::exit_label_for_callable(¤t_callable.0);
1024 let exit_function_pc = self.emit(Instruction::Nop);
1025 self.fixups.insert(exit_function_pc, Fixup::from_exit(exit_label, span));
1026 }
1027
1028 Statement::ExitSub(span) => {
1029 let Some(current_callable) = self.current_callable.as_ref() else {
1030 return Err(Error::MisplacedExit(span.pos, "SUB"));
1031 };
1032 if current_callable.1 {
1033 return Err(Error::MisplacedExit(span.pos, "SUB"));
1034 }
1035
1036 let exit_label = Compiler::exit_label_for_callable(¤t_callable.0);
1037 let exit_sub_pc = self.emit(Instruction::Nop);
1038 self.fixups.insert(exit_sub_pc, Fixup::from_exit(exit_label, span));
1039 }
1040
1041 Statement::For(span) => {
1042 self.compile_for(span)?;
1043 }
1044
1045 Statement::Gosub(span) => {
1046 let gosub_pc = self.emit(Instruction::Nop);
1047 self.fixups.insert(gosub_pc, Fixup::from_gosub(span));
1048 }
1049
1050 Statement::Goto(span) => {
1051 let goto_pc = self.emit(Instruction::Nop);
1052 self.fixups.insert(goto_pc, Fixup::from_goto(span));
1053 }
1054
1055 Statement::If(span) => {
1056 self.compile_if(span)?;
1057 }
1058
1059 Statement::Label(span) => {
1060 if self.labels.insert(span.name.clone(), self.instrs.len()).is_some() {
1061 return Err(Error::DuplicateLabel(span.name_pos, span.name));
1062 }
1063 }
1064
1065 Statement::OnError(span) => {
1066 self.compile_on_error(span);
1067 }
1068
1069 Statement::Return(span) => {
1070 self.emit(Instruction::Return(span.pos));
1071 }
1072
1073 Statement::Select(span) => {
1074 self.compile_select(span)?;
1075 }
1076
1077 Statement::While(span) => {
1078 self.compile_while(span)?;
1079 }
1080 }
1081
1082 Ok(())
1083 }
1084
1085 fn compile_many(&mut self, stmts: Vec<Statement>) -> Result<()> {
1088 for stmt in stmts {
1089 self.compile_one(stmt)?;
1090 }
1091
1092 Ok(())
1093 }
1094
1095 fn compile_callables(&mut self) -> Result<HashMap<SymbolKey, usize>> {
1100 if self.callable_spans.is_empty() {
1101 return Ok(HashMap::default());
1102 }
1103
1104 let end = self.emit(Instruction::Nop);
1105
1106 let mut callables = HashMap::with_capacity(self.callable_spans.len());
1107 let callable_spans = std::mem::take(&mut self.callable_spans);
1108 for span in callable_spans {
1109 let pc = self.instrs.len();
1110
1111 let key = SymbolKey::from(span.name.name);
1112 let return_value = Compiler::return_key(&key);
1113 match span.name.ref_type {
1114 Some(return_type) => {
1115 self.emit(Instruction::EnterScope);
1116 self.symtable.enter_scope();
1117
1118 self.emit(Instruction::Dim(DimISpan {
1119 name: return_value.clone(),
1120 shared: false,
1121 vtype: return_type,
1122 }));
1123 self.symtable
1124 .insert(return_value.clone(), SymbolPrototype::Variable(return_type));
1125
1126 for param in span.params {
1127 let key = SymbolKey::from(param.name);
1128 let ptype = param.ref_type.unwrap_or(ExprType::Integer);
1129 self.emit(Instruction::Assign(key.clone()));
1130 self.symtable.insert(key, SymbolPrototype::Variable(ptype));
1131 }
1132
1133 debug_assert!(self.current_callable.is_none(), "Callables cannot be nested");
1134 self.current_callable = Some((key.clone(), true));
1135 self.compile_many(span.body)?;
1136 self.current_callable = None;
1137
1138 let load_inst = match return_type {
1139 ExprType::Boolean => Instruction::LoadBoolean,
1140 ExprType::Double => Instruction::LoadDouble,
1141 ExprType::Integer => Instruction::LoadInteger,
1142 ExprType::Text => Instruction::LoadString,
1143 };
1144 let epilogue_pc = self.emit(load_inst(return_value.clone(), span.end_pos));
1145
1146 self.emit(Instruction::LeaveScope);
1147 self.symtable.leave_scope();
1148 self.emit(Instruction::Return(span.end_pos));
1149
1150 let existing =
1151 self.labels.insert(Compiler::exit_label_for_callable(&key), epilogue_pc);
1152 assert!(existing.is_none(), "Auto-generated label must be unique");
1153
1154 callables.insert(key, pc);
1155 }
1156 None => {
1157 self.emit(Instruction::EnterScope);
1158 self.symtable.enter_scope();
1159
1160 for param in span.params {
1161 let key = SymbolKey::from(param.name);
1162 let ptype = param.ref_type.unwrap_or(ExprType::Integer);
1163 self.emit(Instruction::Assign(key.clone()));
1164 self.symtable.insert(key, SymbolPrototype::Variable(ptype));
1165 }
1166
1167 debug_assert!(self.current_callable.is_none(), "Callables cannot be nested");
1168 self.current_callable = Some((key.clone(), false));
1169 self.compile_many(span.body)?;
1170 self.current_callable = None;
1171
1172 let epilogue_pc = self.emit(Instruction::LeaveScope);
1173 self.symtable.leave_scope();
1174 self.emit(Instruction::Return(span.end_pos));
1175
1176 let existing =
1177 self.labels.insert(Compiler::exit_label_for_callable(&key), epilogue_pc);
1178 assert!(existing.is_none(), "Auto-generated label must be unique");
1179
1180 callables.insert(key, pc);
1181 }
1182 }
1183 }
1184
1185 self.instrs[end] = Instruction::Jump(JumpISpan { addr: self.instrs.len() });
1186
1187 Ok(callables)
1188 }
1189
1190 fn get_label_addr(
1192 labels: &HashMap<String, Address>,
1193 label: String,
1194 pos: LineCol,
1195 ) -> Result<usize> {
1196 match labels.get(&label) {
1197 Some(addr) => Ok(*addr),
1198 None => Err(Error::UnknownLabel(pos, label.to_owned())),
1199 }
1200 }
1201
1202 #[allow(clippy::wrong_self_convention)]
1204 fn to_image(mut self) -> Result<(Image, SymbolsTable)> {
1205 let callables = self.compile_callables()?;
1206
1207 for (pc, fixup) in self.fixups {
1208 let new_instr = match fixup {
1209 Fixup::CallAddr(label, pos) => {
1210 let addr = Self::get_label_addr(&self.labels, label, pos)?;
1211 Instruction::Call(JumpISpan { addr })
1212 }
1213
1214 Fixup::Call(vref, pos) => {
1215 let key = SymbolKey::from(&vref.name);
1216 let Some(addr) = callables.get(&key) else {
1217 return Err(Error::UndefinedSymbol(pos, vref));
1218 };
1219 Instruction::Call(JumpISpan { addr: *addr })
1220 }
1221
1222 Fixup::GotoAddr(label, pos) => {
1223 let addr = Self::get_label_addr(&self.labels, label, pos)?;
1224 Instruction::Jump(JumpISpan { addr })
1225 }
1226
1227 Fixup::OnErrorGotoAddr(label, pos) => {
1228 let addr = Self::get_label_addr(&self.labels, label, pos)?;
1229 Instruction::SetErrorHandler(ErrorHandlerISpan::Jump(addr))
1230 }
1231 };
1232 debug_assert_eq!(
1233 std::mem::discriminant(&Instruction::Nop),
1234 std::mem::discriminant(&self.instrs[pc]),
1235 "Fixup target address must contain a Nop instruction",
1236 );
1237 self.instrs[pc] = new_instr;
1238 }
1239
1240 let data = compile_data(self.data);
1241
1242 let image = Image { upcalls: self.symtable.upcalls(), instrs: self.instrs, data };
1243 Ok((image, self.symtable))
1244 }
1245}
1246
1247fn compile_data(data: Vec<Option<Expr>>) -> Vec<Option<Value>> {
1249 data.into_iter()
1250 .map(|expr| match expr {
1251 None => None,
1252 Some(Expr::Boolean(span)) => Some(Value::Boolean(span.value)),
1253 Some(Expr::Double(span)) => Some(Value::Double(span.value)),
1254 Some(Expr::Integer(span)) => Some(Value::Integer(span.value)),
1255 Some(Expr::Text(span)) => Some(Value::Text(span.value)),
1256 _ => unreachable!("Valid data types enforced at parse time"),
1257 })
1258 .collect()
1259}
1260
1261fn compile_aux(input: &mut dyn io::Read, symtable: SymbolsTable) -> Result<(Image, SymbolsTable)> {
1266 let mut compiler = Compiler { symtable, ..Default::default() };
1267 for stmt in parser::parse(input) {
1268 compiler.compile_one(stmt?)?;
1269 }
1270 compiler.to_image()
1271}
1272
1273pub fn compile(input: &mut dyn io::Read, syms: &Symbols) -> Result<Image> {
1280 compile_aux(input, SymbolsTable::from(syms)).map(|(image, _symtable)| image)
1281}
1282
1283#[cfg(test)]
1284mod testutils {
1285 use super::*;
1286 use crate::syms::CallableMetadataBuilder;
1287
1288 #[derive(Default)]
1290 #[must_use]
1291 pub(crate) struct Tester {
1292 source: String,
1293 symtable: SymbolsTable,
1294 exp_upcalls: Vec<SymbolKey>,
1295 }
1296
1297 impl Tester {
1298 pub(crate) fn define<K: Into<SymbolKey>>(
1300 mut self,
1301 name: K,
1302 proto: SymbolPrototype,
1303 ) -> Self {
1304 self.symtable.insert(name.into(), proto);
1305 self
1306 }
1307
1308 pub(crate) fn define_callable(mut self, builder: CallableMetadataBuilder) -> Self {
1312 let md = builder.test_build();
1313 let key = SymbolKey::from(md.name());
1314 self.symtable.insert_builtin_callable(key.clone(), md);
1315 self.exp_upcalls.push(key);
1316 self
1317 }
1318
1319 pub(crate) fn parse(mut self, input: &str) -> Self {
1321 self.source.push_str(input);
1322 self.source.push('\n');
1323 self
1324 }
1325
1326 pub(crate) fn compile(self) -> Checker {
1329 Checker {
1330 result: compile_aux(&mut self.source.as_bytes(), self.symtable),
1331 exp_error: None,
1332 ignore_instrs: false,
1333 exp_upcalls: self.exp_upcalls,
1334 exp_instrs: vec![],
1335 exp_data: vec![],
1336 exp_symtable: HashMap::default(),
1337 }
1338 }
1339 }
1340
1341 #[must_use]
1343 pub(crate) struct Checker {
1344 result: Result<(Image, SymbolsTable)>,
1345 exp_error: Option<String>,
1346 ignore_instrs: bool,
1347 exp_upcalls: Vec<SymbolKey>,
1348 exp_instrs: Vec<Instruction>,
1349 exp_data: Vec<Option<Value>>,
1350 exp_symtable: HashMap<SymbolKey, SymbolPrototype>,
1351 }
1352
1353 impl Checker {
1354 pub(crate) fn expect_instr(mut self, addr: Address, instr: Instruction) -> Self {
1359 if addr >= self.exp_instrs.len() {
1360 self.exp_instrs.resize_with(addr + 1, || Instruction::Nop);
1361 }
1362 self.exp_instrs[addr] = instr;
1363 self
1364 }
1365
1366 pub(crate) fn expect_symtable(mut self, key: SymbolKey, proto: SymbolPrototype) -> Self {
1368 let previous = self.exp_symtable.insert(key, proto);
1369 assert!(previous.is_none());
1370 self
1371 }
1372
1373 pub(crate) fn ignore_instrs(mut self) -> Self {
1375 self.ignore_instrs = true;
1376 self
1377 }
1378
1379 pub(crate) fn expect_datum(mut self, datum: Option<Value>) -> Self {
1381 self.exp_data.push(datum);
1382 self
1383 }
1384
1385 pub(crate) fn expect_err<S: Into<String>>(mut self, message: S) -> Self {
1387 let message = message.into();
1388 self.exp_error = Some(message);
1389 self
1390 }
1391
1392 pub(crate) fn check(self) {
1394 if let Some(message) = self.exp_error {
1395 match self.result {
1396 Ok(_) => panic!("Compilation succeeded but expected error: {}", message),
1397 Err(e) => assert_eq!(message, e.to_string()),
1398 }
1399 return;
1400 }
1401 let (image, symtable) = self.result.unwrap();
1402
1403 assert_eq!(self.exp_upcalls, symtable.upcalls());
1404
1405 if self.ignore_instrs {
1406 assert!(
1407 self.exp_instrs.is_empty(),
1408 "Cannot ignore instructions if some are expected"
1409 );
1410 } else {
1411 assert_eq!(self.exp_instrs, image.instrs);
1412 }
1413
1414 assert_eq!(self.exp_data, image.data);
1415
1416 for (key, exp_proto) in self.exp_symtable {
1420 match symtable.get(&key) {
1421 Some(proto) => assert_eq!(
1422 std::mem::discriminant(&exp_proto),
1423 std::mem::discriminant(proto)
1424 ),
1425 None => panic!("Expected symbol {:?} not defined", key),
1426 }
1427 }
1428 }
1429 }
1430
1431 pub(crate) fn lc(line: usize, col: usize) -> LineCol {
1433 LineCol { line, col }
1434 }
1435}
1436
1437#[cfg(test)]
1438mod tests {
1439 use super::testutils::*;
1440 use super::*;
1441 use crate::bytecode::{BuiltinCallISpan, DimArrayISpan};
1442 use crate::syms::CallableMetadataBuilder;
1443 use std::borrow::Cow;
1444
1445 #[test]
1446 fn test_compile_nothing() {
1447 Tester::default().compile().check();
1448 }
1449
1450 #[test]
1451 fn test_compile_array_assignment_exprs() {
1452 Tester::default()
1453 .define("foo", SymbolPrototype::Array(ExprType::Integer, 3))
1454 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1455 .parse("foo(3, 4 + i, i) = 5")
1456 .compile()
1457 .expect_instr(0, Instruction::PushInteger(5, lc(1, 20)))
1458 .expect_instr(1, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 15)))
1459 .expect_instr(2, Instruction::PushInteger(4, lc(1, 8)))
1460 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 12)))
1461 .expect_instr(4, Instruction::AddIntegers(lc(1, 10)))
1462 .expect_instr(5, Instruction::PushInteger(3, lc(1, 5)))
1463 .expect_instr(6, Instruction::ArrayAssignment(SymbolKey::from("foo"), lc(1, 1), 3))
1464 .check();
1465 }
1466
1467 #[test]
1468 fn test_compile_array_assignment_ok_annotation() {
1469 Tester::default()
1470 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1471 .parse("a%(0) = 1")
1472 .compile()
1473 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
1474 .expect_instr(1, Instruction::PushInteger(0, lc(1, 4)))
1475 .expect_instr(2, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1476 .check();
1477 }
1478
1479 #[test]
1480 fn test_compile_array_assignment_index_double() {
1481 Tester::default()
1482 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1483 .parse("a(1.2) = 1")
1484 .compile()
1485 .expect_instr(0, Instruction::PushInteger(1, lc(1, 10)))
1486 .expect_instr(1, Instruction::PushDouble(1.2, lc(1, 3)))
1487 .expect_instr(2, Instruction::DoubleToInteger)
1488 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1489 .check();
1490 }
1491
1492 #[test]
1493 fn test_compile_array_assignment_index_bad_type() {
1494 Tester::default()
1495 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1496 .parse("a(TRUE) = 1")
1497 .compile()
1498 .expect_err("1:3: BOOLEAN is not a number")
1499 .check();
1500 }
1501
1502 #[test]
1503 fn test_compile_array_assignment_bad_annotation() {
1504 Tester::default()
1505 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1506 .parse("a#(0) = 1")
1507 .compile()
1508 .expect_err("1:1: Incompatible type annotation in a# reference")
1509 .check();
1510 }
1511
1512 #[test]
1513 fn test_compile_array_assignment_double_to_integer_promotion() {
1514 Tester::default()
1515 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1516 .define("d", SymbolPrototype::Variable(ExprType::Double))
1517 .parse("a(3) = d")
1518 .compile()
1519 .expect_instr(0, Instruction::LoadDouble(SymbolKey::from("d"), lc(1, 8)))
1520 .expect_instr(1, Instruction::DoubleToInteger)
1521 .expect_instr(2, Instruction::PushInteger(3, lc(1, 3)))
1522 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1523 .check();
1524 }
1525
1526 #[test]
1527 fn test_compile_array_assignment_integer_to_double_promotion() {
1528 Tester::default()
1529 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1530 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1531 .parse("a(3) = i")
1532 .compile()
1533 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 8)))
1534 .expect_instr(1, Instruction::IntegerToDouble)
1535 .expect_instr(2, Instruction::PushInteger(3, lc(1, 3)))
1536 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1537 .check();
1538 }
1539
1540 #[test]
1541 fn test_compile_array_assignment_bad_types() {
1542 Tester::default()
1543 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1544 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1545 .parse("a(3) = FALSE")
1546 .compile()
1547 .expect_err("1:1: Cannot assign value of type BOOLEAN to variable of type DOUBLE")
1548 .check();
1549 }
1550
1551 #[test]
1552 fn test_compile_array_assignment_bad_dimensions() {
1553 Tester::default()
1554 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1555 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1556 .parse("a(3, 5) = 7")
1557 .compile()
1558 .expect_err("1:1: Cannot index array with 2 subscripts; need 1")
1559 .check();
1560 }
1561
1562 #[test]
1563 fn test_compile_array_assignment_not_defined() {
1564 Tester::default()
1565 .parse("a(3) = FALSE")
1566 .compile()
1567 .expect_err("1:1: Undefined symbol a")
1568 .check();
1569 }
1570
1571 #[test]
1572 fn test_compile_array_assignment_not_an_array() {
1573 Tester::default()
1574 .define("a", SymbolPrototype::Variable(ExprType::Integer))
1575 .parse("a(3) = FALSE")
1576 .compile()
1577 .expect_err("1:1: Cannot index non-array a")
1578 .check();
1579 }
1580
1581 #[test]
1582 fn test_compile_assignment_literal() {
1583 Tester::default()
1584 .parse("foo = 5")
1585 .compile()
1586 .expect_instr(0, Instruction::PushInteger(5, lc(1, 7)))
1587 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1588 .check();
1589 }
1590
1591 #[test]
1592 fn test_compile_assignment_varref_is_evaluated() {
1593 Tester::default()
1594 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1595 .parse("foo = i")
1596 .compile()
1597 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 7)))
1598 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1599 .check();
1600 }
1601
1602 #[test]
1603 fn test_compile_assignment_new_var_auto_ref_expr_determines_type() {
1604 Tester::default()
1605 .parse("foo = 2.3")
1606 .compile()
1607 .expect_instr(0, Instruction::PushDouble(2.3, lc(1, 7)))
1608 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1609 .expect_symtable(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Double))
1610 .check();
1611 }
1612
1613 #[test]
1614 fn test_compile_assignment_new_var_explicit_ref_determines_type() {
1615 Tester::default()
1616 .parse("foo# = 2")
1617 .compile()
1618 .expect_instr(0, Instruction::PushInteger(2, lc(1, 8)))
1619 .expect_instr(1, Instruction::IntegerToDouble)
1620 .expect_instr(2, Instruction::Assign(SymbolKey::from("foo")))
1621 .expect_symtable(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Double))
1622 .check();
1623 }
1624
1625 #[test]
1626 fn test_compile_assignment_bad_types_existing_var() {
1627 Tester::default()
1628 .parse("foo# = TRUE")
1629 .compile()
1630 .expect_err("1:1: Cannot assign value of type BOOLEAN to variable of type DOUBLE")
1631 .check();
1632 }
1633
1634 #[test]
1635 fn test_compile_assignment_bad_annotation_existing_var() {
1636 Tester::default()
1637 .define(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Text))
1638 .parse("foo# = \"hello\"")
1639 .compile()
1640 .expect_err("1:1: Incompatible type annotation in foo# reference")
1641 .check();
1642 }
1643
1644 #[test]
1645 fn test_compile_builtin_call_no_args() {
1646 Tester::default()
1647 .define_callable(CallableMetadataBuilder::new("CMD"))
1648 .parse("CMD")
1649 .compile()
1650 .expect_instr(
1651 0,
1652 Instruction::BuiltinCall(BuiltinCallISpan {
1653 name: SymbolKey::from("CMD"),
1654 name_pos: lc(1, 1),
1655 upcall_index: 0,
1656 nargs: 0,
1657 }),
1658 )
1659 .check();
1660 }
1661
1662 #[test]
1663 fn test_compile_builtin_call_increments_next_pc() {
1664 Tester::default()
1665 .define_callable(CallableMetadataBuilder::new("CMD").with_syntax(&[(
1666 &[],
1667 Some(&RepeatedSyntax {
1668 name: Cow::Borrowed("expr"),
1669 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1670 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1671 require_one: false,
1672 allow_missing: false,
1673 }),
1674 )]))
1675 .parse("IF TRUE THEN: CMD 1, 2: END IF")
1676 .compile()
1677 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 4)))
1678 .expect_instr(1, Instruction::JumpIfNotTrue(5))
1679 .expect_instr(2, Instruction::PushInteger(2, lc(1, 22)))
1680 .expect_instr(3, Instruction::PushInteger(1, lc(1, 19)))
1681 .expect_instr(
1682 4,
1683 Instruction::BuiltinCall(BuiltinCallISpan {
1684 name: SymbolKey::from("CMD"),
1685 name_pos: lc(1, 15),
1686 upcall_index: 0,
1687 nargs: 2,
1688 }),
1689 )
1690 .check();
1691 }
1692
1693 #[test]
1694 fn test_compile_data_top_level() {
1695 Tester::default()
1696 .parse("DATA TRUE, 3\nDATA , 1")
1697 .compile()
1698 .expect_datum(Some(Value::Boolean(true)))
1699 .expect_datum(Some(Value::Integer(3)))
1700 .expect_datum(None)
1701 .expect_datum(Some(Value::Integer(1)))
1702 .check();
1703 }
1704
1705 #[test]
1706 fn test_compile_data_interspersed() {
1707 Tester::default()
1708 .parse("IF FALSE THEN: DATA TRUE: END IF")
1709 .parse("FOR i = 1 TO 10: DATA , 5: NEXT")
1710 .parse("WHILE FALSE: DATA 2.3: WEND")
1711 .compile()
1712 .expect_datum(Some(Value::Boolean(true)))
1713 .expect_datum(None)
1714 .expect_datum(Some(Value::Integer(5)))
1715 .expect_datum(Some(Value::Double(2.3)))
1716 .ignore_instrs()
1717 .check();
1718 }
1719
1720 #[test]
1721 fn test_compile_dim_ok() {
1722 Tester::default()
1723 .parse("DIM var AS BOOLEAN")
1724 .compile()
1725 .expect_instr(
1726 0,
1727 Instruction::Dim(DimISpan {
1728 name: SymbolKey::from("var"),
1729 shared: false,
1730 vtype: ExprType::Boolean,
1731 }),
1732 )
1733 .check();
1734 Tester::default()
1735 .parse("DIM var AS DOUBLE")
1736 .compile()
1737 .expect_instr(
1738 0,
1739 Instruction::Dim(DimISpan {
1740 name: SymbolKey::from("var"),
1741 shared: false,
1742 vtype: ExprType::Double,
1743 }),
1744 )
1745 .check();
1746 Tester::default()
1747 .parse("DIM var AS INTEGER")
1748 .compile()
1749 .expect_instr(
1750 0,
1751 Instruction::Dim(DimISpan {
1752 name: SymbolKey::from("var"),
1753 shared: false,
1754 vtype: ExprType::Integer,
1755 }),
1756 )
1757 .check();
1758 Tester::default()
1759 .parse("DIM var AS STRING")
1760 .compile()
1761 .expect_instr(
1762 0,
1763 Instruction::Dim(DimISpan {
1764 name: SymbolKey::from("var"),
1765 shared: false,
1766 vtype: ExprType::Text,
1767 }),
1768 )
1769 .check();
1770 }
1771
1772 #[test]
1773 fn test_compile_dim_case_insensitivity() {
1774 Tester::default()
1775 .parse("DIM foo: DIM Foo")
1776 .compile()
1777 .expect_err("1:14: Cannot define already-defined symbol FOO")
1778 .check();
1779 }
1780
1781 #[test]
1782 fn test_compile_dim_name_overlap() {
1783 Tester::default()
1784 .define("SomeArray", SymbolPrototype::Array(ExprType::Integer, 3))
1785 .parse("DIM somearray")
1786 .compile()
1787 .expect_err("1:5: Cannot define already-defined symbol SOMEARRAY")
1788 .check();
1789
1790 Tester::default()
1791 .define_callable(
1792 CallableMetadataBuilder::new("OUT").with_return_type(ExprType::Integer),
1793 )
1794 .parse("DIM OuT")
1795 .compile()
1796 .expect_err("1:5: Cannot define already-defined symbol OUT")
1797 .check();
1798
1799 Tester::default()
1800 .define("SomeVar", SymbolPrototype::Variable(ExprType::Integer))
1801 .parse("DIM SOMEVAR")
1802 .compile()
1803 .expect_err("1:5: Cannot define already-defined symbol SOMEVAR")
1804 .check();
1805 }
1806
1807 #[test]
1808 fn test_compile_dim_shared_ok() {
1809 Tester::default()
1810 .parse("DIM SHARED var AS BOOLEAN")
1811 .compile()
1812 .expect_instr(
1813 0,
1814 Instruction::Dim(DimISpan {
1815 name: SymbolKey::from("var"),
1816 shared: true,
1817 vtype: ExprType::Boolean,
1818 }),
1819 )
1820 .check();
1821 }
1822
1823 #[test]
1824 fn test_compile_dim_shared_name_overlap_with_local() {
1825 Tester::default()
1826 .parse("DIM var AS BOOLEAN: DIM SHARED Var AS BOOLEAN")
1827 .compile()
1828 .expect_err("1:32: Cannot define already-defined symbol VAR")
1829 .check();
1830
1831 Tester::default()
1832 .parse("DIM SHARED var AS BOOLEAN: DIM Var AS BOOLEAN")
1833 .compile()
1834 .expect_err("1:32: Cannot define already-defined symbol VAR")
1835 .check();
1836 }
1837
1838 #[test]
1839 fn test_compile_dim_array_immediate() {
1840 Tester::default()
1841 .parse("DIM var(1) AS INTEGER")
1842 .compile()
1843 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
1844 .expect_instr(
1845 1,
1846 Instruction::DimArray(DimArrayISpan {
1847 name: SymbolKey::from("var"),
1848 name_pos: lc(1, 5),
1849 shared: false,
1850 dimensions: 1,
1851 subtype: ExprType::Integer,
1852 subtype_pos: lc(1, 15),
1853 }),
1854 )
1855 .check();
1856 }
1857
1858 #[test]
1859 fn test_compile_dim_array_exprs() {
1860 Tester::default()
1861 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1862 .parse("DIM var(i, 3 + 4) AS INTEGER")
1863 .compile()
1864 .expect_instr(0, Instruction::PushInteger(3, lc(1, 12)))
1865 .expect_instr(1, Instruction::PushInteger(4, lc(1, 16)))
1866 .expect_instr(2, Instruction::AddIntegers(lc(1, 14)))
1867 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 9)))
1868 .expect_instr(
1869 4,
1870 Instruction::DimArray(DimArrayISpan {
1871 name: SymbolKey::from("var"),
1872 name_pos: lc(1, 5),
1873 shared: false,
1874 dimensions: 2,
1875 subtype: ExprType::Integer,
1876 subtype_pos: lc(1, 22),
1877 }),
1878 )
1879 .check();
1880 }
1881
1882 #[test]
1883 fn test_compile_dim_array_double_to_integer() {
1884 Tester::default()
1885 .parse("DIM var(3.7) AS INTEGER")
1886 .compile()
1887 .expect_instr(0, Instruction::PushDouble(3.7, lc(1, 9)))
1888 .expect_instr(1, Instruction::DoubleToInteger)
1889 .expect_instr(
1890 2,
1891 Instruction::DimArray(DimArrayISpan {
1892 name: SymbolKey::from("var"),
1893 name_pos: lc(1, 5),
1894 shared: false,
1895 dimensions: 1,
1896 subtype: ExprType::Integer,
1897 subtype_pos: lc(1, 17),
1898 }),
1899 )
1900 .check();
1901 }
1902
1903 #[test]
1904 fn test_compile_dim_array_dimension_type_error() {
1905 Tester::default()
1906 .parse("DIM var(TRUE) AS INTEGER")
1907 .compile()
1908 .expect_err("1:9: BOOLEAN is not a number")
1909 .check();
1910 }
1911
1912 #[test]
1913 fn test_compile_dim_array_shared_ok() {
1914 Tester::default()
1915 .parse("DIM SHARED var(1) AS INTEGER")
1916 .compile()
1917 .expect_instr(0, Instruction::PushInteger(1, lc(1, 16)))
1918 .expect_instr(
1919 1,
1920 Instruction::DimArray(DimArrayISpan {
1921 name: SymbolKey::from("var"),
1922 name_pos: lc(1, 12),
1923 shared: true,
1924 dimensions: 1,
1925 subtype: ExprType::Integer,
1926 subtype_pos: lc(1, 22),
1927 }),
1928 )
1929 .check();
1930 }
1931
1932 #[test]
1933 fn test_compile_dim_array_shared_name_overlap_with_local() {
1934 Tester::default()
1935 .parse("DIM var(1) AS BOOLEAN: DIM SHARED Var(1) AS BOOLEAN")
1936 .compile()
1937 .expect_err("1:35: Cannot define already-defined symbol VAR")
1938 .check();
1939
1940 Tester::default()
1941 .parse("DIM SHARED var(1) AS BOOLEAN: DIM Var(1) AS BOOLEAN")
1942 .compile()
1943 .expect_err("1:35: Cannot define already-defined symbol VAR")
1944 .check();
1945 }
1946
1947 #[test]
1948 fn test_compile_do_infinite() {
1949 Tester::default()
1950 .define_callable(CallableMetadataBuilder::new("FOO"))
1951 .parse("DO\nFOO\nLOOP")
1952 .compile()
1953 .expect_instr(
1954 0,
1955 Instruction::BuiltinCall(BuiltinCallISpan {
1956 name: SymbolKey::from("FOO"),
1957 name_pos: lc(2, 1),
1958 upcall_index: 0,
1959 nargs: 0,
1960 }),
1961 )
1962 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
1963 .check();
1964 }
1965
1966 #[test]
1967 fn test_compile_do_pre_guard() {
1968 Tester::default()
1969 .define_callable(CallableMetadataBuilder::new("FOO"))
1970 .parse("DO WHILE TRUE\nFOO\nLOOP")
1971 .compile()
1972 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1973 .expect_instr(1, Instruction::JumpIfNotTrue(4))
1974 .expect_instr(
1975 2,
1976 Instruction::BuiltinCall(BuiltinCallISpan {
1977 name: SymbolKey::from("FOO"),
1978 name_pos: lc(2, 1),
1979 upcall_index: 0,
1980 nargs: 0,
1981 }),
1982 )
1983 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
1984 .check();
1985 }
1986
1987 #[test]
1988 fn test_compile_do_post_guard() {
1989 Tester::default()
1990 .define_callable(CallableMetadataBuilder::new("FOO"))
1991 .parse("DO\nFOO\nLOOP WHILE TRUE")
1992 .compile()
1993 .expect_instr(
1994 0,
1995 Instruction::BuiltinCall(BuiltinCallISpan {
1996 name: SymbolKey::from("FOO"),
1997 name_pos: lc(2, 1),
1998 upcall_index: 0,
1999 nargs: 0,
2000 }),
2001 )
2002 .expect_instr(1, Instruction::PushBoolean(true, lc(3, 12)))
2003 .expect_instr(2, Instruction::JumpIfTrue(0))
2004 .check();
2005 }
2006
2007 #[test]
2008 fn test_compile_end_without_exit_code() {
2009 Tester::default().parse("END").compile().expect_instr(0, Instruction::End(false)).check();
2010 }
2011
2012 #[test]
2013 fn test_compile_end_with_exit_code_expr() {
2014 Tester::default()
2015 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2016 .parse("END 2 + i")
2017 .compile()
2018 .expect_instr(0, Instruction::PushInteger(2, lc(1, 5)))
2019 .expect_instr(1, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 9)))
2020 .expect_instr(2, Instruction::AddIntegers(lc(1, 7)))
2021 .expect_instr(3, Instruction::End(true))
2022 .check();
2023 }
2024
2025 #[test]
2026 fn test_compile_end_with_exit_code_varref() {
2027 Tester::default()
2028 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2029 .parse("END i")
2030 .compile()
2031 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
2032 .expect_instr(1, Instruction::End(true))
2033 .check();
2034 }
2035
2036 #[test]
2037 fn test_compile_end_with_exit_code_type_error() {
2038 Tester::default()
2039 .parse("END TRUE")
2040 .compile()
2041 .expect_err("1:5: BOOLEAN is not a number")
2042 .check();
2043 }
2044
2045 #[test]
2046 fn test_compile_end_with_exit_code_double_to_integer() {
2047 Tester::default()
2048 .parse("END 2.3")
2049 .compile()
2050 .expect_instr(0, Instruction::PushDouble(2.3, lc(1, 5)))
2051 .expect_instr(1, Instruction::DoubleToInteger)
2052 .expect_instr(2, Instruction::End(true))
2053 .check();
2054 }
2055
2056 #[test]
2057 fn test_compile_exit_do_infinite_simple() {
2058 Tester::default()
2059 .parse("DO\nEXIT DO\nLOOP")
2060 .compile()
2061 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 2 }))
2062 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
2063 .check();
2064 }
2065
2066 #[test]
2067 fn test_compile_exit_do_pre_simple() {
2068 Tester::default()
2069 .parse("DO WHILE TRUE\nEXIT DO\nLOOP")
2070 .compile()
2071 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
2072 .expect_instr(1, Instruction::JumpIfNotTrue(4))
2073 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 4 }))
2074 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
2075 .check();
2076 }
2077
2078 #[test]
2079 fn test_compile_exit_do_nested() {
2080 Tester::default()
2081 .parse("DO WHILE TRUE\nEXIT DO\nDO UNTIL FALSE\nEXIT DO\nLOOP\nLOOP")
2082 .compile()
2083 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
2084 .expect_instr(1, Instruction::JumpIfNotTrue(8))
2085 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 8 }))
2086 .expect_instr(3, Instruction::PushBoolean(false, lc(3, 10)))
2087 .expect_instr(4, Instruction::JumpIfTrue(7))
2088 .expect_instr(5, Instruction::Jump(JumpISpan { addr: 7 }))
2089 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 3 }))
2090 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 0 }))
2091 .check();
2092 }
2093
2094 #[test]
2095 fn test_compile_exit_do_sequential() {
2096 Tester::default()
2097 .parse("DO WHILE TRUE\nEXIT DO\nLOOP\nDO WHILE TRUE\nEXIT DO\nLOOP")
2098 .compile()
2099 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
2100 .expect_instr(1, Instruction::JumpIfNotTrue(4))
2101 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 4 }))
2102 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
2103 .expect_instr(4, Instruction::PushBoolean(true, lc(4, 10)))
2104 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2105 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 8 }))
2106 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 4 }))
2107 .check();
2108 }
2109
2110 #[test]
2111 fn test_compile_exit_do_from_nested_while() {
2112 Tester::default()
2113 .parse("DO WHILE TRUE\nEXIT DO\nWHILE FALSE\nEXIT DO\nWEND\nLOOP")
2114 .compile()
2115 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
2116 .expect_instr(1, Instruction::JumpIfNotTrue(8))
2117 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 8 }))
2118 .expect_instr(3, Instruction::PushBoolean(false, lc(3, 7)))
2119 .expect_instr(4, Instruction::JumpIfNotTrue(7))
2120 .expect_instr(5, Instruction::Jump(JumpISpan { addr: 8 }))
2121 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 3 }))
2122 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 0 }))
2123 .check();
2124 }
2125
2126 #[test]
2127 fn test_compile_exit_do_outside_of_loop() {
2128 Tester::default()
2129 .parse("EXIT DO")
2130 .compile()
2131 .expect_err("1:1: EXIT DO outside of DO")
2132 .check();
2133
2134 Tester::default()
2135 .parse("WHILE TRUE: EXIT DO: WEND")
2136 .compile()
2137 .expect_err("1:13: EXIT DO outside of DO")
2138 .check();
2139 }
2140
2141 #[test]
2142 fn test_compile_exit_for_infinite_simple() {
2143 Tester::default()
2144 .parse("FOR i = 1 to 10\nEXIT FOR\nNEXT")
2145 .compile()
2146 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
2147 .expect_instr(1, Instruction::Assign(SymbolKey::from("i")))
2148 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
2149 .expect_instr(3, Instruction::PushInteger(10, lc(1, 14)))
2150 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 11)))
2151 .expect_instr(5, Instruction::JumpIfNotTrue(12))
2152 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 12 }))
2153 .expect_instr(7, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
2154 .expect_instr(8, Instruction::PushInteger(1, lc(1, 16)))
2155 .expect_instr(9, Instruction::AddIntegers(lc(1, 11)))
2156 .expect_instr(10, Instruction::Assign(SymbolKey::from("i")))
2157 .expect_instr(11, Instruction::Jump(JumpISpan { addr: 2 }))
2158 .check();
2159 }
2160
2161 #[test]
2162 fn test_compile_exit_for_nested() {
2163 Tester::default()
2164 .parse("FOR i = 1 to 10\nFOR j = 2 to 20\nEXIT FOR\nNEXT\nEXIT FOR\nNEXT")
2165 .compile()
2166 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
2167 .expect_instr(1, Instruction::Assign(SymbolKey::from("i")))
2168 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
2169 .expect_instr(3, Instruction::PushInteger(10, lc(1, 14)))
2170 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 11)))
2171 .expect_instr(5, Instruction::JumpIfNotTrue(24))
2172 .expect_instr(6, Instruction::PushInteger(2, lc(2, 9)))
2174 .expect_instr(7, Instruction::Assign(SymbolKey::from("j")))
2175 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("j"), lc(2, 5)))
2176 .expect_instr(9, Instruction::PushInteger(20, lc(2, 14)))
2177 .expect_instr(10, Instruction::LessEqualIntegers(lc(2, 11)))
2178 .expect_instr(11, Instruction::JumpIfNotTrue(18))
2179 .expect_instr(12, Instruction::Jump(JumpISpan { addr: 18 })) .expect_instr(13, Instruction::LoadInteger(SymbolKey::from("j"), lc(2, 5)))
2181 .expect_instr(14, Instruction::PushInteger(1, lc(2, 16)))
2182 .expect_instr(15, Instruction::AddIntegers(lc(2, 11)))
2183 .expect_instr(16, Instruction::Assign(SymbolKey::from("j")))
2184 .expect_instr(17, Instruction::Jump(JumpISpan { addr: 8 }))
2185 .expect_instr(18, Instruction::Jump(JumpISpan { addr: 24 })) .expect_instr(19, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
2188 .expect_instr(20, Instruction::PushInteger(1, lc(1, 16)))
2189 .expect_instr(21, Instruction::AddIntegers(lc(1, 11)))
2190 .expect_instr(22, Instruction::Assign(SymbolKey::from("i")))
2191 .expect_instr(23, Instruction::Jump(JumpISpan { addr: 2 }))
2192 .check();
2193 }
2194
2195 #[test]
2196 fn test_compile_exit_for_outside_of_loop() {
2197 Tester::default()
2198 .parse("EXIT FOR")
2199 .compile()
2200 .expect_err("1:1: EXIT FOR outside of FOR")
2201 .check();
2202
2203 Tester::default()
2204 .parse("WHILE TRUE: EXIT FOR: WEND")
2205 .compile()
2206 .expect_err("1:13: EXIT FOR outside of FOR")
2207 .check();
2208 }
2209
2210 #[test]
2211 fn test_compile_exit_do_and_exit_for() {
2212 Tester::default()
2213 .parse("DO\nFOR i = 1 to 10\nDO\nEXIT DO\nLOOP\nEXIT FOR\nNEXT\nLOOP")
2214 .compile()
2215 .expect_instr(0, Instruction::PushInteger(1, lc(2, 9)))
2217 .expect_instr(1, Instruction::Assign(SymbolKey::from("i")))
2218 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("i"), lc(2, 5)))
2219 .expect_instr(3, Instruction::PushInteger(10, lc(2, 14)))
2220 .expect_instr(4, Instruction::LessEqualIntegers(lc(2, 11)))
2221 .expect_instr(5, Instruction::JumpIfNotTrue(14))
2222 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 8 })) .expect_instr(7, Instruction::Jump(JumpISpan { addr: 6 }))
2225 .expect_instr(8, Instruction::Jump(JumpISpan { addr: 14 })) .expect_instr(9, Instruction::LoadInteger(SymbolKey::from("i"), lc(2, 5)))
2228 .expect_instr(10, Instruction::PushInteger(1, lc(2, 16)))
2229 .expect_instr(11, Instruction::AddIntegers(lc(2, 11)))
2230 .expect_instr(12, Instruction::Assign(SymbolKey::from("i")))
2231 .expect_instr(13, Instruction::Jump(JumpISpan { addr: 2 }))
2232 .expect_instr(14, Instruction::Jump(JumpISpan { addr: 0 }))
2234 .check();
2235 }
2236
2237 #[test]
2238 fn test_compile_for_simple_literals() {
2239 Tester::default()
2240 .parse("FOR iter = 1 TO 5: a = FALSE: NEXT")
2241 .compile()
2242 .expect_instr(0, Instruction::PushInteger(1, lc(1, 12)))
2243 .expect_instr(1, Instruction::Assign(SymbolKey::from("iter")))
2244 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2245 .expect_instr(3, Instruction::PushInteger(5, lc(1, 17)))
2246 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 14)))
2247 .expect_instr(5, Instruction::JumpIfNotTrue(13))
2248 .expect_instr(6, Instruction::PushBoolean(false, lc(1, 24)))
2249 .expect_instr(7, Instruction::Assign(SymbolKey::from("a")))
2250 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2251 .expect_instr(9, Instruction::PushInteger(1, lc(1, 18)))
2252 .expect_instr(10, Instruction::AddIntegers(lc(1, 14)))
2253 .expect_instr(11, Instruction::Assign(SymbolKey::from("iter")))
2254 .expect_instr(12, Instruction::Jump(JumpISpan { addr: 2 }))
2255 .check();
2256 }
2257
2258 #[test]
2259 fn test_compile_for_simple_varrefs_are_evaluated() {
2260 Tester::default()
2261 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2262 .define("j", SymbolPrototype::Variable(ExprType::Integer))
2263 .parse("FOR iter = i TO j: a = FALSE: NEXT")
2264 .compile()
2265 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 12)))
2266 .expect_instr(1, Instruction::Assign(SymbolKey::from("iter")))
2267 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2268 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("j"), lc(1, 17)))
2269 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 14)))
2270 .expect_instr(5, Instruction::JumpIfNotTrue(13))
2271 .expect_instr(6, Instruction::PushBoolean(false, lc(1, 24)))
2272 .expect_instr(7, Instruction::Assign(SymbolKey::from("a")))
2273 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2274 .expect_instr(9, Instruction::PushInteger(1, lc(1, 18)))
2275 .expect_instr(10, Instruction::AddIntegers(lc(1, 14)))
2276 .expect_instr(11, Instruction::Assign(SymbolKey::from("iter")))
2277 .expect_instr(12, Instruction::Jump(JumpISpan { addr: 2 }))
2278 .check();
2279 }
2280
2281 #[test]
2282 fn test_compile_for_expressions() {
2283 Tester::default()
2284 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2285 .define("j", SymbolPrototype::Variable(ExprType::Integer))
2286 .parse("FOR iter = (i + 1) TO (2 + j): a = FALSE: NEXT")
2287 .compile()
2288 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 13)))
2289 .expect_instr(1, Instruction::PushInteger(1, lc(1, 17)))
2290 .expect_instr(2, Instruction::AddIntegers(lc(1, 15)))
2291 .expect_instr(3, Instruction::Assign(SymbolKey::from("iter")))
2292 .expect_instr(4, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2293 .expect_instr(5, Instruction::PushInteger(2, lc(1, 24)))
2294 .expect_instr(6, Instruction::LoadInteger(SymbolKey::from("j"), lc(1, 28)))
2295 .expect_instr(7, Instruction::AddIntegers(lc(1, 26)))
2296 .expect_instr(8, Instruction::LessEqualIntegers(lc(1, 20)))
2297 .expect_instr(9, Instruction::JumpIfNotTrue(17))
2298 .expect_instr(10, Instruction::PushBoolean(false, lc(1, 36)))
2299 .expect_instr(11, Instruction::Assign(SymbolKey::from("a")))
2300 .expect_instr(12, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2301 .expect_instr(13, Instruction::PushInteger(1, lc(1, 30)))
2302 .expect_instr(14, Instruction::AddIntegers(lc(1, 20)))
2303 .expect_instr(15, Instruction::Assign(SymbolKey::from("iter")))
2304 .expect_instr(16, Instruction::Jump(JumpISpan { addr: 4 }))
2305 .check();
2306 }
2307
2308 #[test]
2309 fn test_compile_for_double_auto_iterator() {
2310 Tester::default()
2311 .parse("FOR iter = 0 TO 2 STEP 0.1\nNEXT")
2312 .compile()
2313 .expect_instr(
2314 0,
2315 Instruction::JumpIfDefined(JumpIfDefinedISpan {
2316 var: SymbolKey::from("iter"),
2317 addr: 2,
2318 }),
2319 )
2320 .expect_instr(
2321 1,
2322 Instruction::Dim(DimISpan {
2323 name: SymbolKey::from("iter"),
2324 shared: false,
2325 vtype: ExprType::Double,
2326 }),
2327 )
2328 .expect_instr(2, Instruction::PushInteger(0, lc(1, 12)))
2329 .expect_instr(3, Instruction::IntegerToDouble)
2330 .expect_instr(4, Instruction::Assign(SymbolKey::from("iter")))
2331 .expect_instr(5, Instruction::LoadDouble(SymbolKey::from("iter"), lc(1, 5)))
2332 .expect_instr(6, Instruction::PushInteger(2, lc(1, 17)))
2333 .expect_instr(7, Instruction::IntegerToDouble)
2334 .expect_instr(8, Instruction::LessEqualDoubles(lc(1, 14)))
2335 .expect_instr(9, Instruction::JumpIfNotTrue(15))
2336 .expect_instr(10, Instruction::LoadDouble(SymbolKey::from("iter"), lc(1, 5)))
2337 .expect_instr(11, Instruction::PushDouble(0.1, lc(1, 24)))
2338 .expect_instr(12, Instruction::AddDoubles(lc(1, 14)))
2339 .expect_instr(13, Instruction::Assign(SymbolKey::from("iter")))
2340 .expect_instr(14, Instruction::Jump(JumpISpan { addr: 5 }))
2341 .check();
2342 }
2343
2344 #[test]
2345 fn test_compile_function_and_nothing_else() {
2346 Tester::default()
2347 .parse("FUNCTION foo: a = 3: END FUNCTION")
2348 .compile()
2349 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 8 }))
2350 .expect_instr(1, Instruction::EnterScope)
2351 .expect_instr(
2352 2,
2353 Instruction::Dim(DimISpan {
2354 name: SymbolKey::from("0return_foo"),
2355 shared: false,
2356 vtype: ExprType::Integer,
2357 }),
2358 )
2359 .expect_instr(3, Instruction::PushInteger(3, lc(1, 19)))
2360 .expect_instr(4, Instruction::Assign(SymbolKey::from("a")))
2361 .expect_instr(5, Instruction::LoadInteger(SymbolKey::from("0return_foo"), lc(1, 22)))
2362 .expect_instr(6, Instruction::LeaveScope)
2363 .expect_instr(7, Instruction::Return(lc(1, 22)))
2364 .expect_symtable(
2365 SymbolKey::from("foo"),
2366 SymbolPrototype::Callable(
2367 CallableMetadataBuilder::new_dynamic("USER DEFINED FUNCTION")
2368 .with_syntax(&[(&[], None)])
2369 .build(),
2370 ),
2371 )
2372 .check();
2373 }
2374
2375 #[test]
2376 fn test_compile_function_defined_between_code() {
2377 Tester::default()
2378 .parse("before = 1: FUNCTION foo: END FUNCTION: after = 2")
2379 .compile()
2380 .expect_instr(0, Instruction::PushInteger(1, lc(1, 10)))
2381 .expect_instr(1, Instruction::Assign(SymbolKey::from("before")))
2382 .expect_instr(2, Instruction::PushInteger(2, lc(1, 49)))
2383 .expect_instr(3, Instruction::Assign(SymbolKey::from("after")))
2384 .expect_instr(4, Instruction::Jump(JumpISpan { addr: 10 }))
2385 .expect_instr(5, Instruction::EnterScope)
2386 .expect_instr(
2387 6,
2388 Instruction::Dim(DimISpan {
2389 name: SymbolKey::from("0return_foo"),
2390 shared: false,
2391 vtype: ExprType::Integer,
2392 }),
2393 )
2394 .expect_instr(7, Instruction::LoadInteger(SymbolKey::from("0return_foo"), lc(1, 27)))
2395 .expect_instr(8, Instruction::LeaveScope)
2396 .expect_instr(9, Instruction::Return(lc(1, 27)))
2397 .expect_symtable(
2398 SymbolKey::from("foo"),
2399 SymbolPrototype::Callable(
2400 CallableMetadataBuilder::new_dynamic("USER DEFINED FUNCTION")
2401 .with_syntax(&[(&[], None)])
2402 .build(),
2403 ),
2404 )
2405 .check();
2406 }
2407
2408 #[test]
2409 fn test_compile_function_early_exit() {
2410 Tester::default()
2411 .parse("FUNCTION foo: a = 3: EXIT FUNCTION: a = 4: END FUNCTION")
2412 .compile()
2413 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 11 }))
2414 .expect_instr(1, Instruction::EnterScope)
2415 .expect_instr(
2416 2,
2417 Instruction::Dim(DimISpan {
2418 name: SymbolKey::from("0return_foo"),
2419 shared: false,
2420 vtype: ExprType::Integer,
2421 }),
2422 )
2423 .expect_instr(3, Instruction::PushInteger(3, lc(1, 19)))
2424 .expect_instr(4, Instruction::Assign(SymbolKey::from("a")))
2425 .expect_instr(5, Instruction::Jump(JumpISpan { addr: 8 }))
2426 .expect_instr(6, Instruction::PushInteger(4, lc(1, 41)))
2427 .expect_instr(7, Instruction::Assign(SymbolKey::from("a")))
2428 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("0return_foo"), lc(1, 44)))
2429 .expect_instr(9, Instruction::LeaveScope)
2430 .expect_instr(10, Instruction::Return(lc(1, 44)))
2431 .expect_symtable(
2432 SymbolKey::from("foo"),
2433 SymbolPrototype::Callable(
2434 CallableMetadataBuilder::new_dynamic("USER DEFINED FUNCTION")
2435 .with_syntax(&[(&[], None)])
2436 .build(),
2437 ),
2438 )
2439 .check();
2440 }
2441
2442 #[test]
2443 fn test_compile_function_misplaced_exit() {
2444 Tester::default()
2445 .parse("FUNCTION a: END FUNCTION: EXIT FUNCTION")
2446 .compile()
2447 .expect_err("1:27: EXIT FUNCTION outside of FUNCTION")
2448 .check();
2449 }
2450
2451 #[test]
2452 fn test_compile_function_mismatched_exit() {
2453 Tester::default()
2454 .parse("FUNCTION a: EXIT SUB: END FUNCTION")
2455 .compile()
2456 .expect_err("1:13: EXIT SUB outside of SUB")
2457 .check();
2458 }
2459
2460 #[test]
2461 fn test_compile_function_redefined_was_variable() {
2462 Tester::default()
2463 .parse("a = 1: FUNCTION a: END FUNCTION")
2464 .compile()
2465 .expect_err("1:17: Cannot define already-defined symbol A")
2466 .check();
2467 }
2468
2469 #[test]
2470 fn test_compile_function_redefined_was_function() {
2471 Tester::default()
2472 .parse("FUNCTION a: END FUNCTION: FUNCTION A: END FUNCTION")
2473 .compile()
2474 .expect_err("1:36: Cannot define already-defined symbol A")
2475 .check();
2476 }
2477
2478 #[test]
2479 fn test_compile_function_redefined_was_sub() {
2480 Tester::default()
2481 .parse("SUB a: END SUB: FUNCTION A: END FUNCTION")
2482 .compile()
2483 .expect_err("1:26: Cannot define already-defined symbol A")
2484 .check();
2485 }
2486
2487 #[test]
2488 fn test_compile_sub_early_exit() {
2489 Tester::default()
2490 .parse("SUB foo: a = 3: EXIT SUB: a = 4: END SUB")
2491 .compile()
2492 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 9 }))
2493 .expect_instr(1, Instruction::EnterScope)
2494 .expect_instr(2, Instruction::PushInteger(3, lc(1, 14)))
2495 .expect_instr(3, Instruction::Assign(SymbolKey::from("a")))
2496 .expect_instr(4, Instruction::Jump(JumpISpan { addr: 7 }))
2497 .expect_instr(5, Instruction::PushInteger(4, lc(1, 31)))
2498 .expect_instr(6, Instruction::Assign(SymbolKey::from("a")))
2499 .expect_instr(7, Instruction::LeaveScope)
2500 .expect_instr(8, Instruction::Return(lc(1, 34)))
2501 .expect_symtable(
2502 SymbolKey::from("foo"),
2503 SymbolPrototype::Callable(
2504 CallableMetadataBuilder::new_dynamic("USER DEFINED SUB")
2505 .with_syntax(&[(&[], None)])
2506 .build(),
2507 ),
2508 )
2509 .check();
2510 }
2511
2512 #[test]
2513 fn test_compile_sub_misplaced_exit() {
2514 Tester::default()
2515 .parse("SUB a: END SUB: EXIT SUB")
2516 .compile()
2517 .expect_err("1:17: EXIT SUB outside of SUB")
2518 .check();
2519 }
2520
2521 #[test]
2522 fn test_compile_sub_mismatched_exit() {
2523 Tester::default()
2524 .parse("SUB a: EXIT FUNCTION: END SUB")
2525 .compile()
2526 .expect_err("1:8: EXIT FUNCTION outside of FUNCTION")
2527 .check();
2528 }
2529
2530 #[test]
2531 fn test_compile_sub_redefined_was_variable() {
2532 Tester::default()
2533 .parse("a = 1: SUB a: END SUB")
2534 .compile()
2535 .expect_err("1:12: Cannot define already-defined symbol A")
2536 .check();
2537 }
2538
2539 #[test]
2540 fn test_compile_sub_redefined_was_function() {
2541 Tester::default()
2542 .parse("FUNCTION a: END FUNCTION: SUB A: END SUB")
2543 .compile()
2544 .expect_err("1:31: Cannot define already-defined symbol A")
2545 .check();
2546 }
2547
2548 #[test]
2549 fn test_compile_sub_redefined_was_sub() {
2550 Tester::default()
2551 .parse("SUB a: END SUB: SUB A: END SUB")
2552 .compile()
2553 .expect_err("1:21: Cannot define already-defined symbol A")
2554 .check();
2555 }
2556
2557 #[test]
2558 fn test_compile_goto() {
2559 Tester::default()
2560 .parse("@first GOTO @second")
2561 .parse("@second: GOTO @first")
2562 .compile()
2563 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 1 }))
2564 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
2565 .check();
2566 }
2567
2568 #[test]
2569 fn test_compile_gosub_and_return() {
2570 Tester::default()
2571 .define_callable(CallableMetadataBuilder::new("FOO"))
2572 .parse("@sub\nFOO\nRETURN\nGOSUB @sub")
2573 .compile()
2574 .expect_instr(
2575 0,
2576 Instruction::BuiltinCall(BuiltinCallISpan {
2577 name: SymbolKey::from("FOO"),
2578 name_pos: lc(2, 1),
2579 upcall_index: 0,
2580 nargs: 0,
2581 }),
2582 )
2583 .expect_instr(1, Instruction::Return(lc(3, 1)))
2584 .expect_instr(2, Instruction::Call(JumpISpan { addr: 0 }))
2585 .check();
2586 }
2587
2588 #[test]
2589 fn test_compile_goto_unknown_label() {
2590 Tester::default()
2591 .parse("@fo: GOTO @foo")
2592 .compile()
2593 .expect_err("1:11: Unknown label foo")
2594 .check();
2595 }
2596
2597 #[test]
2598 fn test_compile_if_one_branch() {
2599 Tester::default()
2600 .define_callable(CallableMetadataBuilder::new("FOO"))
2601 .parse("IF FALSE THEN: FOO: END IF")
2602 .compile()
2603 .expect_instr(0, Instruction::PushBoolean(false, lc(1, 4)))
2604 .expect_instr(1, Instruction::JumpIfNotTrue(3))
2605 .expect_instr(
2606 2,
2607 Instruction::BuiltinCall(BuiltinCallISpan {
2608 name: SymbolKey::from("FOO"),
2609 name_pos: lc(1, 16),
2610 upcall_index: 0,
2611 nargs: 0,
2612 }),
2613 )
2614 .check();
2615 }
2616
2617 #[test]
2618 fn test_compile_if_many_branches() {
2619 Tester::default()
2620 .define_callable(CallableMetadataBuilder::new("FOO"))
2621 .define_callable(CallableMetadataBuilder::new("BAR"))
2622 .define_callable(CallableMetadataBuilder::new("BAZ"))
2623 .parse("IF FALSE THEN\nFOO\nELSEIF TRUE THEN\nBAR\nELSE\nBAZ\nEND IF")
2624 .compile()
2625 .expect_instr(0, Instruction::PushBoolean(false, lc(1, 4)))
2626 .expect_instr(1, Instruction::JumpIfNotTrue(4))
2627 .expect_instr(
2628 2,
2629 Instruction::BuiltinCall(BuiltinCallISpan {
2630 name: SymbolKey::from("FOO"),
2631 name_pos: lc(2, 1),
2632 upcall_index: 0,
2633 nargs: 0,
2634 }),
2635 )
2636 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 11 }))
2637 .expect_instr(4, Instruction::PushBoolean(true, lc(3, 8)))
2638 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2639 .expect_instr(
2640 6,
2641 Instruction::BuiltinCall(BuiltinCallISpan {
2642 name: SymbolKey::from("BAR"),
2643 name_pos: lc(4, 1),
2644 upcall_index: 1,
2645 nargs: 0,
2646 }),
2647 )
2648 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 11 }))
2649 .expect_instr(8, Instruction::PushBoolean(true, lc(5, 1)))
2650 .expect_instr(9, Instruction::JumpIfNotTrue(11))
2651 .expect_instr(
2652 10,
2653 Instruction::BuiltinCall(BuiltinCallISpan {
2654 name: SymbolKey::from("BAZ"),
2655 name_pos: lc(6, 1),
2656 upcall_index: 2,
2657 nargs: 0,
2658 }),
2659 )
2660 .check();
2661 }
2662
2663 #[test]
2664 fn test_compile_on_error_reset() {
2665 Tester::default()
2666 .parse("ON ERROR GOTO 0")
2667 .compile()
2668 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::None))
2669 .check();
2670 }
2671
2672 #[test]
2673 fn test_compile_on_error_goto_label() {
2674 Tester::default()
2675 .parse("ON ERROR GOTO @foo\n\n\n@foo")
2676 .compile()
2677 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::Jump(1)))
2678 .check();
2679 }
2680
2681 #[test]
2682 fn test_compile_on_error_goto_unknown_label() {
2683 Tester::default()
2684 .parse("ON ERROR GOTO @foo")
2685 .compile()
2686 .expect_err("1:15: Unknown label foo")
2687 .check();
2688 }
2689
2690 #[test]
2691 fn test_compile_on_error_resume_next() {
2692 Tester::default()
2693 .parse("ON ERROR RESUME NEXT")
2694 .compile()
2695 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::ResumeNext))
2696 .check();
2697 }
2698
2699 fn do_compile_case_guard_test(guards: &str, exp_expr_instrs: Vec<Instruction>) {
2704 let mut t = Tester::default()
2705 .define_callable(CallableMetadataBuilder::new("FOO"))
2706 .parse(&format!("SELECT CASE 5\nCASE {}\nFOO\nEND SELECT", guards))
2707 .compile()
2708 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2709 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")));
2710 let mut n = 2;
2711 for instr in exp_expr_instrs {
2712 t = t.expect_instr(n, instr);
2713 n += 1;
2714 }
2715 t.expect_instr(n, Instruction::JumpIfNotTrue(n + 2))
2716 .expect_instr(
2717 n + 1,
2718 Instruction::BuiltinCall(BuiltinCallISpan {
2719 name: SymbolKey::from("FOO"),
2720 name_pos: lc(3, 1),
2721 upcall_index: 0,
2722 nargs: 0,
2723 }),
2724 )
2725 .expect_instr(
2726 n + 2,
2727 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2728 )
2729 .check();
2730 }
2731
2732 #[test]
2733 fn test_compile_case_guards_equals() {
2734 do_compile_case_guard_test(
2735 "1 + 2",
2736 vec![
2737 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2738 Instruction::PushInteger(1, lc(2, 6)),
2739 Instruction::PushInteger(2, lc(2, 10)),
2740 Instruction::AddIntegers(lc(2, 8)),
2741 Instruction::EqualIntegers(lc(2, 6)),
2742 ],
2743 );
2744 }
2745
2746 #[test]
2747 fn test_compile_case_guards_is() {
2748 do_compile_case_guard_test(
2749 "IS = 9 + 8",
2750 vec![
2751 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2752 Instruction::PushInteger(9, lc(2, 11)),
2753 Instruction::PushInteger(8, lc(2, 15)),
2754 Instruction::AddIntegers(lc(2, 13)),
2755 Instruction::EqualIntegers(lc(2, 11)),
2756 ],
2757 );
2758
2759 do_compile_case_guard_test(
2760 "IS <> 9",
2761 vec![
2762 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2763 Instruction::PushInteger(9, lc(2, 12)),
2764 Instruction::NotEqualIntegers(lc(2, 12)),
2765 ],
2766 );
2767
2768 do_compile_case_guard_test(
2769 "IS < 9",
2770 vec![
2771 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2772 Instruction::PushInteger(9, lc(2, 11)),
2773 Instruction::LessIntegers(lc(2, 11)),
2774 ],
2775 );
2776
2777 do_compile_case_guard_test(
2778 "IS <= 9",
2779 vec![
2780 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2781 Instruction::PushInteger(9, lc(2, 12)),
2782 Instruction::LessEqualIntegers(lc(2, 12)),
2783 ],
2784 );
2785
2786 do_compile_case_guard_test(
2787 "IS > 9",
2788 vec![
2789 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2790 Instruction::PushInteger(9, lc(2, 11)),
2791 Instruction::GreaterIntegers(lc(2, 11)),
2792 ],
2793 );
2794
2795 do_compile_case_guard_test(
2796 "IS >= 9",
2797 vec![
2798 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2799 Instruction::PushInteger(9, lc(2, 12)),
2800 Instruction::GreaterEqualIntegers(lc(2, 12)),
2801 ],
2802 );
2803 }
2804
2805 #[test]
2806 fn test_compile_case_guards_to() {
2807 do_compile_case_guard_test(
2808 "1 TO 2",
2809 vec![
2810 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2811 Instruction::PushInteger(1, lc(2, 6)),
2812 Instruction::GreaterEqualIntegers(lc(2, 6)),
2813 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2814 Instruction::PushInteger(2, lc(2, 11)),
2815 Instruction::LessEqualIntegers(lc(2, 11)),
2816 Instruction::LogicalAnd(lc(2, 6)),
2817 ],
2818 );
2819 }
2820
2821 #[test]
2822 fn test_compile_case_guards_many() {
2823 do_compile_case_guard_test(
2824 "IS <> 9, 8",
2825 vec![
2826 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2827 Instruction::PushInteger(9, lc(2, 12)),
2828 Instruction::NotEqualIntegers(lc(2, 12)),
2829 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 15)),
2830 Instruction::PushInteger(8, lc(2, 15)),
2831 Instruction::EqualIntegers(lc(2, 15)),
2832 Instruction::LogicalOr(lc(2, 12)),
2833 ],
2834 );
2835 }
2836
2837 #[test]
2838 fn test_compile_select_no_cases() {
2839 Tester::default()
2840 .parse("SELECT CASE 5 + 3: END SELECT")
2841 .compile()
2842 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2843 .expect_instr(1, Instruction::PushInteger(3, lc(1, 17)))
2844 .expect_instr(2, Instruction::AddIntegers(lc(1, 15)))
2845 .expect_instr(3, Instruction::Assign(SymbolKey::from("0select1")))
2846 .expect_instr(
2847 4,
2848 Instruction::Unset(UnsetISpan {
2849 name: SymbolKey::from("0select1"),
2850 pos: lc(1, 20),
2851 }),
2852 )
2853 .check();
2854 }
2855
2856 #[test]
2857 fn test_compile_select_one_case() {
2858 Tester::default()
2859 .define_callable(CallableMetadataBuilder::new("FOO"))
2860 .parse("SELECT CASE 5\nCASE 7\nFOO\nEND SELECT")
2861 .compile()
2862 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2863 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2864 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2865 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2866 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2867 .expect_instr(5, Instruction::JumpIfNotTrue(7))
2868 .expect_instr(
2869 6,
2870 Instruction::BuiltinCall(BuiltinCallISpan {
2871 name: SymbolKey::from("FOO"),
2872 name_pos: lc(3, 1),
2873 upcall_index: 0,
2874 nargs: 0,
2875 }),
2876 )
2877 .expect_instr(
2878 7,
2879 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2880 )
2881 .check();
2882 }
2883
2884 #[test]
2885 fn test_compile_select_one_case_varref_is_evaluated() {
2886 Tester::default()
2887 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2888 .parse("SELECT CASE i: END SELECT")
2889 .compile()
2890 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 13)))
2891 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2892 .expect_instr(
2893 2,
2894 Instruction::Unset(UnsetISpan {
2895 name: SymbolKey::from("0select1"),
2896 pos: lc(1, 16),
2897 }),
2898 )
2899 .check();
2900 }
2901
2902 #[test]
2903 fn test_compile_select_only_case_else() {
2904 Tester::default()
2905 .define_callable(CallableMetadataBuilder::new("FOO"))
2906 .parse("SELECT CASE 5\nCASE ELSE\nFOO\nEND SELECT")
2907 .compile()
2908 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2909 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2910 .expect_instr(
2911 2,
2912 Instruction::BuiltinCall(BuiltinCallISpan {
2913 name: SymbolKey::from("FOO"),
2914 name_pos: lc(3, 1),
2915 upcall_index: 0,
2916 nargs: 0,
2917 }),
2918 )
2919 .expect_instr(
2920 3,
2921 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2922 )
2923 .check();
2924 }
2925
2926 #[test]
2927 fn test_compile_select_many_cases_without_else() {
2928 Tester::default()
2929 .define_callable(CallableMetadataBuilder::new("FOO"))
2930 .define_callable(CallableMetadataBuilder::new("BAR"))
2931 .parse("SELECT CASE 5\nCASE 7\nFOO\nCASE IS <> 8\nBAR\nEND SELECT")
2932 .compile()
2933 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2934 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2935 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2936 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2937 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2938 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2939 .expect_instr(
2940 6,
2941 Instruction::BuiltinCall(BuiltinCallISpan {
2942 name: SymbolKey::from("FOO"),
2943 name_pos: lc(3, 1),
2944 upcall_index: 0,
2945 nargs: 0,
2946 }),
2947 )
2948 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 13 }))
2949 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(4, 12)))
2950 .expect_instr(9, Instruction::PushInteger(8, lc(4, 12)))
2951 .expect_instr(10, Instruction::NotEqualIntegers(lc(4, 12)))
2952 .expect_instr(11, Instruction::JumpIfNotTrue(13))
2953 .expect_instr(
2954 12,
2955 Instruction::BuiltinCall(BuiltinCallISpan {
2956 name: SymbolKey::from("BAR"),
2957 name_pos: lc(5, 1),
2958 upcall_index: 1,
2959 nargs: 0,
2960 }),
2961 )
2962 .expect_instr(
2963 13,
2964 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(6, 1) }),
2965 )
2966 .check();
2967 }
2968
2969 #[test]
2970 fn test_compile_select_many_cases_with_else() {
2971 Tester::default()
2972 .define_callable(CallableMetadataBuilder::new("FOO"))
2973 .define_callable(CallableMetadataBuilder::new("BAR"))
2974 .parse("SELECT CASE 5\nCASE 7\nFOO\nCASE ELSE\nBAR\nEND SELECT")
2975 .compile()
2976 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2977 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2978 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2979 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2980 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2981 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2982 .expect_instr(
2983 6,
2984 Instruction::BuiltinCall(BuiltinCallISpan {
2985 name: SymbolKey::from("FOO"),
2986 name_pos: lc(3, 1),
2987 upcall_index: 0,
2988 nargs: 0,
2989 }),
2990 )
2991 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 9 }))
2992 .expect_instr(
2993 8,
2994 Instruction::BuiltinCall(BuiltinCallISpan {
2995 name: SymbolKey::from("BAR"),
2996 name_pos: lc(5, 1),
2997 upcall_index: 1,
2998 nargs: 0,
2999 }),
3000 )
3001 .expect_instr(
3002 9,
3003 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(6, 1) }),
3004 )
3005 .check();
3006 }
3007
3008 #[test]
3009 fn test_compile_select_internal_var_names() {
3010 Tester::default()
3011 .parse("SELECT CASE 0: END SELECT\nSELECT CASE 0: END SELECT")
3012 .compile()
3013 .expect_instr(0, Instruction::PushInteger(0, lc(1, 13)))
3014 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
3015 .expect_instr(
3016 2,
3017 Instruction::Unset(UnsetISpan {
3018 name: SymbolKey::from("0select1"),
3019 pos: lc(1, 16),
3020 }),
3021 )
3022 .expect_instr(3, Instruction::PushInteger(0, lc(2, 13)))
3023 .expect_instr(4, Instruction::Assign(SymbolKey::from("0select2")))
3024 .expect_instr(
3025 5,
3026 Instruction::Unset(UnsetISpan {
3027 name: SymbolKey::from("0select2"),
3028 pos: lc(2, 16),
3029 }),
3030 )
3031 .check();
3032 }
3033
3034 #[test]
3035 fn test_compile_while() {
3036 Tester::default()
3037 .define_callable(CallableMetadataBuilder::new("FOO"))
3038 .parse("WHILE TRUE\nFOO\nWEND")
3039 .compile()
3040 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 7)))
3041 .expect_instr(1, Instruction::JumpIfNotTrue(4))
3042 .expect_instr(
3043 2,
3044 Instruction::BuiltinCall(BuiltinCallISpan {
3045 name: SymbolKey::from("FOO"),
3046 name_pos: lc(2, 1),
3047 upcall_index: 0,
3048 nargs: 0,
3049 }),
3050 )
3051 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
3052 .check();
3053 }
3054}