git @ Cat's Eye Technologies SixtyPical / 1eaec60
The exception object now contains an AST node, renders name. Chris Pressey 3 years ago
2 changed file(s) with 104 addition(s) and 108 deletion(s). Raw diff Collapse all Expand all
99
1010
1111 class StaticAnalysisError(ValueError):
12 def __init__(self, line_number, message):
13 super(StaticAnalysisError, self).__init__(line_number, message)
12 def __init__(self, ast, message):
13 super(StaticAnalysisError, self).__init__(ast, message)
1414
1515 def __str__(self):
16 return "{} (Line {})".format(self.args[1], self.args[0])
16 ast = self.args[0]
17 message = self.args[1]
18 if isinstance(ast, Routine):
19 return "{} (in {}, line {})".format(message, ast.name, ast.line_number)
20 else:
21 return "{} (line {})".format(message, ast.line_number)
1722
1823
1924 class UnmeaningfulReadError(StaticAnalysisError):
97102
98103 for ref in inputs:
99104 if ref.is_constant():
100 raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
105 raise ConstantConstraintError(self.routine, ref.name)
101106 self._range[ref] = ref.max_range()
102107 output_names = set()
103108 for ref in outputs:
104109 if ref.is_constant():
105 raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
110 raise ConstantConstraintError(self.routine, ref.name)
106111 output_names.add(ref.name)
107112 self._writeable.add(ref)
108113 for ref in trashes:
109114 if ref.is_constant():
110 raise ConstantConstraintError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
115 raise ConstantConstraintError(self.routine, ref.name)
111116 if ref.name in output_names:
112 raise InconsistentConstraintsError(self.routine.line_number, '%s in %s' % (ref.name, routine.name))
117 raise InconsistentConstraintsError(self.routine, ref.name)
113118 self._writeable.add(ref)
114119
115120 def __str__(self):
142147 pass
143148 elif isinstance(ref, LocationRef):
144149 if ref not in self._range:
145 message = '%s in %s' % (ref.name, self.routine.name)
150 message = ref.name
146151 if kwargs.get('message'):
147152 message += ' (%s)' % kwargs['message']
148 raise exception_class(self.routine.line_number, message)
153 raise exception_class(self.routine, message)
149154 elif isinstance(ref, IndexedRef):
150155 self.assert_meaningful(ref.ref, **kwargs)
151156 self.assert_meaningful(ref.index, **kwargs)
159164 if routine_has_static(self.routine, ref):
160165 continue
161166 if ref not in self._writeable:
162 message = '%s in %s' % (ref.name, self.routine.name)
167 message = ref.name
163168 if kwargs.get('message'):
164169 message += ' (%s)' % kwargs['message']
165 raise exception_class(self.routine.line_number, message)
170 raise exception_class(self.routine, message)
166171
167172 def assert_in_range(self, inside, outside):
168173 # FIXME there's a bit of I'm-not-sure-the-best-way-to-do-this-ness, here...
179184 outside_range = (0, outside.type.size-1)
180185
181186 if inside_range[0] < outside_range[0] or inside_range[1] > outside_range[1]:
182 raise RangeExceededError(self.routine.line_number,
187 raise RangeExceededError(self.routine,
183188 "Possible range of {} {} exceeds acceptable range of {} {}".format(
184189 inside, inside_range, outside, outside_range
185190 )
239244 def assert_type(self, type, *locations):
240245 for location in locations:
241246 if location.type != type:
242 raise TypeMismatchError(self.current_routine.line_number, '%s in %s' %
243 (location.name, self.current_routine.name)
244 )
247 raise TypeMismatchError(self.current_routine, location.name)
245248
246249 def assert_affected_within(self, name, affecting_type, limiting_type):
247250 assert name in ('inputs', 'outputs', 'trashes')
250253 overage = affected - limited_to
251254 if not overage:
252255 return
253 message = 'in %s: %s for %s are %s\n\nbut %s affects %s\n\nwhich exceeds it by: %s ' % (
254 self.current_routine.name, name,
256 message = '%s for %s are %s\n\nbut %s affects %s\n\nwhich exceeds it by: %s ' % (
257 name,
255258 limiting_type, LocationRef.format_set(limited_to),
256259 affecting_type, LocationRef.format_set(affected),
257260 LocationRef.format_set(overage)
258261 )
259 raise IncompatibleConstraintsError(message)
262 raise IncompatibleConstraintsError(self.current_routine, message)
260263
261264 def analyze_program(self, program):
262265 assert isinstance(program, Program)
296299 # even if we goto another routine, we can't trash an output.
297300 for ref in trashed:
298301 if ref in type_.outputs:
299 raise UnmeaningfulOutputError(routine.line_number, '%s in %s' % (ref.name, routine.name))
302 raise UnmeaningfulOutputError(routine, ref.name)
300303
301304 if not self.has_encountered_goto:
302305 for ref in type_.outputs:
303306 context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
304307 for ref in context.each_touched():
305308 if ref not in type_.outputs and ref not in type_.trashes and not routine_has_static(routine, ref):
306 message = '%s in %s' % (ref.name, routine.name)
307 raise ForbiddenWriteError(routine.line_number, message)
309 raise ForbiddenWriteError(routine, ref.name)
308310 self.current_routine = None
309311
310312 def analyze_block(self, block, context):
311313 assert isinstance(block, Block)
312314 for i in block.instrs:
313315 if self.has_encountered_goto:
314 raise IllegalJumpError(i)
316 raise IllegalJumpError(i, i)
315317 self.analyze_instr(i, context)
316318
317319 def analyze_instr(self, instr, context):
337339 if TableType.is_a_table_type(src.ref.type, TYPE_BYTE) and dest.type == TYPE_BYTE:
338340 pass
339341 else:
340 raise TypeMismatchError(instr.line_number, '%s and %s in %s' %
341 (src.ref.name, dest.name, self.current_routine.name)
342 )
342 raise TypeMismatchError(instr, '{} and {}'.format(src.ref.name, dest.name))
343343 context.assert_meaningful(src, src.index)
344344 context.assert_in_range(src.index, src.ref)
345345 elif isinstance(src, IndirectRef):
347347 if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
348348 pass
349349 else:
350 raise TypeMismatchError(instr.line_number, (src, dest))
350 raise TypeMismatchError(instr, (src, dest))
351351 context.assert_meaningful(src.ref, REG_Y)
352352 elif src.type != dest.type:
353 raise TypeMismatchError(instr.line_number, '%s and %s in %s' %
354 (src.name, dest.name, self.current_routine.name)
355 )
353 raise TypeMismatchError(instr, '{} and {}'.format(src.name, dest.name))
356354 else:
357355 context.assert_meaningful(src)
358356 context.copy_range(src, dest)
362360 if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.ref.type, TYPE_BYTE):
363361 pass
364362 else:
365 raise TypeMismatchError(instr.line_number, (src, dest))
363 raise TypeMismatchError(instr, (src, dest))
366364 context.assert_meaningful(dest.index)
367365 context.assert_in_range(dest.index, dest.ref)
368366 context.set_written(dest.ref)
371369 if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE:
372370 pass
373371 else:
374 raise TypeMismatchError(instr.line_number, (src, dest))
372 raise TypeMismatchError(instr, (src, dest))
375373 context.assert_meaningful(dest.ref, REG_Y)
376374 context.set_written(dest.ref)
377375 elif src.type != dest.type:
378 raise TypeMismatchError(instr.line_number, '%r and %r in %s' %
379 (src, dest, self.current_routine.name)
380 )
376 raise TypeMismatchError(instr, '{} and {}'.format(src, name))
381377 else:
382378 context.set_written(dest)
383379 context.assert_meaningful(src)
446442 context.set_unmeaningful(ref)
447443 elif opcode == 'copy':
448444 if dest == REG_A:
449 raise ForbiddenWriteError(instr.line_number, "{} cannot be used as destination for copy".format(dest))
445 raise ForbiddenWriteError(instr, "{} cannot be used as destination for copy".format(dest))
450446
451447 # 1. check that their types are compatible
452448
454450 if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType):
455451 pass
456452 else:
457 raise TypeMismatchError(instr.line_number, (src, dest))
453 raise TypeMismatchError(instr, (src, dest))
458454 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
459455 if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
460456 pass
461457 else:
462 raise TypeMismatchError(instr.line_number, (src, dest))
458 raise TypeMismatchError(instr, (src, dest))
463459 elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef):
464460 if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE:
465461 pass
466462 else:
467 raise TypeMismatchError(instr.line_number, (src, dest))
463 raise TypeMismatchError(instr, (src, dest))
468464
469465 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef):
470466 if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD):
476472 RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)):
477473 pass
478474 else:
479 raise TypeMismatchError(instr.line_number, (src, dest))
475 raise TypeMismatchError(instr, (src, dest))
480476 context.assert_in_range(dest.index, dest.ref)
481477
482478 elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef):
486482 RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)):
487483 pass
488484 else:
489 raise TypeMismatchError(instr.line_number, (src, dest))
485 raise TypeMismatchError(instr, (src, dest))
490486 context.assert_in_range(src.index, src.ref)
491487
492488 elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef):
497493 self.assert_affected_within('outputs', src.type, dest.type.of_type)
498494 self.assert_affected_within('trashes', src.type, dest.type.of_type)
499495 else:
500 raise TypeMismatchError(instr.line_number, (src, dest))
496 raise TypeMismatchError(instr, (src, dest))
501497 else:
502 raise TypeMismatchError(instr.line_number, (src, dest))
498 raise TypeMismatchError(instr, (src, dest))
503499
504500 # 2. check that the context is meaningful
505501
531527 type_ = location.type
532528
533529 if not isinstance(type_, (RoutineType, VectorType)):
534 raise TypeMismatchError(instr.line_number, location)
530 raise TypeMismatchError(instr, location)
535531
536532 # assert that the dest routine's inputs are all initialized
537533 if isinstance(type_, VectorType):
4141 | {
4242 | ld x, 0
4343 | }
44 ? UnmeaningfulOutputError: a in main
44 ? UnmeaningfulOutputError: a
4545
4646 | routine main
4747 | inputs a
7272 | {
7373 | ld x, 0
7474 | }
75 ? ForbiddenWriteError: x in main
75 ? ForbiddenWriteError: x
7676
7777 | routine main
7878 | outputs x, z, n
9595 | {
9696 | ld x, 0
9797 | }
98 ? ForbiddenWriteError: x in main
98 ? ForbiddenWriteError: x
9999
100100 | routine main
101101 | inputs x
126126 | {
127127 | trash x
128128 | }
129 ? ForbiddenWriteError: x in foo
129 ? ForbiddenWriteError: x
130130
131131 | routine foo
132132 | outputs x
133133 | {
134134 | trash x
135135 | }
136 ? UnmeaningfulOutputError: x in foo
136 ? UnmeaningfulOutputError: x
137137
138138 If a routine causes a location to be trashed, this must be declared in the caller.
139139
161161 | {
162162 | call trash_x
163163 | }
164 ? ForbiddenWriteError: x in foo
164 ? ForbiddenWriteError: x
165165
166166 | routine trash_x
167167 | trashes x, z, n
175175 | {
176176 | call trash_x
177177 | }
178 ? UnmeaningfulOutputError: x in foo
178 ? UnmeaningfulOutputError: x (in foo, line 12)
179179
180180 If a routine reads or writes a user-define memory location, it needs to declare that too.
181181
213213 | {
214214 | ld a, x
215215 | }
216 ? UnmeaningfulReadError: x in main
216 ? UnmeaningfulReadError: x
217217
218218 Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes).
219219
245245 | {
246246 | ld a, 0
247247 | }
248 ? ForbiddenWriteError: a in main
248 ? ForbiddenWriteError: a
249249
250250 | routine main
251251 | trashes a, n
252252 | {
253253 | ld a, 0
254254 | }
255 ? ForbiddenWriteError: z in main
255 ? ForbiddenWriteError: z
256256
257257 Can't `ld` a `word` type.
258258
264264 | {
265265 | ld a, foo
266266 | }
267 ? TypeMismatchError: foo and a in main
267 ? TypeMismatchError: foo and a
268268
269269 ### st ###
270270
285285 | {
286286 | st x, lives
287287 | }
288 ? UnmeaningfulReadError: x in main
288 ? UnmeaningfulReadError: x
289289
290290 Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes).
291291
311311 | {
312312 | st 0, lives
313313 | }
314 ? ForbiddenWriteError: lives in main
314 ? ForbiddenWriteError: lives
315315
316316 Can't `st` a `word` type.
317317
645645 | st off, c
646646 | add a, lives
647647 | }
648 ? UnmeaningfulReadError: lives in main
648 ? UnmeaningfulReadError: lives
649649
650650 | byte lives
651651 | routine main
656656 | st off, c
657657 | add a, lives
658658 | }
659 ? UnmeaningfulReadError: a in main
659 ? UnmeaningfulReadError: a
660660
661661 Can't `add` to a memory location that isn't writeable.
662662
667667 | st off, c
668668 | add a, 0
669669 | }
670 ? ForbiddenWriteError: a in main
670 ? ForbiddenWriteError: a
671671
672672 You can `add` a word constant to a word memory location.
673673
693693 | st off, c
694694 | add score, 1999
695695 | }
696 ? UnmeaningfulOutputError: a in main
696 ? UnmeaningfulOutputError: a
697697
698698 To be sure, `add`ing a word constant to a word memory location trashes `a`.
699699
706706 | st off, c
707707 | add score, 1999
708708 | }
709 ? ForbiddenWriteError: a in main
709 ? ForbiddenWriteError: a
710710
711711 You can `add` a word memory location to another word memory location.
712712
734734 | st off, c
735735 | add score, delta
736736 | }
737 ? ForbiddenWriteError: a in main
737 ? ForbiddenWriteError: a
738738
739739 You can `add` a word memory location, or a constant, to a pointer.
740740
764764 | add ptr, delta
765765 | add ptr, word 1
766766 | }
767 ? ForbiddenWriteError: a in main
767 ? ForbiddenWriteError: a
768768
769769 ### sub ###
770770
789789 | st off, c
790790 | sub a, lives
791791 | }
792 ? UnmeaningfulReadError: lives in main
792 ? UnmeaningfulReadError: lives
793793
794794 | byte lives
795795 | routine main
800800 | st off, c
801801 | sub a, lives
802802 | }
803 ? UnmeaningfulReadError: a in main
803 ? UnmeaningfulReadError: a
804804
805805 Can't `sub` to a memory location that isn't writeable.
806806
811811 | st off, c
812812 | sub a, 0
813813 | }
814 ? ForbiddenWriteError: a in main
814 ? ForbiddenWriteError: a
815815
816816 You can `sub` a word constant from a word memory location.
817817
837837 | st on, c
838838 | sub score, 1999
839839 | }
840 ? UnmeaningfulOutputError: a in main
840 ? UnmeaningfulOutputError: a
841841
842842 You can `sub` a word memory location from another word memory location.
843843
865865 | st off, c
866866 | sub score, delta
867867 | }
868 ? ForbiddenWriteError: a in main
868 ? ForbiddenWriteError: a
869869
870870 ### inc ###
871871
877877 | {
878878 | inc x
879879 | }
880 ? UnmeaningfulReadError: x in main
880 ? UnmeaningfulReadError: x
881881
882882 | routine main
883883 | inputs x
885885 | {
886886 | inc x
887887 | }
888 ? ForbiddenWriteError: x in main
888 ? ForbiddenWriteError: x
889889
890890 | routine main
891891 | inputs x
907907 | {
908908 | inc foo
909909 | }
910 ? TypeMismatchError: foo in main
910 ? TypeMismatchError: foo
911911
912912 ### dec ###
913913
919919 | {
920920 | dec x
921921 | }
922 ? UnmeaningfulReadError: x in main
922 ? UnmeaningfulReadError: x
923923
924924 | routine main
925925 | inputs x
927927 | {
928928 | dec x
929929 | }
930 ? ForbiddenWriteError: x in main
930 ? ForbiddenWriteError: x
931931
932932 | routine main
933933 | inputs x
949949 | {
950950 | dec foo
951951 | }
952 ? TypeMismatchError: foo in main
952 ? TypeMismatchError: foo
953953
954954 ### cmp ###
955955
969969 | {
970970 | cmp a, 4
971971 | }
972 ? ForbiddenWriteError: c in main
972 ? ForbiddenWriteError: c
973973
974974 | routine main
975975 | trashes z, c, n
976976 | {
977977 | cmp a, 4
978978 | }
979 ? UnmeaningfulReadError: a in main
979 ? UnmeaningfulReadError: a
980980
981981 ### and ###
982982
996996 | {
997997 | and a, 4
998998 | }
999 ? ForbiddenWriteError: a in main
999 ? ForbiddenWriteError: a
10001000
10011001 | routine main
10021002 | trashes z, n
10031003 | {
10041004 | and a, 4
10051005 | }
1006 ? UnmeaningfulReadError: a in main
1006 ? UnmeaningfulReadError: a
10071007
10081008 ### or ###
10091009
10231023 | {
10241024 | or a, 4
10251025 | }
1026 ? ForbiddenWriteError: a in main
1026 ? ForbiddenWriteError: a
10271027
10281028 | routine main
10291029 | trashes z, n
10301030 | {
10311031 | or a, 4
10321032 | }
1033 ? UnmeaningfulReadError: a in main
1033 ? UnmeaningfulReadError: a
10341034
10351035 ### xor ###
10361036
10501050 | {
10511051 | xor a, 4
10521052 | }
1053 ? ForbiddenWriteError: a in main
1053 ? ForbiddenWriteError: a
10541054
10551055 | routine main
10561056 | trashes z, n
10571057 | {
10581058 | xor a, 4
10591059 | }
1060 ? UnmeaningfulReadError: a in main
1060 ? UnmeaningfulReadError: a
10611061
10621062 ### shl ###
10631063
10771077 | {
10781078 | shl a
10791079 | }
1080 ? ForbiddenWriteError: a in main
1080 ? ForbiddenWriteError: a
10811081
10821082 | routine main
10831083 | inputs a
10851085 | {
10861086 | shl a
10871087 | }
1088 ? UnmeaningfulReadError: c in main
1088 ? UnmeaningfulReadError: c
10891089
10901090 ### shr ###
10911091
11051105 | {
11061106 | shr a
11071107 | }
1108 ? ForbiddenWriteError: a in main
1108 ? ForbiddenWriteError: a
11091109
11101110 | routine main
11111111 | inputs a
11131113 | {
11141114 | shr a
11151115 | }
1116 ? UnmeaningfulReadError: c in main
1116 ? UnmeaningfulReadError: c
11171117
11181118 ### call ###
11191119
11331133 | {
11341134 | call foo
11351135 | }
1136 ? UnmeaningfulReadError: x in main
1136 ? UnmeaningfulReadError: x
11371137
11381138 Note that if you call a routine that trashes a location, you also trash it.
11391139
11521152 | ld x, 0
11531153 | call foo
11541154 | }
1155 ? ForbiddenWriteError: lives in main
1155 ? ForbiddenWriteError: lives
11561156
11571157 | byte lives
11581158 |
11891189 | ld x, 0
11901190 | call foo
11911191 | }
1192 ? UnmeaningfulOutputError: lives in main
1192 ? UnmeaningfulOutputError: lives
11931193
11941194 ...unless you write to it yourself afterwards.
11951195
12401240 | call foo
12411241 | ld a, x
12421242 | }
1243 ? UnmeaningfulReadError: x in main
1243 ? UnmeaningfulReadError: x
12441244
12451245 If a routine trashes locations, they are uninitialized in the caller after
12461246 calling it.
12651265 | call foo
12661266 | ld a, x
12671267 | }
1268 ? UnmeaningfulReadError: x in main
1268 ? UnmeaningfulReadError: x
12691269
12701270 Calling an extern is just the same as calling a defined routine with the
12711271 same constraints.
12931293 | {
12941294 | call chrout
12951295 | }
1296 ? UnmeaningfulReadError: a in main
1296 ? UnmeaningfulReadError: a
12971297
12981298 | routine chrout
12991299 | inputs a
13071307 | call chrout
13081308 | ld x, a
13091309 | }
1310 ? UnmeaningfulReadError: a in main
1310 ? UnmeaningfulReadError: a
13111311
13121312 ### trash ###
13131313
13331333 | ld a, 0
13341334 | trash a
13351335 | }
1336 ? UnmeaningfulOutputError: a in foo
1336 ? UnmeaningfulOutputError: a
13371337
13381338 | routine foo
13391339 | inputs a
13441344 | trash a
13451345 | st a, x
13461346 | }
1347 ? UnmeaningfulReadError: a in foo
1347 ? UnmeaningfulReadError: a
13481348
13491349 ### if ###
13501350
15091509 | trash x
15101510 | }
15111511 | }
1512 ? ForbiddenWriteError: x in foo
1512 ? ForbiddenWriteError: x (in foo, line 10)
15131513
15141514 | routine foo
15151515 | inputs a, x, z
15211521 | trash x
15221522 | }
15231523 | }
1524 ? ForbiddenWriteError: a in foo
1524 ? ForbiddenWriteError: a (in foo, line 10)
15251525
15261526 ### repeat ###
15271527
15741574 | cmp x, 10
15751575 | } until z
15761576 | }
1577 ? UnmeaningfulReadError: y in main
1577 ? UnmeaningfulReadError: y
15781578
15791579 And if you trash the test expression (i.e. `z` in the below) inside the loop,
15801580 this is an error too.
15911591 | copy one, two
15921592 | } until z
15931593 | }
1594 ? UnmeaningfulReadError: z in main
1594 ? UnmeaningfulReadError: z
15951595
15961596 The body of `repeat forever` can be empty.
15971597
16231623 | {
16241624 | copy x, lives
16251625 | }
1626 ? UnmeaningfulReadError: x in main
1626 ? UnmeaningfulReadError: x
16271627
16281628 Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes).
16291629
16511651 | {
16521652 | copy 0, lives
16531653 | }
1654 ? ForbiddenWriteError: lives in main
1654 ? ForbiddenWriteError: lives
16551655
16561656 a, z, and n are trashed, and must be declared as such
16571657
16611661 | {
16621662 | copy 0, lives
16631663 | }
1664 ? ForbiddenWriteError: n in main
1664 ? ForbiddenWriteError: n
16651665
16661666 a, z, and n are trashed, and must not be declared as outputs.
16671667
16711671 | {
16721672 | copy 0, lives
16731673 | }
1674 ? UnmeaningfulOutputError: n in main
1674 ? UnmeaningfulOutputError: n
16751675
16761676 Unless of course you subsequently initialize them.
16771677
19001900 | {
19011901 | copy foo, vec
19021902 | }
1903 ? ConstantConstraintError: foo in main
1903 ? ConstantConstraintError: foo
19041904
19051905 | vector routine
19061906 | inputs x
19221922 | {
19231923 | copy foo, vec
19241924 | }
1925 ? ConstantConstraintError: foo in main
1925 ? ConstantConstraintError: foo
19261926
19271927 | vector routine
19281928 | inputs x
19441944 | {
19451945 | copy foo, vec
19461946 | }
1947 ? ConstantConstraintError: foo in main
1947 ? ConstantConstraintError: foo
19481948
19491949 You can copy the address of a routine into a vector, if that vector is
19501950 declared appropriately.
20732073 | copy bar, foo
20742074 | call foo
20752075 | }
2076 ? UnmeaningfulOutputError: x in main
2076 ? UnmeaningfulOutputError: x
20772077
20782078 `goto`, if present, must be in tail position (the final instruction in a routine.)
20792079
21992199 | call sub
22002200 | ld a, x
22012201 | }
2202 ? UnmeaningfulReadError: x in main
2202 ? UnmeaningfulReadError: x
22032203
22042204 | vector routine
22052205 | outputs x