1use crate::ast::*;
19use crate::bytecode::*;
20use crate::reader::LineCol;
21use crate::syms::{
22 CallError, CallableMetadata, CallableMetadataBuilder, Symbol, SymbolKey, Symbols,
23};
24use std::borrow::Cow;
25use std::collections::HashMap;
26#[cfg(test)]
27use std::collections::HashSet;
28
29mod args;
30pub use args::*;
31mod exprs;
32use exprs::{compile_expr, compile_expr_as_type, compile_expr_in_command};
33
34#[derive(Debug, thiserror::Error)]
36#[error("{}:{}: {}", pos.line, pos.col, message)]
37pub struct Error {
38 pub(crate) pos: LineCol,
39 pub(crate) message: String,
40}
41
42impl Error {
43 pub(crate) fn new<S: Into<String>>(pos: LineCol, message: S) -> Self {
45 let message = message.into();
46 Self { pos, message }
47 }
48
49 fn from_call_error(md: &CallableMetadata, e: CallError, pos: LineCol) -> Self {
54 match e {
55 CallError::ArgumentError(pos2, e) => Self::new(
56 pos,
57 format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
58 ),
59 CallError::EvalError(pos2, e) => {
60 if !md.is_function() {
61 Self::new(pos2, e)
62 } else {
63 Self::new(
64 pos,
65 format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
66 )
67 }
68 }
69 CallError::InternalError(pos2, e) => Self::new(
70 pos,
71 format!("In call to {}: {}:{}: {}", md.name(), pos2.line, pos2.col, e),
72 ),
73 CallError::IoError(_) => unreachable!("I/O errors are not possible during compilation"),
74 CallError::NestedError(_) => unreachable!("Nested are not possible during compilation"),
75 CallError::SyntaxError if md.syntax().is_empty() => {
76 Self::new(pos, format!("In call to {}: expected no arguments", md.name()))
77 }
78 CallError::SyntaxError => {
79 Self::new(pos, format!("In call to {}: expected {}", md.name(), md.syntax()))
80 }
81 }
82 }
83}
84
85pub type Result<T> = std::result::Result<T, Error>;
87
88#[derive(Clone)]
90enum SymbolPrototype {
91 Array(ExprType, usize),
93
94 Callable(CallableMetadata),
96
97 Variable(ExprType),
99}
100
101struct SymbolsTable {
115 globals: HashMap<SymbolKey, SymbolPrototype>,
117
118 scopes: Vec<HashMap<SymbolKey, SymbolPrototype>>,
120}
121
122impl Default for SymbolsTable {
123 fn default() -> Self {
124 Self { globals: HashMap::default(), scopes: vec![HashMap::default()] }
125 }
126}
127
128impl From<HashMap<SymbolKey, SymbolPrototype>> for SymbolsTable {
129 fn from(globals: HashMap<SymbolKey, SymbolPrototype>) -> Self {
130 Self { globals, scopes: vec![HashMap::default()] }
131 }
132}
133
134impl From<&Symbols> for SymbolsTable {
135 fn from(syms: &Symbols) -> Self {
136 let mut globals = HashMap::default();
137 for (name, callable) in syms.callables() {
138 let proto = SymbolPrototype::Callable(callable.metadata().clone());
139 globals.insert(name.clone(), proto);
140 }
141
142 let mut scope = HashMap::default();
143 for (name, symbol) in syms.locals() {
144 let proto = match symbol {
145 Symbol::Array(array) => {
146 SymbolPrototype::Array(array.subtype(), array.dimensions().len())
147 }
148 Symbol::Callable(_) => {
149 unreachable!("Callables must only be global");
150 }
151 Symbol::Variable(var) => SymbolPrototype::Variable(var.as_exprtype()),
152 };
153 scope.insert(name.clone(), proto);
154 }
155
156 Self { globals, scopes: vec![scope] }
157 }
158}
159
160impl SymbolsTable {
161 fn enter_scope(&mut self) {
163 self.scopes.push(HashMap::default());
164 }
165
166 fn leave_scope(&mut self) {
168 let last = self.scopes.pop();
169 assert!(last.is_some(), "Must have at least one scope to pop");
170 assert!(!self.scopes.is_empty(), "Cannot pop the global scope");
171 }
172
173 fn contains_key(&mut self, key: &SymbolKey) -> bool {
175 self.scopes.last().unwrap().contains_key(key) || self.globals.contains_key(key)
176 }
177
178 fn get(&self, key: &SymbolKey) -> Option<&SymbolPrototype> {
180 let proto = self.scopes.last().unwrap().get(key);
181 if proto.is_some() {
182 return proto;
183 }
184
185 self.globals.get(key)
186 }
187
188 fn insert(&mut self, key: SymbolKey, proto: SymbolPrototype) {
191 debug_assert!(!self.globals.contains_key(&key), "Cannot redefine a symbol");
192 let previous = self.scopes.last_mut().unwrap().insert(key, proto);
193 debug_assert!(previous.is_none(), "Cannot redefine a symbol");
194 }
195
196 fn insert_global(&mut self, key: SymbolKey, proto: SymbolPrototype) {
199 let previous = self.globals.insert(key, proto);
200 debug_assert!(previous.is_none(), "Cannot redefine a symbol");
201 }
202
203 fn remove(&mut self, key: SymbolKey) {
205 let previous = self.scopes.last_mut().unwrap().remove(&key);
206 debug_assert!(previous.is_some(), "Cannot unset a non-existing symbol");
207 }
208
209 #[cfg(test)]
211 fn keys(&self) -> HashSet<&SymbolKey> {
212 let mut keys = HashSet::default();
213 keys.extend(self.globals.keys());
214 keys.extend(self.scopes.last().unwrap().keys());
215 keys
216 }
217}
218
219enum FixupType {
221 Gosub,
222 Goto,
223 OnError,
224}
225
226struct Fixup {
228 target: String,
229 target_pos: LineCol,
230 ftype: FixupType,
231}
232
233impl Fixup {
234 fn from_exit_do(target: String, span: ExitDoSpan) -> Self {
236 Self { target, target_pos: span.pos, ftype: FixupType::Goto }
237 }
238
239 fn from_gosub(span: GotoSpan) -> Self {
241 Self { target: span.target, target_pos: span.target_pos, ftype: FixupType::Gosub }
242 }
243
244 fn from_goto(span: GotoSpan) -> Self {
246 Self { target: span.target, target_pos: span.target_pos, ftype: FixupType::Goto }
247 }
248
249 fn from_on_error(span: GotoSpan) -> Self {
251 Self { target: span.target, target_pos: span.target_pos, ftype: FixupType::OnError }
252 }
253}
254
255#[derive(Default)]
257struct Compiler {
258 next_pc: Address,
260
261 exit_do_level: (usize, usize),
267
268 selects: usize,
270
271 labels: HashMap<String, Address>,
273
274 fixups: HashMap<Address, Fixup>,
276
277 instrs: Vec<Instruction>,
279
280 data: Vec<Option<Value>>,
282
283 symtable: SymbolsTable,
285
286 current_function: Option<SymbolKey>,
288
289 callable_spans: Vec<CallableSpan>,
291}
292
293impl Compiler {
294 fn emit(&mut self, instr: Instruction) -> Address {
296 let pc = self.next_pc;
297 self.instrs.push(instr);
298 self.next_pc += 1;
299 pc
300 }
301
302 fn do_label(level: (usize, usize)) -> String {
309 format!("0do{}_{}", level.0, level.1)
310 }
311
312 fn return_key(name: &SymbolKey) -> SymbolKey {
314 SymbolKey::from(format!("0return_{}", name))
315 }
316
317 fn select_test_var_name(selects: usize) -> String {
323 format!("0select{}", selects)
324 }
325
326 fn maybe_cast(&mut self, target: ExprType, from: ExprType) -> ExprType {
331 match (target, from) {
332 (ExprType::Double, ExprType::Integer) => {
333 self.emit(Instruction::IntegerToDouble);
334 target
335 }
336 (ExprType::Integer, ExprType::Double) => {
337 self.emit(Instruction::DoubleToInteger);
338 target
339 }
340 _ => from,
341 }
342 }
343
344 fn compile_array_indices(
346 &mut self,
347 exp_nargs: usize,
348 args: Vec<Expr>,
349 name_pos: LineCol,
350 ) -> Result<()> {
351 let mut instrs = vec![];
352 match exprs::compile_array_indices(&mut instrs, &self.symtable, exp_nargs, args, name_pos) {
353 Ok(result) => {
354 self.next_pc += instrs.len();
355 self.instrs.append(&mut instrs);
356 Ok(result)
357 }
358 Err(e) => Err(e),
359 }
360 }
361
362 fn compile_array_assignment(&mut self, span: ArrayAssignmentSpan) -> Result<()> {
364 let key = SymbolKey::from(span.vref.name());
365 let (atype, dims) = match self.symtable.get(&key) {
366 Some(SymbolPrototype::Array(atype, dims)) => (*atype, *dims),
367 Some(_) => {
368 return Err(Error::new(
369 span.vref_pos,
370 format!("Cannot index non-array {}", span.vref),
371 ));
372 }
373 None => {
374 return Err(Error::new(
375 span.vref_pos,
376 format!("Cannot index undefined array {}", span.vref),
377 ));
378 }
379 };
380
381 if !span.vref.accepts(atype) {
382 return Err(Error::new(
383 span.vref_pos,
384 format!("Incompatible types in {} reference", span.vref),
385 ));
386 }
387
388 let etype = self.compile_expr(span.expr, false)?;
389 let etype = self.maybe_cast(atype, etype);
390 if etype != atype {
391 return Err(Error::new(
392 span.vref_pos,
393 format!("Cannot assign value of type {} to array of type {}", etype, atype),
394 ));
395 }
396
397 let nargs = span.subscripts.len();
398 self.compile_array_indices(dims, span.subscripts, span.vref_pos)?;
399
400 self.emit(Instruction::ArrayAssignment(key, span.vref_pos, nargs));
401
402 Ok(())
403 }
404
405 fn compile_assignment(&mut self, vref: VarRef, vref_pos: LineCol, expr: Expr) -> Result<()> {
410 let mut key = SymbolKey::from(&vref.name());
411 let etype = self.compile_expr(expr, false)?;
412
413 if let Some(current_function) = self.current_function.as_ref() {
414 if &key == current_function {
415 key = Compiler::return_key(current_function);
416 }
417 }
418
419 let vtype = match self.symtable.get(&key) {
420 Some(SymbolPrototype::Variable(vtype)) => *vtype,
421 Some(_) => {
422 return Err(Error::new(vref_pos, format!("Cannot redefine {} as a variable", vref)))
423 }
424 None => {
425 let key = key.clone();
428 let vtype = vref.ref_type().unwrap_or(etype);
429 self.symtable.insert(key, SymbolPrototype::Variable(vtype));
430 vtype
431 }
432 };
433
434 let etype = self.maybe_cast(vtype, etype);
435 if etype != vtype {
436 return Err(Error::new(
437 vref_pos,
438 format!("Cannot assign value of type {} to variable of type {}", etype, vtype,),
439 ));
440 }
441
442 if let Some(ref_type) = vref.ref_type() {
443 if ref_type != vtype {
444 return Err(Error::new(
445 vref_pos,
446 format!("Incompatible types in {} reference", vref),
447 ));
448 }
449 }
450
451 self.emit(Instruction::Assign(key));
452
453 Ok(())
454 }
455
456 fn compile_callable(&mut self, span: CallableSpan) -> Result<()> {
458 let key = SymbolKey::from(span.name.name());
459 if self.symtable.contains_key(&key) {
460 return Err(Error::new(
461 span.name_pos,
462 format!("Cannot define already-defined symbol {}", span.name),
463 ));
464 }
465
466 let mut syntax = vec![];
467 for (i, param) in span.params.iter().enumerate() {
468 let sep = if i == span.params.len() - 1 {
469 ArgSepSyntax::End
470 } else {
471 ArgSepSyntax::Exactly(ArgSep::Long)
472 };
473 syntax.push(SingularArgSyntax::RequiredValue(
474 RequiredValueSyntax {
475 name: Cow::Owned(param.name().to_owned()),
476 vtype: param.ref_type().unwrap_or(ExprType::Integer),
477 },
478 sep,
479 ));
480 }
481
482 let mut builder = CallableMetadataBuilder::new_dynamic(span.name.name().to_owned())
483 .with_dynamic_syntax(vec![(syntax, None)])
484 .with_category("User defined")
485 .with_description("User defined symbol.");
486 if let Some(ctype) = span.name.ref_type() {
487 builder = builder.with_return_type(ctype);
488 }
489 self.symtable.insert_global(key, SymbolPrototype::Callable(builder.build()));
490 self.callable_spans.push(span);
491
492 Ok(())
493 }
494
495 fn compile_dim(&mut self, span: DimSpan) -> Result<()> {
497 let key = SymbolKey::from(&span.name);
498 if self.symtable.contains_key(&key) {
499 return Err(Error::new(
500 span.name_pos,
501 format!("Cannot DIM already-defined symbol {}", span.name),
502 ));
503 }
504
505 self.emit(Instruction::Dim(DimISpan {
506 name: key.clone(),
507 shared: span.shared,
508 vtype: span.vtype,
509 }));
510 if span.shared {
511 self.symtable.insert_global(key, SymbolPrototype::Variable(span.vtype));
512 } else {
513 self.symtable.insert(key, SymbolPrototype::Variable(span.vtype));
514 }
515
516 Ok(())
517 }
518
519 fn compile_do(&mut self, span: DoSpan) -> Result<()> {
521 self.exit_do_level.1 += 1;
522
523 let end_pc;
524 match span.guard {
525 DoGuard::Infinite => {
526 let start_pc = self.next_pc;
527 self.compile_many(span.body)?;
528 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
529 }
530
531 DoGuard::PreUntil(guard) => {
532 let start_pc = self.next_pc;
533 self.compile_expr_guard(guard, "DO requires a boolean condition")?;
534 let jump_pc = self.emit(Instruction::Nop);
535 self.compile_many(span.body)?;
536 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
537 self.instrs[jump_pc] = Instruction::JumpIfTrue(self.next_pc);
538 }
539
540 DoGuard::PreWhile(guard) => {
541 let start_pc = self.next_pc;
542 self.compile_expr_guard(guard, "DO requires a boolean condition")?;
543 let jump_pc = self.emit(Instruction::Nop);
544 self.compile_many(span.body)?;
545 end_pc = self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
546 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.next_pc);
547 }
548
549 DoGuard::PostUntil(guard) => {
550 let start_pc = self.next_pc;
551 self.compile_many(span.body)?;
552 self.compile_expr_guard(guard, "LOOP requires a boolean condition")?;
553 end_pc = self.emit(Instruction::JumpIfNotTrue(start_pc));
554 }
555
556 DoGuard::PostWhile(guard) => {
557 let start_pc = self.next_pc;
558 self.compile_many(span.body)?;
559 self.compile_expr_guard(guard, "LOOP requires a boolean condition")?;
560 end_pc = self.emit(Instruction::JumpIfTrue(start_pc));
561 }
562 }
563
564 let existing = self.labels.insert(Compiler::do_label(self.exit_do_level), end_pc + 1);
565 assert!(existing.is_none(), "Auto-generated label must be unique");
566 self.exit_do_level.1 -= 1;
567 if self.exit_do_level.1 == 0 {
568 self.exit_do_level.0 += 1;
569 }
570
571 Ok(())
572 }
573
574 fn compile_for(&mut self, span: ForSpan) -> Result<()> {
576 debug_assert!(
577 span.iter.ref_type().is_none()
578 || span.iter.ref_type().unwrap() == ExprType::Double
579 || span.iter.ref_type().unwrap() == ExprType::Integer
580 );
581
582 if span.iter_double && span.iter.ref_type().is_none() {
583 let key = SymbolKey::from(span.iter.name());
584 let skip_pc = self.emit(Instruction::Nop);
585
586 let iter_key = SymbolKey::from(span.iter.name());
587 if self.symtable.get(&key).is_none() {
588 self.emit(Instruction::Dim(DimISpan {
589 name: key,
590 shared: false,
591 vtype: ExprType::Double,
592 }));
593 self.symtable.insert(iter_key.clone(), SymbolPrototype::Variable(ExprType::Double));
594 }
595
596 self.instrs[skip_pc] = Instruction::JumpIfDefined(JumpIfDefinedISpan {
597 var: iter_key,
598 addr: self.next_pc,
599 });
600 }
601
602 self.compile_assignment(span.iter.clone(), span.iter_pos, span.start)?;
603
604 let start_pc = self.next_pc;
605 let end_etype = self.compile_expr(span.end, false)?;
606 match end_etype {
607 ExprType::Boolean => (),
608 _ => panic!("Synthesized end condition for FOR must yield a boolean"),
609 };
610 let jump_pc = self.emit(Instruction::Nop);
611
612 self.compile_many(span.body)?;
613
614 self.compile_assignment(span.iter.clone(), span.iter_pos, span.next)?;
615
616 self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
617
618 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.next_pc);
619
620 Ok(())
621 }
622
623 fn compile_if(&mut self, span: IfSpan) -> Result<()> {
625 let mut end_pcs = vec![];
626
627 let mut iter = span.branches.into_iter();
628 let mut next = iter.next();
629 while let Some(branch) = next {
630 let next2 = iter.next();
631
632 self.compile_expr_guard(branch.guard, "IF/ELSEIF require a boolean condition")?;
633 let jump_pc = self.emit(Instruction::Nop);
634 self.compile_many(branch.body)?;
635
636 if next2.is_some() {
637 end_pcs.push(self.next_pc);
638 self.emit(Instruction::Nop);
639 }
640
641 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.next_pc);
642
643 next = next2;
644 }
645
646 for end_pc in end_pcs {
647 self.instrs[end_pc] = Instruction::Jump(JumpISpan { addr: self.next_pc });
648 }
649
650 Ok(())
651 }
652
653 fn compile_on_error(&mut self, span: OnErrorSpan) {
655 match span {
656 OnErrorSpan::Goto(span) => {
657 let goto_pc = self.emit(Instruction::Nop);
658 self.fixups.insert(goto_pc, Fixup::from_on_error(span));
659 }
660 OnErrorSpan::Reset => {
661 self.emit(Instruction::SetErrorHandler(ErrorHandlerISpan::None));
662 }
663 OnErrorSpan::ResumeNext => {
664 self.emit(Instruction::SetErrorHandler(ErrorHandlerISpan::ResumeNext));
665 }
666 }
667 }
668
669 fn compile_case_guards(test_vref: &VarRef, guards: Vec<CaseGuardSpan>) -> Option<Expr> {
672 let mut expr = None;
673 for guard in guards {
674 let one_expr = match guard {
675 CaseGuardSpan::Is(relop, expr) => {
676 let pos = expr.start_pos();
677 let test_expr = Expr::Symbol(SymbolSpan { vref: test_vref.clone(), pos });
678 let binop = Box::from(BinaryOpSpan { lhs: test_expr, rhs: expr, pos });
679 match relop {
680 CaseRelOp::Equal => Expr::Equal(binop),
681 CaseRelOp::NotEqual => Expr::NotEqual(binop),
682 CaseRelOp::Less => Expr::Less(binop),
683 CaseRelOp::LessEqual => Expr::LessEqual(binop),
684 CaseRelOp::Greater => Expr::Greater(binop),
685 CaseRelOp::GreaterEqual => Expr::GreaterEqual(binop),
686 }
687 }
688
689 CaseGuardSpan::To(from_expr, to_expr) => {
690 let from_pos = from_expr.start_pos();
691 let to_pos = to_expr.start_pos();
692 let test_expr =
693 Expr::Symbol(SymbolSpan { vref: test_vref.clone(), pos: from_pos });
694 Expr::And(Box::from(BinaryOpSpan {
695 lhs: Expr::GreaterEqual(Box::from(BinaryOpSpan {
696 lhs: test_expr.clone(),
697 rhs: from_expr,
698 pos: from_pos,
699 })),
700 rhs: Expr::LessEqual(Box::from(BinaryOpSpan {
701 lhs: test_expr,
702 rhs: to_expr,
703 pos: to_pos,
704 })),
705 pos: from_pos,
706 }))
707 }
708 };
709
710 expr = match expr {
711 None => Some(one_expr),
712 Some(expr) => {
713 let pos = expr.start_pos();
714 Some(Expr::Or(Box::from(BinaryOpSpan { lhs: expr, rhs: one_expr, pos })))
715 }
716 };
717 }
718 expr
719 }
720
721 fn compile_select(&mut self, span: SelectSpan) -> Result<()> {
723 let mut end_pcs = vec![];
724
725 self.selects += 1;
726 let test_vref = VarRef::new(Compiler::select_test_var_name(self.selects), None);
727 self.compile_assignment(test_vref.clone(), span.expr.start_pos(), span.expr)?;
728
729 let mut iter = span.cases.into_iter();
730 let mut next = iter.next();
731 while let Some(case) = next {
732 let next2 = iter.next();
733
734 match Compiler::compile_case_guards(&test_vref, case.guards) {
735 None => {
736 self.compile_many(case.body)?;
737 }
738 Some(guard) => {
739 self.compile_expr_guard(guard, "SELECT requires a boolean condition")?;
740 let jump_pc = self.emit(Instruction::Nop);
741 self.compile_many(case.body)?;
742
743 if next2.is_some() {
744 end_pcs.push(self.next_pc);
745 self.emit(Instruction::Nop);
746 }
747
748 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.next_pc);
749 }
750 }
751
752 next = next2;
753 }
754
755 for end_pc in end_pcs {
756 self.instrs[end_pc] = Instruction::Jump(JumpISpan { addr: self.next_pc });
757 }
758
759 let test_key = SymbolKey::from(test_vref.name());
760 self.emit(Instruction::Unset(UnsetISpan { name: test_key.clone(), pos: span.end_pos }));
761 self.symtable.remove(test_key);
762
763 Ok(())
764 }
765
766 fn compile_while(&mut self, span: WhileSpan) -> Result<()> {
768 let start_pc = self.next_pc;
769 self.compile_expr_guard(span.expr, "WHILE requires a boolean condition")?;
770 let jump_pc = self.emit(Instruction::Nop);
771
772 self.compile_many(span.body)?;
773
774 self.emit(Instruction::Jump(JumpISpan { addr: start_pc }));
775
776 self.instrs[jump_pc] = Instruction::JumpIfNotTrue(self.next_pc);
777
778 Ok(())
779 }
780
781 fn compile_expr(&mut self, expr: Expr, allow_varrefs: bool) -> Result<ExprType> {
789 let result = if allow_varrefs {
790 compile_expr_in_command(&mut self.instrs, &mut self.symtable, expr)
791 } else {
792 compile_expr(&mut self.instrs, &self.symtable, expr, false)
793 };
794 match result {
795 Ok(result) => {
796 self.next_pc = self.instrs.len();
797 Ok(result)
798 }
799 Err(e) => Err(e),
800 }
801 }
802
803 fn compile_expr_as_type(&mut self, expr: Expr, target: ExprType) -> Result<()> {
806 compile_expr_as_type(&mut self.instrs, &self.symtable, expr, target)?;
807 self.next_pc = self.instrs.len();
808 Ok(())
809 }
810
811 fn compile_expr_guard<S: Into<String>>(&mut self, guard: Expr, errmsg: S) -> Result<()> {
814 let pos = guard.start_pos();
815 match self.compile_expr(guard, false)? {
816 ExprType::Boolean => Ok(()),
817 _ => Err(Error::new(pos, errmsg.into())),
818 }
819 }
820
821 fn compile_one(&mut self, stmt: Statement) -> Result<()> {
823 match stmt {
824 Statement::ArrayAssignment(span) => {
825 self.compile_array_assignment(span)?;
826 }
827
828 Statement::Assignment(span) => {
829 self.compile_assignment(span.vref, span.vref_pos, span.expr)?;
830 }
831
832 Statement::Call(span) => {
833 let key = SymbolKey::from(&span.vref.name());
834 let md = match self.symtable.get(&key) {
835 Some(SymbolPrototype::Callable(md)) => {
836 if md.is_function() {
837 return Err(Error::new(
838 span.vref_pos,
839 format!("{} is not a command", span.vref.name()),
840 ));
841 }
842 md.clone()
843 }
844
845 Some(_) => {
846 return Err(Error::new(
847 span.vref_pos,
848 format!("{} is not a command", span.vref.name()),
849 ));
850 }
851
852 None => {
853 return Err(Error::new(
854 span.vref_pos,
855 format!("Unknown builtin {}", span.vref.name()),
856 ))
857 }
858 };
859
860 let name_pos = span.vref_pos;
861 let nargs = compile_command_args(
862 md.syntaxes(),
863 &mut self.instrs,
864 &mut self.symtable,
865 name_pos,
866 span.args,
867 )
868 .map_err(|e| Error::from_call_error(&md, e, name_pos))?;
869 self.next_pc = self.instrs.len();
870 self.emit(Instruction::BuiltinCall(key, span.vref_pos, nargs));
871 }
872
873 Statement::Callable(span) => {
874 self.compile_callable(span)?;
875 }
876
877 Statement::Data(mut span) => {
878 self.data.append(&mut span.values);
879 }
880
881 Statement::Dim(span) => {
882 self.compile_dim(span)?;
883 }
884
885 Statement::DimArray(span) => {
886 let key = SymbolKey::from(&span.name);
887 if self.symtable.contains_key(&key) {
888 return Err(Error::new(
889 span.name_pos,
890 format!("Cannot DIM already-defined symbol {}", span.name),
891 ));
892 }
893
894 let nargs = span.dimensions.len();
895 for arg in span.dimensions.into_iter().rev() {
896 self.compile_expr_as_type(arg, ExprType::Integer)?;
897 }
898 self.emit(Instruction::DimArray(DimArrayISpan {
899 name: key.clone(),
900 name_pos: span.name_pos,
901 shared: span.shared,
902 dimensions: nargs,
903 subtype: span.subtype,
904 subtype_pos: span.subtype_pos,
905 }));
906
907 if span.shared {
908 self.symtable.insert_global(key, SymbolPrototype::Array(span.subtype, nargs));
909 } else {
910 self.symtable.insert(key, SymbolPrototype::Array(span.subtype, nargs));
911 }
912 }
913
914 Statement::Do(span) => {
915 self.compile_do(span)?;
916 }
917
918 Statement::End(span) => match span.code {
919 Some(expr) => {
920 self.compile_expr_as_type(expr, ExprType::Integer)?;
921 self.emit(Instruction::End(true));
922 }
923 None => {
924 self.emit(Instruction::End(false));
925 }
926 },
927
928 Statement::ExitDo(span) => {
929 if self.exit_do_level.1 == 0 {
930 return Err(Error::new(span.pos, "EXIT DO outside of DO loop".to_owned()));
931 }
932 let exit_do_pc = self.emit(Instruction::Nop);
933 self.fixups.insert(
934 exit_do_pc,
935 Fixup::from_exit_do(Compiler::do_label(self.exit_do_level), span),
936 );
937 }
938
939 Statement::For(span) => {
940 self.compile_for(span)?;
941 }
942
943 Statement::Gosub(span) => {
944 let gosub_pc = self.emit(Instruction::Nop);
945 self.fixups.insert(gosub_pc, Fixup::from_gosub(span));
946 }
947
948 Statement::Goto(span) => {
949 let goto_pc = self.emit(Instruction::Nop);
950 self.fixups.insert(goto_pc, Fixup::from_goto(span));
951 }
952
953 Statement::If(span) => {
954 self.compile_if(span)?;
955 }
956
957 Statement::Label(span) => {
958 if self.labels.insert(span.name.clone(), self.next_pc).is_some() {
959 return Err(Error::new(
960 span.name_pos,
961 format!("Duplicate label {}", span.name),
962 ));
963 }
964 }
965
966 Statement::OnError(span) => {
967 self.compile_on_error(span);
968 }
969
970 Statement::Return(span) => {
971 self.emit(Instruction::Return(span.pos));
972 }
973
974 Statement::Select(span) => {
975 self.compile_select(span)?;
976 }
977
978 Statement::While(span) => {
979 self.compile_while(span)?;
980 }
981 }
982
983 Ok(())
984 }
985
986 fn compile_many(&mut self, stmts: Vec<Statement>) -> Result<()> {
989 for stmt in stmts {
990 self.compile_one(stmt)?;
991 }
992
993 Ok(())
994 }
995
996 fn compile_callables(&mut self) -> Result<()> {
999 let end = self.emit(Instruction::Nop);
1000
1001 let mut functions = HashMap::with_capacity(self.callable_spans.len());
1002 let mut subs = HashMap::with_capacity(self.callable_spans.len());
1003 let callable_spans = std::mem::take(&mut self.callable_spans);
1004 for span in callable_spans {
1005 let pc = self.next_pc;
1006
1007 let key = SymbolKey::from(span.name.name());
1008 let return_value = Compiler::return_key(&key);
1009 match span.name.ref_type() {
1010 Some(return_type) => {
1011 self.emit(Instruction::EnterScope);
1012 self.symtable.enter_scope();
1013
1014 self.emit(Instruction::Dim(DimISpan {
1015 name: return_value.clone(),
1016 shared: false,
1017 vtype: return_type,
1018 }));
1019 self.symtable
1020 .insert(return_value.clone(), SymbolPrototype::Variable(return_type));
1021
1022 for param in span.params {
1023 let key = SymbolKey::from(param.name());
1024 let ptype = param.ref_type().unwrap_or(ExprType::Integer);
1025 self.emit(Instruction::Assign(key.clone()));
1026 self.symtable.insert(key, SymbolPrototype::Variable(ptype));
1027 }
1028
1029 self.current_function = Some(key.clone());
1030 self.compile_many(span.body)?;
1031 self.current_function = None;
1032
1033 let load_inst = match return_type {
1034 ExprType::Boolean => Instruction::LoadBoolean,
1035 ExprType::Double => Instruction::LoadDouble,
1036 ExprType::Integer => Instruction::LoadInteger,
1037 ExprType::Text => Instruction::LoadString,
1038 };
1039 self.emit(load_inst(return_value.clone(), span.end_pos));
1040
1041 self.emit(Instruction::LeaveScope);
1042 self.symtable.leave_scope();
1043 self.emit(Instruction::Return(span.end_pos));
1044
1045 functions.insert(key, pc);
1046 }
1047 None => {
1048 self.emit(Instruction::EnterScope);
1049 self.symtable.enter_scope();
1050
1051 for param in span.params {
1052 let key = SymbolKey::from(param.name());
1053 let ptype = param.ref_type().unwrap_or(ExprType::Integer);
1054 self.emit(Instruction::Assign(key.clone()));
1055 self.symtable.insert(key, SymbolPrototype::Variable(ptype));
1056 }
1057
1058 self.compile_many(span.body)?;
1059
1060 self.emit(Instruction::LeaveScope);
1061 self.symtable.leave_scope();
1062 self.emit(Instruction::Return(span.end_pos));
1063
1064 subs.insert(key, pc);
1065 }
1066 }
1067 }
1068
1069 for instr in &mut self.instrs {
1070 match instr {
1071 Instruction::BuiltinCall(key, _, _) => {
1072 if let Some(addr) = subs.get(key) {
1073 *instr = Instruction::Call(JumpISpan { addr: *addr });
1074 }
1075 }
1076
1077 Instruction::FunctionCall(key, _, _, _) => {
1078 if let Some(addr) = functions.get(key) {
1079 *instr = Instruction::Call(JumpISpan { addr: *addr });
1080 }
1081 }
1082
1083 _ => (),
1084 }
1085 }
1086
1087 self.instrs[end] = Instruction::Jump(JumpISpan { addr: self.next_pc });
1088
1089 Ok(())
1090 }
1091
1092 #[allow(clippy::wrong_self_convention)]
1094 fn to_image(mut self) -> Result<(Image, SymbolsTable)> {
1095 if !self.callable_spans.is_empty() {
1096 self.compile_callables()?;
1097 }
1098
1099 for (pc, fixup) in self.fixups {
1100 let addr = match self.labels.get(&fixup.target) {
1101 Some(addr) => *addr,
1102 None => {
1103 return Err(Error::new(
1104 fixup.target_pos,
1105 format!("Unknown label {}", fixup.target),
1106 ));
1107 }
1108 };
1109
1110 match fixup.ftype {
1111 FixupType::Gosub => self.instrs[pc] = Instruction::Call(JumpISpan { addr }),
1112 FixupType::Goto => self.instrs[pc] = Instruction::Jump(JumpISpan { addr }),
1113 FixupType::OnError => {
1114 self.instrs[pc] = Instruction::SetErrorHandler(ErrorHandlerISpan::Jump(addr))
1115 }
1116 }
1117 }
1118 let image = Image { instrs: self.instrs, data: self.data };
1119 Ok((image, self.symtable))
1120 }
1121}
1122
1123fn compile_aux(stmts: Vec<Statement>, symtable: SymbolsTable) -> Result<(Image, SymbolsTable)> {
1128 let mut compiler = Compiler { symtable, ..Default::default() };
1129 compiler.compile_many(stmts)?;
1130 compiler.to_image()
1131}
1132
1133pub fn compile(stmts: Vec<Statement>, syms: &Symbols) -> Result<Image> {
1140 compile_aux(stmts, SymbolsTable::from(syms)).map(|(image, _symtable)| image)
1141}
1142
1143#[cfg(test)]
1144mod testutils {
1145 use super::*;
1146 use crate::parser;
1147 use crate::syms::CallableMetadataBuilder;
1148
1149 #[derive(Default)]
1151 #[must_use]
1152 pub(crate) struct Tester {
1153 stmts: Vec<Statement>,
1154 symtable: SymbolsTable,
1155 }
1156
1157 impl Tester {
1158 pub(crate) fn define<K: Into<SymbolKey>>(
1160 mut self,
1161 name: K,
1162 proto: SymbolPrototype,
1163 ) -> Self {
1164 self.symtable.insert(name.into(), proto);
1165 self
1166 }
1167
1168 pub(crate) fn define_callable(mut self, builder: CallableMetadataBuilder) -> Self {
1172 let md = builder.test_build();
1173 let key = SymbolKey::from(md.name());
1174 self.symtable.insert(key, SymbolPrototype::Callable(md));
1175 self
1176 }
1177
1178 pub(crate) fn parse(mut self, input: &str) -> Self {
1180 let mut stmts = parser::parse(&mut input.as_bytes()).unwrap();
1181 self.stmts.append(&mut stmts);
1182 self
1183 }
1184
1185 pub(crate) fn compile(self) -> Checker {
1188 Checker {
1189 result: compile_aux(self.stmts, self.symtable),
1190 exp_error: None,
1191 ignore_instrs: false,
1192 exp_instrs: vec![],
1193 exp_data: vec![],
1194 exp_symtable: HashMap::default(),
1195 }
1196 }
1197 }
1198
1199 #[must_use]
1201 pub(crate) struct Checker {
1202 result: Result<(Image, SymbolsTable)>,
1203 exp_error: Option<String>,
1204 ignore_instrs: bool,
1205 exp_instrs: Vec<Instruction>,
1206 exp_data: Vec<Option<Value>>,
1207 exp_symtable: HashMap<SymbolKey, SymbolPrototype>,
1208 }
1209
1210 impl Checker {
1211 pub(crate) fn expect_instr(mut self, addr: Address, instr: Instruction) -> Self {
1216 if addr >= self.exp_instrs.len() {
1217 self.exp_instrs.resize_with(addr + 1, || Instruction::Nop);
1218 }
1219 self.exp_instrs[addr] = instr;
1220 self
1221 }
1222
1223 pub(crate) fn expect_symtable(mut self, key: SymbolKey, proto: SymbolPrototype) -> Self {
1225 let previous = self.exp_symtable.insert(key, proto);
1226 assert!(previous.is_none());
1227 self
1228 }
1229
1230 pub(crate) fn ignore_instrs(mut self) -> Self {
1232 self.ignore_instrs = true;
1233 self
1234 }
1235
1236 pub(crate) fn expect_datum(mut self, datum: Option<Value>) -> Self {
1238 self.exp_data.push(datum);
1239 self
1240 }
1241
1242 pub(crate) fn expect_err<S: Into<String>>(mut self, message: S) -> Self {
1244 let message = message.into();
1245 self.exp_error = Some(message);
1246 self
1247 }
1248
1249 pub(crate) fn check(self) {
1251 if let Some(message) = self.exp_error {
1252 match self.result {
1253 Ok(_) => panic!("Compilation succeeded but expected error: {}", message),
1254 Err(e) => assert_eq!(message, e.to_string()),
1255 }
1256 return;
1257 }
1258 let (image, symtable) = self.result.unwrap();
1259
1260 if self.ignore_instrs {
1261 assert!(
1262 self.exp_instrs.is_empty(),
1263 "Cannot ignore instructions if some are expected"
1264 );
1265 } else {
1266 assert_eq!(self.exp_instrs, image.instrs);
1267 }
1268
1269 assert_eq!(self.exp_data, image.data);
1270
1271 for (key, exp_proto) in self.exp_symtable {
1275 match symtable.get(&key) {
1276 Some(proto) => assert_eq!(
1277 std::mem::discriminant(&exp_proto),
1278 std::mem::discriminant(proto)
1279 ),
1280 None => panic!("Expected symbol {:?} not defined", key),
1281 }
1282 }
1283 }
1284 }
1285
1286 pub(crate) fn lc(line: usize, col: usize) -> LineCol {
1288 LineCol { line, col }
1289 }
1290}
1291
1292#[cfg(test)]
1293mod tests {
1294 use super::testutils::*;
1295 use super::*;
1296 use crate::syms::CallableMetadataBuilder;
1297 use std::borrow::Cow;
1298
1299 #[test]
1300 fn test_compile_nothing() {
1301 Tester::default().compile().check();
1302 }
1303
1304 #[test]
1305 fn test_compile_array_assignment_exprs() {
1306 Tester::default()
1307 .define("foo", SymbolPrototype::Array(ExprType::Integer, 3))
1308 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1309 .parse("foo(3, 4 + i, i) = 5")
1310 .compile()
1311 .expect_instr(0, Instruction::PushInteger(5, lc(1, 20)))
1312 .expect_instr(1, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 15)))
1313 .expect_instr(2, Instruction::PushInteger(4, lc(1, 8)))
1314 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 12)))
1315 .expect_instr(4, Instruction::AddIntegers(lc(1, 10)))
1316 .expect_instr(5, Instruction::PushInteger(3, lc(1, 5)))
1317 .expect_instr(6, Instruction::ArrayAssignment(SymbolKey::from("foo"), lc(1, 1), 3))
1318 .check();
1319 }
1320
1321 #[test]
1322 fn test_compile_array_assignment_ok_annotation() {
1323 Tester::default()
1324 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1325 .parse("a%(0) = 1")
1326 .compile()
1327 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
1328 .expect_instr(1, Instruction::PushInteger(0, lc(1, 4)))
1329 .expect_instr(2, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1330 .check();
1331 }
1332
1333 #[test]
1334 fn test_compile_array_assignment_index_double() {
1335 Tester::default()
1336 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1337 .parse("a(1.2) = 1")
1338 .compile()
1339 .expect_instr(0, Instruction::PushInteger(1, lc(1, 10)))
1340 .expect_instr(1, Instruction::PushDouble(1.2, lc(1, 3)))
1341 .expect_instr(2, Instruction::DoubleToInteger)
1342 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1343 .check();
1344 }
1345
1346 #[test]
1347 fn test_compile_array_assignment_index_bad_type() {
1348 Tester::default()
1349 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1350 .parse("a(TRUE) = 1")
1351 .compile()
1352 .expect_err("1:3: Array index must be INTEGER, not BOOLEAN")
1353 .check();
1354 }
1355
1356 #[test]
1357 fn test_compile_array_assignment_bad_annotation() {
1358 Tester::default()
1359 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1360 .parse("a#(0) = 1")
1361 .compile()
1362 .expect_err("1:1: Incompatible types in a# reference")
1363 .check();
1364 }
1365
1366 #[test]
1367 fn test_compile_array_assignment_double_to_integer_promotion() {
1368 Tester::default()
1369 .define("a", SymbolPrototype::Array(ExprType::Integer, 1))
1370 .define("d", SymbolPrototype::Variable(ExprType::Double))
1371 .parse("a(3) = d")
1372 .compile()
1373 .expect_instr(0, Instruction::LoadDouble(SymbolKey::from("d"), lc(1, 8)))
1374 .expect_instr(1, Instruction::DoubleToInteger)
1375 .expect_instr(2, Instruction::PushInteger(3, lc(1, 3)))
1376 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1377 .check();
1378 }
1379
1380 #[test]
1381 fn test_compile_array_assignment_integer_to_double_promotion() {
1382 Tester::default()
1383 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1384 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1385 .parse("a(3) = i")
1386 .compile()
1387 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 8)))
1388 .expect_instr(1, Instruction::IntegerToDouble)
1389 .expect_instr(2, Instruction::PushInteger(3, lc(1, 3)))
1390 .expect_instr(3, Instruction::ArrayAssignment(SymbolKey::from("a"), lc(1, 1), 1))
1391 .check();
1392 }
1393
1394 #[test]
1395 fn test_compile_array_assignment_bad_types() {
1396 Tester::default()
1397 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1398 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1399 .parse("a(3) = FALSE")
1400 .compile()
1401 .expect_err("1:1: Cannot assign value of type BOOLEAN to array of type DOUBLE")
1402 .check();
1403 }
1404
1405 #[test]
1406 fn test_compile_array_assignment_bad_dimensions() {
1407 Tester::default()
1408 .define("a", SymbolPrototype::Array(ExprType::Double, 1))
1409 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1410 .parse("a(3, 5) = 7")
1411 .compile()
1412 .expect_err("1:1: Cannot index array with 2 subscripts; need 1")
1413 .check();
1414 }
1415
1416 #[test]
1417 fn test_compile_array_assignment_not_defined() {
1418 Tester::default()
1419 .parse("a(3) = FALSE")
1420 .compile()
1421 .expect_err("1:1: Cannot index undefined array a")
1422 .check();
1423 }
1424
1425 #[test]
1426 fn test_compile_array_assignment_not_an_array() {
1427 Tester::default()
1428 .define("a", SymbolPrototype::Variable(ExprType::Integer))
1429 .parse("a(3) = FALSE")
1430 .compile()
1431 .expect_err("1:1: Cannot index non-array a")
1432 .check();
1433 }
1434
1435 #[test]
1436 fn test_compile_assignment_literal() {
1437 Tester::default()
1438 .parse("foo = 5")
1439 .compile()
1440 .expect_instr(0, Instruction::PushInteger(5, lc(1, 7)))
1441 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1442 .check();
1443 }
1444
1445 #[test]
1446 fn test_compile_assignment_varref_is_evaluated() {
1447 Tester::default()
1448 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1449 .parse("foo = i")
1450 .compile()
1451 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 7)))
1452 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1453 .check();
1454 }
1455
1456 #[test]
1457 fn test_compile_assignment_new_var_auto_ref_expr_determines_type() {
1458 Tester::default()
1459 .parse("foo = 2.3")
1460 .compile()
1461 .expect_instr(0, Instruction::PushDouble(2.3, lc(1, 7)))
1462 .expect_instr(1, Instruction::Assign(SymbolKey::from("foo")))
1463 .expect_symtable(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Double))
1464 .check();
1465 }
1466
1467 #[test]
1468 fn test_compile_assignment_new_var_explicit_ref_determines_type() {
1469 Tester::default()
1470 .parse("foo# = 2")
1471 .compile()
1472 .expect_instr(0, Instruction::PushInteger(2, lc(1, 8)))
1473 .expect_instr(1, Instruction::IntegerToDouble)
1474 .expect_instr(2, Instruction::Assign(SymbolKey::from("foo")))
1475 .expect_symtable(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Double))
1476 .check();
1477 }
1478
1479 #[test]
1480 fn test_compile_assignment_bad_types_existing_var() {
1481 Tester::default()
1482 .parse("foo# = TRUE")
1483 .compile()
1484 .expect_err("1:1: Cannot assign value of type BOOLEAN to variable of type DOUBLE")
1485 .check();
1486 }
1487
1488 #[test]
1489 fn test_compile_assignment_bad_annotation_existing_var() {
1490 Tester::default()
1491 .define(SymbolKey::from("foo"), SymbolPrototype::Variable(ExprType::Text))
1492 .parse("foo# = \"hello\"")
1493 .compile()
1494 .expect_err("1:1: Incompatible types in foo# reference")
1495 .check();
1496 }
1497
1498 #[test]
1499 fn test_compile_builtin_call_no_args() {
1500 Tester::default()
1501 .define_callable(CallableMetadataBuilder::new("CMD"))
1502 .parse("CMD")
1503 .compile()
1504 .expect_instr(0, Instruction::BuiltinCall(SymbolKey::from("CMD"), lc(1, 1), 0))
1505 .check();
1506 }
1507
1508 #[test]
1509 fn test_compile_builtin_call_increments_next_pc() {
1510 Tester::default()
1511 .define_callable(CallableMetadataBuilder::new("CMD").with_syntax(&[(
1512 &[],
1513 Some(&RepeatedSyntax {
1514 name: Cow::Borrowed("expr"),
1515 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1516 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1517 require_one: false,
1518 allow_missing: false,
1519 }),
1520 )]))
1521 .parse("IF TRUE THEN: CMD 1, 2: END IF")
1522 .compile()
1523 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 4)))
1524 .expect_instr(1, Instruction::JumpIfNotTrue(5))
1525 .expect_instr(2, Instruction::PushInteger(2, lc(1, 22)))
1526 .expect_instr(3, Instruction::PushInteger(1, lc(1, 19)))
1527 .expect_instr(4, Instruction::BuiltinCall(SymbolKey::from("CMD"), lc(1, 15), 2))
1528 .check();
1529 }
1530
1531 #[test]
1532 fn test_compile_data_top_level() {
1533 Tester::default()
1534 .parse("DATA TRUE, 3\nDATA , 1")
1535 .compile()
1536 .expect_datum(Some(Value::Boolean(true)))
1537 .expect_datum(Some(Value::Integer(3)))
1538 .expect_datum(None)
1539 .expect_datum(Some(Value::Integer(1)))
1540 .check();
1541 }
1542
1543 #[test]
1544 fn test_compile_data_interspersed() {
1545 Tester::default()
1546 .parse("IF FALSE THEN: DATA TRUE: END IF")
1547 .parse("FOR i = 1 TO 10: DATA , 5: NEXT")
1548 .parse("WHILE FALSE: DATA 2.3: WEND")
1549 .compile()
1550 .expect_datum(Some(Value::Boolean(true)))
1551 .expect_datum(None)
1552 .expect_datum(Some(Value::Integer(5)))
1553 .expect_datum(Some(Value::Double(2.3)))
1554 .ignore_instrs()
1555 .check();
1556 }
1557
1558 #[test]
1559 fn test_compile_dim_ok() {
1560 Tester::default()
1561 .parse("DIM var AS BOOLEAN")
1562 .compile()
1563 .expect_instr(
1564 0,
1565 Instruction::Dim(DimISpan {
1566 name: SymbolKey::from("var"),
1567 shared: false,
1568 vtype: ExprType::Boolean,
1569 }),
1570 )
1571 .check();
1572 Tester::default()
1573 .parse("DIM var AS DOUBLE")
1574 .compile()
1575 .expect_instr(
1576 0,
1577 Instruction::Dim(DimISpan {
1578 name: SymbolKey::from("var"),
1579 shared: false,
1580 vtype: ExprType::Double,
1581 }),
1582 )
1583 .check();
1584 Tester::default()
1585 .parse("DIM var AS INTEGER")
1586 .compile()
1587 .expect_instr(
1588 0,
1589 Instruction::Dim(DimISpan {
1590 name: SymbolKey::from("var"),
1591 shared: false,
1592 vtype: ExprType::Integer,
1593 }),
1594 )
1595 .check();
1596 Tester::default()
1597 .parse("DIM var AS STRING")
1598 .compile()
1599 .expect_instr(
1600 0,
1601 Instruction::Dim(DimISpan {
1602 name: SymbolKey::from("var"),
1603 shared: false,
1604 vtype: ExprType::Text,
1605 }),
1606 )
1607 .check();
1608 }
1609
1610 #[test]
1611 fn test_compile_dim_case_insensitivity() {
1612 Tester::default()
1613 .parse("DIM foo: DIM Foo")
1614 .compile()
1615 .expect_err("1:14: Cannot DIM already-defined symbol Foo")
1616 .check();
1617 }
1618
1619 #[test]
1620 fn test_compile_dim_name_overlap() {
1621 Tester::default()
1622 .define("SomeArray", SymbolPrototype::Array(ExprType::Integer, 3))
1623 .parse("DIM somearray")
1624 .compile()
1625 .expect_err("1:5: Cannot DIM already-defined symbol somearray")
1626 .check();
1627
1628 Tester::default()
1629 .define_callable(
1630 CallableMetadataBuilder::new("OUT").with_return_type(ExprType::Integer),
1631 )
1632 .parse("DIM OuT")
1633 .compile()
1634 .expect_err("1:5: Cannot DIM already-defined symbol OuT")
1635 .check();
1636
1637 Tester::default()
1638 .define("SomeVar", SymbolPrototype::Variable(ExprType::Integer))
1639 .parse("DIM SOMEVAR")
1640 .compile()
1641 .expect_err("1:5: Cannot DIM already-defined symbol SOMEVAR")
1642 .check();
1643 }
1644
1645 #[test]
1646 fn test_compile_dim_shared_ok() {
1647 Tester::default()
1648 .parse("DIM SHARED var AS BOOLEAN")
1649 .compile()
1650 .expect_instr(
1651 0,
1652 Instruction::Dim(DimISpan {
1653 name: SymbolKey::from("var"),
1654 shared: true,
1655 vtype: ExprType::Boolean,
1656 }),
1657 )
1658 .check();
1659 }
1660
1661 #[test]
1662 fn test_compile_dim_shared_name_overlap_with_local() {
1663 Tester::default()
1664 .parse("DIM var AS BOOLEAN: DIM SHARED Var AS BOOLEAN")
1665 .compile()
1666 .expect_err("1:32: Cannot DIM already-defined symbol Var")
1667 .check();
1668
1669 Tester::default()
1670 .parse("DIM SHARED var AS BOOLEAN: DIM Var AS BOOLEAN")
1671 .compile()
1672 .expect_err("1:32: Cannot DIM already-defined symbol Var")
1673 .check();
1674 }
1675
1676 #[test]
1677 fn test_compile_dim_array_immediate() {
1678 Tester::default()
1679 .parse("DIM var(1) AS INTEGER")
1680 .compile()
1681 .expect_instr(0, Instruction::PushInteger(1, lc(1, 9)))
1682 .expect_instr(
1683 1,
1684 Instruction::DimArray(DimArrayISpan {
1685 name: SymbolKey::from("var"),
1686 name_pos: lc(1, 5),
1687 shared: false,
1688 dimensions: 1,
1689 subtype: ExprType::Integer,
1690 subtype_pos: lc(1, 15),
1691 }),
1692 )
1693 .check();
1694 }
1695
1696 #[test]
1697 fn test_compile_dim_array_exprs() {
1698 Tester::default()
1699 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1700 .parse("DIM var(i, 3 + 4) AS INTEGER")
1701 .compile()
1702 .expect_instr(0, Instruction::PushInteger(3, lc(1, 12)))
1703 .expect_instr(1, Instruction::PushInteger(4, lc(1, 16)))
1704 .expect_instr(2, Instruction::AddIntegers(lc(1, 14)))
1705 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 9)))
1706 .expect_instr(
1707 4,
1708 Instruction::DimArray(DimArrayISpan {
1709 name: SymbolKey::from("var"),
1710 name_pos: lc(1, 5),
1711 shared: false,
1712 dimensions: 2,
1713 subtype: ExprType::Integer,
1714 subtype_pos: lc(1, 22),
1715 }),
1716 )
1717 .check();
1718 }
1719
1720 #[test]
1721 fn test_compile_dim_array_double_to_integer() {
1722 Tester::default()
1723 .parse("DIM var(3.7) AS INTEGER")
1724 .compile()
1725 .expect_instr(0, Instruction::PushDouble(3.7, lc(1, 9)))
1726 .expect_instr(1, Instruction::DoubleToInteger)
1727 .expect_instr(
1728 2,
1729 Instruction::DimArray(DimArrayISpan {
1730 name: SymbolKey::from("var"),
1731 name_pos: lc(1, 5),
1732 shared: false,
1733 dimensions: 1,
1734 subtype: ExprType::Integer,
1735 subtype_pos: lc(1, 17),
1736 }),
1737 )
1738 .check();
1739 }
1740
1741 #[test]
1742 fn test_compile_dim_array_dimension_type_error() {
1743 Tester::default()
1744 .parse("DIM var(TRUE) AS INTEGER")
1745 .compile()
1746 .expect_err("1:9: BOOLEAN is not a number")
1747 .check();
1748 }
1749
1750 #[test]
1751 fn test_compile_dim_array_shared_ok() {
1752 Tester::default()
1753 .parse("DIM SHARED var(1) AS INTEGER")
1754 .compile()
1755 .expect_instr(0, Instruction::PushInteger(1, lc(1, 16)))
1756 .expect_instr(
1757 1,
1758 Instruction::DimArray(DimArrayISpan {
1759 name: SymbolKey::from("var"),
1760 name_pos: lc(1, 12),
1761 shared: true,
1762 dimensions: 1,
1763 subtype: ExprType::Integer,
1764 subtype_pos: lc(1, 22),
1765 }),
1766 )
1767 .check();
1768 }
1769
1770 #[test]
1771 fn test_compile_dim_array_shared_name_overlap_with_local() {
1772 Tester::default()
1773 .parse("DIM var(1) AS BOOLEAN: DIM SHARED Var(1) AS BOOLEAN")
1774 .compile()
1775 .expect_err("1:35: Cannot DIM already-defined symbol Var")
1776 .check();
1777
1778 Tester::default()
1779 .parse("DIM SHARED var(1) AS BOOLEAN: DIM Var(1) AS BOOLEAN")
1780 .compile()
1781 .expect_err("1:35: Cannot DIM already-defined symbol Var")
1782 .check();
1783 }
1784
1785 #[test]
1786 fn test_compile_do_infinite() {
1787 Tester::default()
1788 .define_callable(CallableMetadataBuilder::new("FOO"))
1789 .parse("DO\nFOO\nLOOP")
1790 .compile()
1791 .expect_instr(0, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
1792 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
1793 .check();
1794 }
1795
1796 #[test]
1797 fn test_compile_do_pre_guard() {
1798 Tester::default()
1799 .define_callable(CallableMetadataBuilder::new("FOO"))
1800 .parse("DO WHILE TRUE\nFOO\nLOOP")
1801 .compile()
1802 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1803 .expect_instr(1, Instruction::JumpIfNotTrue(4))
1804 .expect_instr(2, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
1805 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
1806 .check();
1807 }
1808
1809 #[test]
1810 fn test_compile_do_post_guard() {
1811 Tester::default()
1812 .define_callable(CallableMetadataBuilder::new("FOO"))
1813 .parse("DO\nFOO\nLOOP WHILE TRUE")
1814 .compile()
1815 .expect_instr(0, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
1816 .expect_instr(1, Instruction::PushBoolean(true, lc(3, 12)))
1817 .expect_instr(2, Instruction::JumpIfTrue(0))
1818 .check();
1819 }
1820
1821 #[test]
1822 fn test_compile_end_without_exit_code() {
1823 Tester::default().parse("END").compile().expect_instr(0, Instruction::End(false)).check();
1824 }
1825
1826 #[test]
1827 fn test_compile_end_with_exit_code_expr() {
1828 Tester::default()
1829 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1830 .parse("END 2 + i")
1831 .compile()
1832 .expect_instr(0, Instruction::PushInteger(2, lc(1, 5)))
1833 .expect_instr(1, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 9)))
1834 .expect_instr(2, Instruction::AddIntegers(lc(1, 7)))
1835 .expect_instr(3, Instruction::End(true))
1836 .check();
1837 }
1838
1839 #[test]
1840 fn test_compile_end_with_exit_code_varref() {
1841 Tester::default()
1842 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1843 .parse("END i")
1844 .compile()
1845 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 5)))
1846 .expect_instr(1, Instruction::End(true))
1847 .check();
1848 }
1849
1850 #[test]
1851 fn test_compile_end_with_exit_code_type_error() {
1852 Tester::default()
1853 .parse("END TRUE")
1854 .compile()
1855 .expect_err("1:5: BOOLEAN is not a number")
1856 .check();
1857 }
1858
1859 #[test]
1860 fn test_compile_end_with_exit_code_double_to_integer() {
1861 Tester::default()
1862 .parse("END 2.3")
1863 .compile()
1864 .expect_instr(0, Instruction::PushDouble(2.3, lc(1, 5)))
1865 .expect_instr(1, Instruction::DoubleToInteger)
1866 .expect_instr(2, Instruction::End(true))
1867 .check();
1868 }
1869
1870 #[test]
1871 fn test_compile_exit_do_infinite_simple() {
1872 Tester::default()
1873 .parse("DO\nEXIT DO\nLOOP")
1874 .compile()
1875 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 2 }))
1876 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
1877 .check();
1878 }
1879
1880 #[test]
1881 fn test_compile_exit_do_pre_simple() {
1882 Tester::default()
1883 .parse("DO WHILE TRUE\nEXIT DO\nLOOP")
1884 .compile()
1885 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1886 .expect_instr(1, Instruction::JumpIfNotTrue(4))
1887 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 4 }))
1888 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
1889 .check();
1890 }
1891
1892 #[test]
1893 fn test_compile_exit_do_nested() {
1894 Tester::default()
1895 .parse("DO WHILE TRUE\nEXIT DO\nDO UNTIL FALSE\nEXIT DO\nLOOP\nLOOP")
1896 .compile()
1897 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1898 .expect_instr(1, Instruction::JumpIfNotTrue(8))
1899 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 8 }))
1900 .expect_instr(3, Instruction::PushBoolean(false, lc(3, 10)))
1901 .expect_instr(4, Instruction::JumpIfTrue(7))
1902 .expect_instr(5, Instruction::Jump(JumpISpan { addr: 7 }))
1903 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 3 }))
1904 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 0 }))
1905 .check();
1906 }
1907
1908 #[test]
1909 fn test_compile_exit_do_sequential() {
1910 Tester::default()
1911 .parse("DO WHILE TRUE\nEXIT DO\nLOOP\nDO WHILE TRUE\nEXIT DO\nLOOP")
1912 .compile()
1913 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1914 .expect_instr(1, Instruction::JumpIfNotTrue(4))
1915 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 4 }))
1916 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
1917 .expect_instr(4, Instruction::PushBoolean(true, lc(4, 10)))
1918 .expect_instr(5, Instruction::JumpIfNotTrue(8))
1919 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 8 }))
1920 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 4 }))
1921 .check();
1922 }
1923
1924 #[test]
1925 fn test_compile_exit_do_from_nested_while() {
1926 Tester::default()
1927 .parse("DO WHILE TRUE\nEXIT DO\nWHILE FALSE\nEXIT DO\nWEND\nLOOP")
1928 .compile()
1929 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 10)))
1930 .expect_instr(1, Instruction::JumpIfNotTrue(8))
1931 .expect_instr(2, Instruction::Jump(JumpISpan { addr: 8 }))
1932 .expect_instr(3, Instruction::PushBoolean(false, lc(3, 7)))
1933 .expect_instr(4, Instruction::JumpIfNotTrue(7))
1934 .expect_instr(5, Instruction::Jump(JumpISpan { addr: 8 }))
1935 .expect_instr(6, Instruction::Jump(JumpISpan { addr: 3 }))
1936 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 0 }))
1937 .check();
1938 }
1939
1940 #[test]
1941 fn test_compile_exit_do_outside_of_loop() {
1942 Tester::default()
1943 .parse("EXIT DO")
1944 .compile()
1945 .expect_err("1:1: EXIT DO outside of DO loop")
1946 .check();
1947
1948 Tester::default()
1949 .parse("WHILE TRUE: EXIT DO: WEND")
1950 .compile()
1951 .expect_err("1:13: EXIT DO outside of DO loop")
1952 .check();
1953 }
1954
1955 #[test]
1956 fn test_compile_for_simple_literals() {
1957 Tester::default()
1958 .parse("FOR iter = 1 TO 5: a = FALSE: NEXT")
1959 .compile()
1960 .expect_instr(0, Instruction::PushInteger(1, lc(1, 12)))
1961 .expect_instr(1, Instruction::Assign(SymbolKey::from("iter")))
1962 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
1963 .expect_instr(3, Instruction::PushInteger(5, lc(1, 17)))
1964 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 14)))
1965 .expect_instr(5, Instruction::JumpIfNotTrue(13))
1966 .expect_instr(6, Instruction::PushBoolean(false, lc(1, 24)))
1967 .expect_instr(7, Instruction::Assign(SymbolKey::from("a")))
1968 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
1969 .expect_instr(9, Instruction::PushInteger(1, lc(1, 18)))
1970 .expect_instr(10, Instruction::AddIntegers(lc(1, 14)))
1971 .expect_instr(11, Instruction::Assign(SymbolKey::from("iter")))
1972 .expect_instr(12, Instruction::Jump(JumpISpan { addr: 2 }))
1973 .check();
1974 }
1975
1976 #[test]
1977 fn test_compile_for_simple_varrefs_are_evaluated() {
1978 Tester::default()
1979 .define("i", SymbolPrototype::Variable(ExprType::Integer))
1980 .define("j", SymbolPrototype::Variable(ExprType::Integer))
1981 .parse("FOR iter = i TO j: a = FALSE: NEXT")
1982 .compile()
1983 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 12)))
1984 .expect_instr(1, Instruction::Assign(SymbolKey::from("iter")))
1985 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
1986 .expect_instr(3, Instruction::LoadInteger(SymbolKey::from("j"), lc(1, 17)))
1987 .expect_instr(4, Instruction::LessEqualIntegers(lc(1, 14)))
1988 .expect_instr(5, Instruction::JumpIfNotTrue(13))
1989 .expect_instr(6, Instruction::PushBoolean(false, lc(1, 24)))
1990 .expect_instr(7, Instruction::Assign(SymbolKey::from("a")))
1991 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
1992 .expect_instr(9, Instruction::PushInteger(1, lc(1, 18)))
1993 .expect_instr(10, Instruction::AddIntegers(lc(1, 14)))
1994 .expect_instr(11, Instruction::Assign(SymbolKey::from("iter")))
1995 .expect_instr(12, Instruction::Jump(JumpISpan { addr: 2 }))
1996 .check();
1997 }
1998
1999 #[test]
2000 fn test_compile_for_expressions() {
2001 Tester::default()
2002 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2003 .define("j", SymbolPrototype::Variable(ExprType::Integer))
2004 .parse("FOR iter = (i + 1) TO (2 + j): a = FALSE: NEXT")
2005 .compile()
2006 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 13)))
2007 .expect_instr(1, Instruction::PushInteger(1, lc(1, 17)))
2008 .expect_instr(2, Instruction::AddIntegers(lc(1, 15)))
2009 .expect_instr(3, Instruction::Assign(SymbolKey::from("iter")))
2010 .expect_instr(4, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2011 .expect_instr(5, Instruction::PushInteger(2, lc(1, 24)))
2012 .expect_instr(6, Instruction::LoadInteger(SymbolKey::from("j"), lc(1, 28)))
2013 .expect_instr(7, Instruction::AddIntegers(lc(1, 26)))
2014 .expect_instr(8, Instruction::LessEqualIntegers(lc(1, 20)))
2015 .expect_instr(9, Instruction::JumpIfNotTrue(17))
2016 .expect_instr(10, Instruction::PushBoolean(false, lc(1, 36)))
2017 .expect_instr(11, Instruction::Assign(SymbolKey::from("a")))
2018 .expect_instr(12, Instruction::LoadInteger(SymbolKey::from("iter"), lc(1, 5)))
2019 .expect_instr(13, Instruction::PushInteger(1, lc(1, 30)))
2020 .expect_instr(14, Instruction::AddIntegers(lc(1, 20)))
2021 .expect_instr(15, Instruction::Assign(SymbolKey::from("iter")))
2022 .expect_instr(16, Instruction::Jump(JumpISpan { addr: 4 }))
2023 .check();
2024 }
2025
2026 #[test]
2027 fn test_compile_for_double_auto_iterator() {
2028 Tester::default()
2029 .parse("FOR iter = 0 TO 2 STEP 0.1\nNEXT")
2030 .compile()
2031 .expect_instr(
2032 0,
2033 Instruction::JumpIfDefined(JumpIfDefinedISpan {
2034 var: SymbolKey::from("iter"),
2035 addr: 2,
2036 }),
2037 )
2038 .expect_instr(
2039 1,
2040 Instruction::Dim(DimISpan {
2041 name: SymbolKey::from("iter"),
2042 shared: false,
2043 vtype: ExprType::Double,
2044 }),
2045 )
2046 .expect_instr(2, Instruction::PushInteger(0, lc(1, 12)))
2047 .expect_instr(3, Instruction::IntegerToDouble)
2048 .expect_instr(4, Instruction::Assign(SymbolKey::from("iter")))
2049 .expect_instr(5, Instruction::LoadDouble(SymbolKey::from("iter"), lc(1, 5)))
2050 .expect_instr(6, Instruction::PushInteger(2, lc(1, 17)))
2051 .expect_instr(7, Instruction::IntegerToDouble)
2052 .expect_instr(8, Instruction::LessEqualDoubles(lc(1, 14)))
2053 .expect_instr(9, Instruction::JumpIfNotTrue(15))
2054 .expect_instr(10, Instruction::LoadDouble(SymbolKey::from("iter"), lc(1, 5)))
2055 .expect_instr(11, Instruction::PushDouble(0.1, lc(1, 24)))
2056 .expect_instr(12, Instruction::AddDoubles(lc(1, 14)))
2057 .expect_instr(13, Instruction::Assign(SymbolKey::from("iter")))
2058 .expect_instr(14, Instruction::Jump(JumpISpan { addr: 5 }))
2059 .check();
2060 }
2061
2062 #[test]
2063 fn test_compile_function_and_nothing_else() {
2064 Tester::default()
2065 .parse("FUNCTION foo: a = 3: END FUNCTION")
2066 .compile()
2067 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 8 }))
2068 .expect_instr(1, Instruction::EnterScope)
2069 .expect_instr(
2070 2,
2071 Instruction::Dim(DimISpan {
2072 name: SymbolKey::from("0return_foo"),
2073 shared: false,
2074 vtype: ExprType::Integer,
2075 }),
2076 )
2077 .expect_instr(3, Instruction::PushInteger(3, lc(1, 19)))
2078 .expect_instr(4, Instruction::Assign(SymbolKey::from("a")))
2079 .expect_instr(5, Instruction::LoadInteger(SymbolKey::from("0return_foo"), lc(1, 22)))
2080 .expect_instr(6, Instruction::LeaveScope)
2081 .expect_instr(7, Instruction::Return(lc(1, 22)))
2082 .expect_symtable(
2083 SymbolKey::from("foo"),
2084 SymbolPrototype::Callable(
2085 CallableMetadataBuilder::new("USER DEFINED FUNCTION")
2086 .with_syntax(&[(&[], None)])
2087 .with_category("User defined")
2088 .with_description("User defined function")
2089 .build(),
2090 ),
2091 )
2092 .check();
2093 }
2094
2095 #[test]
2096 fn test_compile_function_defined_between_code() {
2097 Tester::default()
2098 .parse("before = 1: FUNCTION foo: END FUNCTION: after = 2")
2099 .compile()
2100 .expect_instr(0, Instruction::PushInteger(1, lc(1, 10)))
2101 .expect_instr(1, Instruction::Assign(SymbolKey::from("before")))
2102 .expect_instr(2, Instruction::PushInteger(2, lc(1, 49)))
2103 .expect_instr(3, Instruction::Assign(SymbolKey::from("after")))
2104 .expect_instr(4, Instruction::Jump(JumpISpan { addr: 10 }))
2105 .expect_instr(5, Instruction::EnterScope)
2106 .expect_instr(
2107 6,
2108 Instruction::Dim(DimISpan {
2109 name: SymbolKey::from("0return_foo"),
2110 shared: false,
2111 vtype: ExprType::Integer,
2112 }),
2113 )
2114 .expect_instr(7, Instruction::LoadInteger(SymbolKey::from("0return_foo"), lc(1, 27)))
2115 .expect_instr(8, Instruction::LeaveScope)
2116 .expect_instr(9, Instruction::Return(lc(1, 27)))
2117 .expect_symtable(
2118 SymbolKey::from("foo"),
2119 SymbolPrototype::Callable(
2120 CallableMetadataBuilder::new("USER DEFINED FUNCTION")
2121 .with_syntax(&[(&[], None)])
2122 .with_category("User defined")
2123 .with_description("User defined function")
2124 .build(),
2125 ),
2126 )
2127 .check();
2128 }
2129
2130 #[test]
2131 fn test_compile_function_redefined_was_variable() {
2132 Tester::default()
2133 .parse("a = 1: FUNCTION a: END FUNCTION")
2134 .compile()
2135 .expect_err("1:17: Cannot define already-defined symbol a%")
2136 .check();
2137 }
2138
2139 #[test]
2140 fn test_compile_function_redefined_was_function() {
2141 Tester::default()
2142 .parse("FUNCTION a: END FUNCTION: FUNCTION A: END FUNCTION")
2143 .compile()
2144 .expect_err("1:36: Cannot define already-defined symbol A%")
2145 .check();
2146 }
2147
2148 #[test]
2149 fn test_compile_function_redefined_was_sub() {
2150 Tester::default()
2151 .parse("SUB a: END SUB: FUNCTION A: END FUNCTION")
2152 .compile()
2153 .expect_err("1:26: Cannot define already-defined symbol A%")
2154 .check();
2155 }
2156
2157 #[test]
2158 fn test_compile_sub_redefined_was_variable() {
2159 Tester::default()
2160 .parse("a = 1: SUB a: END SUB")
2161 .compile()
2162 .expect_err("1:12: Cannot define already-defined symbol a")
2163 .check();
2164 }
2165
2166 #[test]
2167 fn test_compile_sub_redefined_was_function() {
2168 Tester::default()
2169 .parse("FUNCTION a: END FUNCTION: SUB A: END SUB")
2170 .compile()
2171 .expect_err("1:31: Cannot define already-defined symbol A")
2172 .check();
2173 }
2174
2175 #[test]
2176 fn test_compile_sub_redefined_was_sub() {
2177 Tester::default()
2178 .parse("SUB a: END SUB: SUB A: END SUB")
2179 .compile()
2180 .expect_err("1:21: Cannot define already-defined symbol A")
2181 .check();
2182 }
2183
2184 #[test]
2185 fn test_compile_goto() {
2186 Tester::default()
2187 .parse("@first GOTO @second")
2188 .parse("@second: GOTO @first")
2189 .compile()
2190 .expect_instr(0, Instruction::Jump(JumpISpan { addr: 1 }))
2191 .expect_instr(1, Instruction::Jump(JumpISpan { addr: 0 }))
2192 .check();
2193 }
2194
2195 #[test]
2196 fn test_compile_gosub_and_return() {
2197 Tester::default()
2198 .define_callable(CallableMetadataBuilder::new("FOO"))
2199 .parse("@sub\nFOO\nRETURN\nGOSUB @sub")
2200 .compile()
2201 .expect_instr(0, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
2202 .expect_instr(1, Instruction::Return(lc(3, 1)))
2203 .expect_instr(2, Instruction::Call(JumpISpan { addr: 0 }))
2204 .check();
2205 }
2206
2207 #[test]
2208 fn test_compile_goto_unknown_label() {
2209 Tester::default()
2210 .parse("@fo: GOTO @foo")
2211 .compile()
2212 .expect_err("1:11: Unknown label foo")
2213 .check();
2214 }
2215
2216 #[test]
2217 fn test_compile_if_one_branch() {
2218 Tester::default()
2219 .define_callable(CallableMetadataBuilder::new("FOO"))
2220 .parse("IF FALSE THEN: FOO: END IF")
2221 .compile()
2222 .expect_instr(0, Instruction::PushBoolean(false, lc(1, 4)))
2223 .expect_instr(1, Instruction::JumpIfNotTrue(3))
2224 .expect_instr(2, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(1, 16), 0))
2225 .check();
2226 }
2227
2228 #[test]
2229 fn test_compile_if_many_branches() {
2230 Tester::default()
2231 .define_callable(CallableMetadataBuilder::new("FOO"))
2232 .define_callable(CallableMetadataBuilder::new("BAR"))
2233 .define_callable(CallableMetadataBuilder::new("BAZ"))
2234 .parse("IF FALSE THEN\nFOO\nELSEIF TRUE THEN\nBAR\nELSE\nBAZ\nEND IF")
2235 .compile()
2236 .expect_instr(0, Instruction::PushBoolean(false, lc(1, 4)))
2237 .expect_instr(1, Instruction::JumpIfNotTrue(4))
2238 .expect_instr(2, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
2239 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 11 }))
2240 .expect_instr(4, Instruction::PushBoolean(true, lc(3, 8)))
2241 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2242 .expect_instr(6, Instruction::BuiltinCall(SymbolKey::from("BAR"), lc(4, 1), 0))
2243 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 11 }))
2244 .expect_instr(8, Instruction::PushBoolean(true, lc(5, 1)))
2245 .expect_instr(9, Instruction::JumpIfNotTrue(11))
2246 .expect_instr(10, Instruction::BuiltinCall(SymbolKey::from("BAZ"), lc(6, 1), 0))
2247 .check();
2248 }
2249
2250 #[test]
2251 fn test_compile_on_error_reset() {
2252 Tester::default()
2253 .parse("ON ERROR GOTO 0")
2254 .compile()
2255 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::None))
2256 .check();
2257 }
2258
2259 #[test]
2260 fn test_compile_on_error_goto_label() {
2261 Tester::default()
2262 .parse("ON ERROR GOTO @foo\n\n\n@foo")
2263 .compile()
2264 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::Jump(1)))
2265 .check();
2266 }
2267
2268 #[test]
2269 fn test_compile_on_error_goto_unknown_label() {
2270 Tester::default()
2271 .parse("ON ERROR GOTO @foo")
2272 .compile()
2273 .expect_err("1:15: Unknown label foo")
2274 .check();
2275 }
2276
2277 #[test]
2278 fn test_compile_on_error_resume_next() {
2279 Tester::default()
2280 .parse("ON ERROR RESUME NEXT")
2281 .compile()
2282 .expect_instr(0, Instruction::SetErrorHandler(ErrorHandlerISpan::ResumeNext))
2283 .check();
2284 }
2285
2286 fn do_compile_case_guard_test(guards: &str, exp_expr_instrs: Vec<Instruction>) {
2291 let mut t = Tester::default()
2292 .define_callable(CallableMetadataBuilder::new("FOO"))
2293 .parse(&format!("SELECT CASE 5\nCASE {}\nFOO\nEND SELECT", guards))
2294 .compile()
2295 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2296 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")));
2297 let mut n = 2;
2298 for instr in exp_expr_instrs {
2299 t = t.expect_instr(n, instr);
2300 n += 1;
2301 }
2302 t.expect_instr(n, Instruction::JumpIfNotTrue(n + 2))
2303 .expect_instr(n + 1, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(3, 1), 0))
2304 .expect_instr(
2305 n + 2,
2306 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2307 )
2308 .check();
2309 }
2310
2311 #[test]
2312 fn test_compile_case_guards_equals() {
2313 do_compile_case_guard_test(
2314 "1 + 2",
2315 vec![
2316 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2317 Instruction::PushInteger(1, lc(2, 6)),
2318 Instruction::PushInteger(2, lc(2, 10)),
2319 Instruction::AddIntegers(lc(2, 8)),
2320 Instruction::EqualIntegers(lc(2, 6)),
2321 ],
2322 );
2323 }
2324
2325 #[test]
2326 fn test_compile_case_guards_is() {
2327 do_compile_case_guard_test(
2328 "IS = 9 + 8",
2329 vec![
2330 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2331 Instruction::PushInteger(9, lc(2, 11)),
2332 Instruction::PushInteger(8, lc(2, 15)),
2333 Instruction::AddIntegers(lc(2, 13)),
2334 Instruction::EqualIntegers(lc(2, 11)),
2335 ],
2336 );
2337
2338 do_compile_case_guard_test(
2339 "IS <> 9",
2340 vec![
2341 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2342 Instruction::PushInteger(9, lc(2, 12)),
2343 Instruction::NotEqualIntegers(lc(2, 12)),
2344 ],
2345 );
2346
2347 do_compile_case_guard_test(
2348 "IS < 9",
2349 vec![
2350 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2351 Instruction::PushInteger(9, lc(2, 11)),
2352 Instruction::LessIntegers(lc(2, 11)),
2353 ],
2354 );
2355
2356 do_compile_case_guard_test(
2357 "IS <= 9",
2358 vec![
2359 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2360 Instruction::PushInteger(9, lc(2, 12)),
2361 Instruction::LessEqualIntegers(lc(2, 12)),
2362 ],
2363 );
2364
2365 do_compile_case_guard_test(
2366 "IS > 9",
2367 vec![
2368 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 11)),
2369 Instruction::PushInteger(9, lc(2, 11)),
2370 Instruction::GreaterIntegers(lc(2, 11)),
2371 ],
2372 );
2373
2374 do_compile_case_guard_test(
2375 "IS >= 9",
2376 vec![
2377 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2378 Instruction::PushInteger(9, lc(2, 12)),
2379 Instruction::GreaterEqualIntegers(lc(2, 12)),
2380 ],
2381 );
2382 }
2383
2384 #[test]
2385 fn test_compile_case_guards_to() {
2386 do_compile_case_guard_test(
2387 "1 TO 2",
2388 vec![
2389 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2390 Instruction::PushInteger(1, lc(2, 6)),
2391 Instruction::GreaterEqualIntegers(lc(2, 6)),
2392 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)),
2393 Instruction::PushInteger(2, lc(2, 11)),
2394 Instruction::LessEqualIntegers(lc(2, 11)),
2395 Instruction::LogicalAnd(lc(2, 6)),
2396 ],
2397 );
2398 }
2399
2400 #[test]
2401 fn test_compile_case_guards_many() {
2402 do_compile_case_guard_test(
2403 "IS <> 9, 8",
2404 vec![
2405 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 12)),
2406 Instruction::PushInteger(9, lc(2, 12)),
2407 Instruction::NotEqualIntegers(lc(2, 12)),
2408 Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 15)),
2409 Instruction::PushInteger(8, lc(2, 15)),
2410 Instruction::EqualIntegers(lc(2, 15)),
2411 Instruction::LogicalOr(lc(2, 12)),
2412 ],
2413 );
2414 }
2415
2416 #[test]
2417 fn test_compile_select_no_cases() {
2418 Tester::default()
2419 .parse("SELECT CASE 5 + 3: END SELECT")
2420 .compile()
2421 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2422 .expect_instr(1, Instruction::PushInteger(3, lc(1, 17)))
2423 .expect_instr(2, Instruction::AddIntegers(lc(1, 15)))
2424 .expect_instr(3, Instruction::Assign(SymbolKey::from("0select1")))
2425 .expect_instr(
2426 4,
2427 Instruction::Unset(UnsetISpan {
2428 name: SymbolKey::from("0select1"),
2429 pos: lc(1, 20),
2430 }),
2431 )
2432 .check();
2433 }
2434
2435 #[test]
2436 fn test_compile_select_one_case() {
2437 Tester::default()
2438 .define_callable(CallableMetadataBuilder::new("FOO"))
2439 .parse("SELECT CASE 5\nCASE 7\nFOO\nEND SELECT")
2440 .compile()
2441 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2442 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2443 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2444 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2445 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2446 .expect_instr(5, Instruction::JumpIfNotTrue(7))
2447 .expect_instr(6, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(3, 1), 0))
2448 .expect_instr(
2449 7,
2450 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2451 )
2452 .check();
2453 }
2454
2455 #[test]
2456 fn test_compile_select_one_case_varref_is_evaluated() {
2457 Tester::default()
2458 .define("i", SymbolPrototype::Variable(ExprType::Integer))
2459 .parse("SELECT CASE i: END SELECT")
2460 .compile()
2461 .expect_instr(0, Instruction::LoadInteger(SymbolKey::from("i"), lc(1, 13)))
2462 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2463 .expect_instr(
2464 2,
2465 Instruction::Unset(UnsetISpan {
2466 name: SymbolKey::from("0select1"),
2467 pos: lc(1, 16),
2468 }),
2469 )
2470 .check();
2471 }
2472
2473 #[test]
2474 fn test_compile_select_only_case_else() {
2475 Tester::default()
2476 .define_callable(CallableMetadataBuilder::new("FOO"))
2477 .parse("SELECT CASE 5\nCASE ELSE\nFOO\nEND SELECT")
2478 .compile()
2479 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2480 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2481 .expect_instr(2, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(3, 1), 0))
2482 .expect_instr(
2483 3,
2484 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(4, 1) }),
2485 )
2486 .check();
2487 }
2488
2489 #[test]
2490 fn test_compile_select_many_cases_without_else() {
2491 Tester::default()
2492 .define_callable(CallableMetadataBuilder::new("FOO"))
2493 .define_callable(CallableMetadataBuilder::new("BAR"))
2494 .parse("SELECT CASE 5\nCASE 7\nFOO\nCASE IS <> 8\nBAR\nEND SELECT")
2495 .compile()
2496 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2497 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2498 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2499 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2500 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2501 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2502 .expect_instr(6, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(3, 1), 0))
2503 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 13 }))
2504 .expect_instr(8, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(4, 12)))
2505 .expect_instr(9, Instruction::PushInteger(8, lc(4, 12)))
2506 .expect_instr(10, Instruction::NotEqualIntegers(lc(4, 12)))
2507 .expect_instr(11, Instruction::JumpIfNotTrue(13))
2508 .expect_instr(12, Instruction::BuiltinCall(SymbolKey::from("BAR"), lc(5, 1), 0))
2509 .expect_instr(
2510 13,
2511 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(6, 1) }),
2512 )
2513 .check();
2514 }
2515
2516 #[test]
2517 fn test_compile_select_many_cases_with_else() {
2518 Tester::default()
2519 .define_callable(CallableMetadataBuilder::new("FOO"))
2520 .define_callable(CallableMetadataBuilder::new("BAR"))
2521 .parse("SELECT CASE 5\nCASE 7\nFOO\nCASE ELSE\nBAR\nEND SELECT")
2522 .compile()
2523 .expect_instr(0, Instruction::PushInteger(5, lc(1, 13)))
2524 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2525 .expect_instr(2, Instruction::LoadInteger(SymbolKey::from("0select1"), lc(2, 6)))
2526 .expect_instr(3, Instruction::PushInteger(7, lc(2, 6)))
2527 .expect_instr(4, Instruction::EqualIntegers(lc(2, 6)))
2528 .expect_instr(5, Instruction::JumpIfNotTrue(8))
2529 .expect_instr(6, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(3, 1), 0))
2530 .expect_instr(7, Instruction::Jump(JumpISpan { addr: 9 }))
2531 .expect_instr(8, Instruction::BuiltinCall(SymbolKey::from("BAR"), lc(5, 1), 0))
2532 .expect_instr(
2533 9,
2534 Instruction::Unset(UnsetISpan { name: SymbolKey::from("0select1"), pos: lc(6, 1) }),
2535 )
2536 .check();
2537 }
2538
2539 #[test]
2540 fn test_compile_select_internal_var_names() {
2541 Tester::default()
2542 .parse("SELECT CASE 0: END SELECT\nSELECT CASE 0: END SELECT")
2543 .compile()
2544 .expect_instr(0, Instruction::PushInteger(0, lc(1, 13)))
2545 .expect_instr(1, Instruction::Assign(SymbolKey::from("0select1")))
2546 .expect_instr(
2547 2,
2548 Instruction::Unset(UnsetISpan {
2549 name: SymbolKey::from("0select1"),
2550 pos: lc(1, 16),
2551 }),
2552 )
2553 .expect_instr(3, Instruction::PushInteger(0, lc(2, 13)))
2554 .expect_instr(4, Instruction::Assign(SymbolKey::from("0select2")))
2555 .expect_instr(
2556 5,
2557 Instruction::Unset(UnsetISpan {
2558 name: SymbolKey::from("0select2"),
2559 pos: lc(2, 16),
2560 }),
2561 )
2562 .check();
2563 }
2564
2565 #[test]
2566 fn test_compile_while() {
2567 Tester::default()
2568 .define_callable(CallableMetadataBuilder::new("FOO"))
2569 .parse("WHILE TRUE\nFOO\nWEND")
2570 .compile()
2571 .expect_instr(0, Instruction::PushBoolean(true, lc(1, 7)))
2572 .expect_instr(1, Instruction::JumpIfNotTrue(4))
2573 .expect_instr(2, Instruction::BuiltinCall(SymbolKey::from("FOO"), lc(2, 1), 0))
2574 .expect_instr(3, Instruction::Jump(JumpISpan { addr: 0 }))
2575 .check();
2576 }
2577}