The exception object now contains an AST node, renders name.
Chris Pressey
4 years ago
9 | 9 | |
10 | 10 | |
11 | 11 | 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) | |
14 | 14 | |
15 | 15 | 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) | |
17 | 22 | |
18 | 23 | |
19 | 24 | class UnmeaningfulReadError(StaticAnalysisError): |
97 | 102 | |
98 | 103 | for ref in inputs: |
99 | 104 | 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) | |
101 | 106 | self._range[ref] = ref.max_range() |
102 | 107 | output_names = set() |
103 | 108 | for ref in outputs: |
104 | 109 | 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) | |
106 | 111 | output_names.add(ref.name) |
107 | 112 | self._writeable.add(ref) |
108 | 113 | for ref in trashes: |
109 | 114 | 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) | |
111 | 116 | 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) | |
113 | 118 | self._writeable.add(ref) |
114 | 119 | |
115 | 120 | def __str__(self): |
142 | 147 | pass |
143 | 148 | elif isinstance(ref, LocationRef): |
144 | 149 | if ref not in self._range: |
145 | message = '%s in %s' % (ref.name, self.routine.name) | |
150 | message = ref.name | |
146 | 151 | if kwargs.get('message'): |
147 | 152 | message += ' (%s)' % kwargs['message'] |
148 | raise exception_class(self.routine.line_number, message) | |
153 | raise exception_class(self.routine, message) | |
149 | 154 | elif isinstance(ref, IndexedRef): |
150 | 155 | self.assert_meaningful(ref.ref, **kwargs) |
151 | 156 | self.assert_meaningful(ref.index, **kwargs) |
159 | 164 | if routine_has_static(self.routine, ref): |
160 | 165 | continue |
161 | 166 | if ref not in self._writeable: |
162 | message = '%s in %s' % (ref.name, self.routine.name) | |
167 | message = ref.name | |
163 | 168 | if kwargs.get('message'): |
164 | 169 | message += ' (%s)' % kwargs['message'] |
165 | raise exception_class(self.routine.line_number, message) | |
170 | raise exception_class(self.routine, message) | |
166 | 171 | |
167 | 172 | def assert_in_range(self, inside, outside): |
168 | 173 | # FIXME there's a bit of I'm-not-sure-the-best-way-to-do-this-ness, here... |
179 | 184 | outside_range = (0, outside.type.size-1) |
180 | 185 | |
181 | 186 | 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, | |
183 | 188 | "Possible range of {} {} exceeds acceptable range of {} {}".format( |
184 | 189 | inside, inside_range, outside, outside_range |
185 | 190 | ) |
239 | 244 | def assert_type(self, type, *locations): |
240 | 245 | for location in locations: |
241 | 246 | 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) | |
245 | 248 | |
246 | 249 | def assert_affected_within(self, name, affecting_type, limiting_type): |
247 | 250 | assert name in ('inputs', 'outputs', 'trashes') |
250 | 253 | overage = affected - limited_to |
251 | 254 | if not overage: |
252 | 255 | 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, | |
255 | 258 | limiting_type, LocationRef.format_set(limited_to), |
256 | 259 | affecting_type, LocationRef.format_set(affected), |
257 | 260 | LocationRef.format_set(overage) |
258 | 261 | ) |
259 | raise IncompatibleConstraintsError(message) | |
262 | raise IncompatibleConstraintsError(self.current_routine, message) | |
260 | 263 | |
261 | 264 | def analyze_program(self, program): |
262 | 265 | assert isinstance(program, Program) |
296 | 299 | # even if we goto another routine, we can't trash an output. |
297 | 300 | for ref in trashed: |
298 | 301 | if ref in type_.outputs: |
299 | raise UnmeaningfulOutputError(routine.line_number, '%s in %s' % (ref.name, routine.name)) | |
302 | raise UnmeaningfulOutputError(routine, ref.name) | |
300 | 303 | |
301 | 304 | if not self.has_encountered_goto: |
302 | 305 | for ref in type_.outputs: |
303 | 306 | context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError) |
304 | 307 | for ref in context.each_touched(): |
305 | 308 | 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) | |
308 | 310 | self.current_routine = None |
309 | 311 | |
310 | 312 | def analyze_block(self, block, context): |
311 | 313 | assert isinstance(block, Block) |
312 | 314 | for i in block.instrs: |
313 | 315 | if self.has_encountered_goto: |
314 | raise IllegalJumpError(i) | |
316 | raise IllegalJumpError(i, i) | |
315 | 317 | self.analyze_instr(i, context) |
316 | 318 | |
317 | 319 | def analyze_instr(self, instr, context): |
337 | 339 | if TableType.is_a_table_type(src.ref.type, TYPE_BYTE) and dest.type == TYPE_BYTE: |
338 | 340 | pass |
339 | 341 | 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)) | |
343 | 343 | context.assert_meaningful(src, src.index) |
344 | 344 | context.assert_in_range(src.index, src.ref) |
345 | 345 | elif isinstance(src, IndirectRef): |
347 | 347 | if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE: |
348 | 348 | pass |
349 | 349 | else: |
350 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
350 | raise TypeMismatchError(instr, (src, dest)) | |
351 | 351 | context.assert_meaningful(src.ref, REG_Y) |
352 | 352 | 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)) | |
356 | 354 | else: |
357 | 355 | context.assert_meaningful(src) |
358 | 356 | context.copy_range(src, dest) |
362 | 360 | if src.type == TYPE_BYTE and TableType.is_a_table_type(dest.ref.type, TYPE_BYTE): |
363 | 361 | pass |
364 | 362 | else: |
365 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
363 | raise TypeMismatchError(instr, (src, dest)) | |
366 | 364 | context.assert_meaningful(dest.index) |
367 | 365 | context.assert_in_range(dest.index, dest.ref) |
368 | 366 | context.set_written(dest.ref) |
371 | 369 | if isinstance(dest.ref.type, PointerType) and src.type == TYPE_BYTE: |
372 | 370 | pass |
373 | 371 | else: |
374 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
372 | raise TypeMismatchError(instr, (src, dest)) | |
375 | 373 | context.assert_meaningful(dest.ref, REG_Y) |
376 | 374 | context.set_written(dest.ref) |
377 | 375 | 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)) | |
381 | 377 | else: |
382 | 378 | context.set_written(dest) |
383 | 379 | context.assert_meaningful(src) |
446 | 442 | context.set_unmeaningful(ref) |
447 | 443 | elif opcode == 'copy': |
448 | 444 | 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)) | |
450 | 446 | |
451 | 447 | # 1. check that their types are compatible |
452 | 448 | |
454 | 450 | if isinstance(src.ref.type, BufferType) and isinstance(dest.type, PointerType): |
455 | 451 | pass |
456 | 452 | else: |
457 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
453 | raise TypeMismatchError(instr, (src, dest)) | |
458 | 454 | elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef): |
459 | 455 | if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType): |
460 | 456 | pass |
461 | 457 | else: |
462 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
458 | raise TypeMismatchError(instr, (src, dest)) | |
463 | 459 | elif isinstance(src, IndirectRef) and isinstance(dest, LocationRef): |
464 | 460 | if isinstance(src.ref.type, PointerType) and dest.type == TYPE_BYTE: |
465 | 461 | pass |
466 | 462 | else: |
467 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
463 | raise TypeMismatchError(instr, (src, dest)) | |
468 | 464 | |
469 | 465 | elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndexedRef): |
470 | 466 | if src.type == TYPE_WORD and TableType.is_a_table_type(dest.ref.type, TYPE_WORD): |
476 | 472 | RoutineType.executable_types_compatible(src.type, dest.ref.type.of_type)): |
477 | 473 | pass |
478 | 474 | else: |
479 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
475 | raise TypeMismatchError(instr, (src, dest)) | |
480 | 476 | context.assert_in_range(dest.index, dest.ref) |
481 | 477 | |
482 | 478 | elif isinstance(src, IndexedRef) and isinstance(dest, LocationRef): |
486 | 482 | RoutineType.executable_types_compatible(src.ref.type.of_type, dest.type.of_type)): |
487 | 483 | pass |
488 | 484 | else: |
489 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
485 | raise TypeMismatchError(instr, (src, dest)) | |
490 | 486 | context.assert_in_range(src.index, src.ref) |
491 | 487 | |
492 | 488 | elif isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, LocationRef): |
497 | 493 | self.assert_affected_within('outputs', src.type, dest.type.of_type) |
498 | 494 | self.assert_affected_within('trashes', src.type, dest.type.of_type) |
499 | 495 | else: |
500 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
496 | raise TypeMismatchError(instr, (src, dest)) | |
501 | 497 | else: |
502 | raise TypeMismatchError(instr.line_number, (src, dest)) | |
498 | raise TypeMismatchError(instr, (src, dest)) | |
503 | 499 | |
504 | 500 | # 2. check that the context is meaningful |
505 | 501 | |
531 | 527 | type_ = location.type |
532 | 528 | |
533 | 529 | if not isinstance(type_, (RoutineType, VectorType)): |
534 | raise TypeMismatchError(instr.line_number, location) | |
530 | raise TypeMismatchError(instr, location) | |
535 | 531 | |
536 | 532 | # assert that the dest routine's inputs are all initialized |
537 | 533 | if isinstance(type_, VectorType): |
41 | 41 | | { |
42 | 42 | | ld x, 0 |
43 | 43 | | } |
44 | ? UnmeaningfulOutputError: a in main | |
44 | ? UnmeaningfulOutputError: a | |
45 | 45 | |
46 | 46 | | routine main |
47 | 47 | | inputs a |
72 | 72 | | { |
73 | 73 | | ld x, 0 |
74 | 74 | | } |
75 | ? ForbiddenWriteError: x in main | |
75 | ? ForbiddenWriteError: x | |
76 | 76 | |
77 | 77 | | routine main |
78 | 78 | | outputs x, z, n |
95 | 95 | | { |
96 | 96 | | ld x, 0 |
97 | 97 | | } |
98 | ? ForbiddenWriteError: x in main | |
98 | ? ForbiddenWriteError: x | |
99 | 99 | |
100 | 100 | | routine main |
101 | 101 | | inputs x |
126 | 126 | | { |
127 | 127 | | trash x |
128 | 128 | | } |
129 | ? ForbiddenWriteError: x in foo | |
129 | ? ForbiddenWriteError: x | |
130 | 130 | |
131 | 131 | | routine foo |
132 | 132 | | outputs x |
133 | 133 | | { |
134 | 134 | | trash x |
135 | 135 | | } |
136 | ? UnmeaningfulOutputError: x in foo | |
136 | ? UnmeaningfulOutputError: x | |
137 | 137 | |
138 | 138 | If a routine causes a location to be trashed, this must be declared in the caller. |
139 | 139 | |
161 | 161 | | { |
162 | 162 | | call trash_x |
163 | 163 | | } |
164 | ? ForbiddenWriteError: x in foo | |
164 | ? ForbiddenWriteError: x | |
165 | 165 | |
166 | 166 | | routine trash_x |
167 | 167 | | trashes x, z, n |
175 | 175 | | { |
176 | 176 | | call trash_x |
177 | 177 | | } |
178 | ? UnmeaningfulOutputError: x in foo | |
178 | ? UnmeaningfulOutputError: x (in foo, line 12) | |
179 | 179 | |
180 | 180 | If a routine reads or writes a user-define memory location, it needs to declare that too. |
181 | 181 | |
213 | 213 | | { |
214 | 214 | | ld a, x |
215 | 215 | | } |
216 | ? UnmeaningfulReadError: x in main | |
216 | ? UnmeaningfulReadError: x | |
217 | 217 | |
218 | 218 | Can't `ld` to a memory location that doesn't appear in (outputs ∪ trashes). |
219 | 219 | |
245 | 245 | | { |
246 | 246 | | ld a, 0 |
247 | 247 | | } |
248 | ? ForbiddenWriteError: a in main | |
248 | ? ForbiddenWriteError: a | |
249 | 249 | |
250 | 250 | | routine main |
251 | 251 | | trashes a, n |
252 | 252 | | { |
253 | 253 | | ld a, 0 |
254 | 254 | | } |
255 | ? ForbiddenWriteError: z in main | |
255 | ? ForbiddenWriteError: z | |
256 | 256 | |
257 | 257 | Can't `ld` a `word` type. |
258 | 258 | |
264 | 264 | | { |
265 | 265 | | ld a, foo |
266 | 266 | | } |
267 | ? TypeMismatchError: foo and a in main | |
267 | ? TypeMismatchError: foo and a | |
268 | 268 | |
269 | 269 | ### st ### |
270 | 270 | |
285 | 285 | | { |
286 | 286 | | st x, lives |
287 | 287 | | } |
288 | ? UnmeaningfulReadError: x in main | |
288 | ? UnmeaningfulReadError: x | |
289 | 289 | |
290 | 290 | Can't `st` to a memory location that doesn't appear in (outputs ∪ trashes). |
291 | 291 | |
311 | 311 | | { |
312 | 312 | | st 0, lives |
313 | 313 | | } |
314 | ? ForbiddenWriteError: lives in main | |
314 | ? ForbiddenWriteError: lives | |
315 | 315 | |
316 | 316 | Can't `st` a `word` type. |
317 | 317 | |
645 | 645 | | st off, c |
646 | 646 | | add a, lives |
647 | 647 | | } |
648 | ? UnmeaningfulReadError: lives in main | |
648 | ? UnmeaningfulReadError: lives | |
649 | 649 | |
650 | 650 | | byte lives |
651 | 651 | | routine main |
656 | 656 | | st off, c |
657 | 657 | | add a, lives |
658 | 658 | | } |
659 | ? UnmeaningfulReadError: a in main | |
659 | ? UnmeaningfulReadError: a | |
660 | 660 | |
661 | 661 | Can't `add` to a memory location that isn't writeable. |
662 | 662 | |
667 | 667 | | st off, c |
668 | 668 | | add a, 0 |
669 | 669 | | } |
670 | ? ForbiddenWriteError: a in main | |
670 | ? ForbiddenWriteError: a | |
671 | 671 | |
672 | 672 | You can `add` a word constant to a word memory location. |
673 | 673 | |
693 | 693 | | st off, c |
694 | 694 | | add score, 1999 |
695 | 695 | | } |
696 | ? UnmeaningfulOutputError: a in main | |
696 | ? UnmeaningfulOutputError: a | |
697 | 697 | |
698 | 698 | To be sure, `add`ing a word constant to a word memory location trashes `a`. |
699 | 699 | |
706 | 706 | | st off, c |
707 | 707 | | add score, 1999 |
708 | 708 | | } |
709 | ? ForbiddenWriteError: a in main | |
709 | ? ForbiddenWriteError: a | |
710 | 710 | |
711 | 711 | You can `add` a word memory location to another word memory location. |
712 | 712 | |
734 | 734 | | st off, c |
735 | 735 | | add score, delta |
736 | 736 | | } |
737 | ? ForbiddenWriteError: a in main | |
737 | ? ForbiddenWriteError: a | |
738 | 738 | |
739 | 739 | You can `add` a word memory location, or a constant, to a pointer. |
740 | 740 | |
764 | 764 | | add ptr, delta |
765 | 765 | | add ptr, word 1 |
766 | 766 | | } |
767 | ? ForbiddenWriteError: a in main | |
767 | ? ForbiddenWriteError: a | |
768 | 768 | |
769 | 769 | ### sub ### |
770 | 770 | |
789 | 789 | | st off, c |
790 | 790 | | sub a, lives |
791 | 791 | | } |
792 | ? UnmeaningfulReadError: lives in main | |
792 | ? UnmeaningfulReadError: lives | |
793 | 793 | |
794 | 794 | | byte lives |
795 | 795 | | routine main |
800 | 800 | | st off, c |
801 | 801 | | sub a, lives |
802 | 802 | | } |
803 | ? UnmeaningfulReadError: a in main | |
803 | ? UnmeaningfulReadError: a | |
804 | 804 | |
805 | 805 | Can't `sub` to a memory location that isn't writeable. |
806 | 806 | |
811 | 811 | | st off, c |
812 | 812 | | sub a, 0 |
813 | 813 | | } |
814 | ? ForbiddenWriteError: a in main | |
814 | ? ForbiddenWriteError: a | |
815 | 815 | |
816 | 816 | You can `sub` a word constant from a word memory location. |
817 | 817 | |
837 | 837 | | st on, c |
838 | 838 | | sub score, 1999 |
839 | 839 | | } |
840 | ? UnmeaningfulOutputError: a in main | |
840 | ? UnmeaningfulOutputError: a | |
841 | 841 | |
842 | 842 | You can `sub` a word memory location from another word memory location. |
843 | 843 | |
865 | 865 | | st off, c |
866 | 866 | | sub score, delta |
867 | 867 | | } |
868 | ? ForbiddenWriteError: a in main | |
868 | ? ForbiddenWriteError: a | |
869 | 869 | |
870 | 870 | ### inc ### |
871 | 871 | |
877 | 877 | | { |
878 | 878 | | inc x |
879 | 879 | | } |
880 | ? UnmeaningfulReadError: x in main | |
880 | ? UnmeaningfulReadError: x | |
881 | 881 | |
882 | 882 | | routine main |
883 | 883 | | inputs x |
885 | 885 | | { |
886 | 886 | | inc x |
887 | 887 | | } |
888 | ? ForbiddenWriteError: x in main | |
888 | ? ForbiddenWriteError: x | |
889 | 889 | |
890 | 890 | | routine main |
891 | 891 | | inputs x |
907 | 907 | | { |
908 | 908 | | inc foo |
909 | 909 | | } |
910 | ? TypeMismatchError: foo in main | |
910 | ? TypeMismatchError: foo | |
911 | 911 | |
912 | 912 | ### dec ### |
913 | 913 | |
919 | 919 | | { |
920 | 920 | | dec x |
921 | 921 | | } |
922 | ? UnmeaningfulReadError: x in main | |
922 | ? UnmeaningfulReadError: x | |
923 | 923 | |
924 | 924 | | routine main |
925 | 925 | | inputs x |
927 | 927 | | { |
928 | 928 | | dec x |
929 | 929 | | } |
930 | ? ForbiddenWriteError: x in main | |
930 | ? ForbiddenWriteError: x | |
931 | 931 | |
932 | 932 | | routine main |
933 | 933 | | inputs x |
949 | 949 | | { |
950 | 950 | | dec foo |
951 | 951 | | } |
952 | ? TypeMismatchError: foo in main | |
952 | ? TypeMismatchError: foo | |
953 | 953 | |
954 | 954 | ### cmp ### |
955 | 955 | |
969 | 969 | | { |
970 | 970 | | cmp a, 4 |
971 | 971 | | } |
972 | ? ForbiddenWriteError: c in main | |
972 | ? ForbiddenWriteError: c | |
973 | 973 | |
974 | 974 | | routine main |
975 | 975 | | trashes z, c, n |
976 | 976 | | { |
977 | 977 | | cmp a, 4 |
978 | 978 | | } |
979 | ? UnmeaningfulReadError: a in main | |
979 | ? UnmeaningfulReadError: a | |
980 | 980 | |
981 | 981 | ### and ### |
982 | 982 | |
996 | 996 | | { |
997 | 997 | | and a, 4 |
998 | 998 | | } |
999 | ? ForbiddenWriteError: a in main | |
999 | ? ForbiddenWriteError: a | |
1000 | 1000 | |
1001 | 1001 | | routine main |
1002 | 1002 | | trashes z, n |
1003 | 1003 | | { |
1004 | 1004 | | and a, 4 |
1005 | 1005 | | } |
1006 | ? UnmeaningfulReadError: a in main | |
1006 | ? UnmeaningfulReadError: a | |
1007 | 1007 | |
1008 | 1008 | ### or ### |
1009 | 1009 | |
1023 | 1023 | | { |
1024 | 1024 | | or a, 4 |
1025 | 1025 | | } |
1026 | ? ForbiddenWriteError: a in main | |
1026 | ? ForbiddenWriteError: a | |
1027 | 1027 | |
1028 | 1028 | | routine main |
1029 | 1029 | | trashes z, n |
1030 | 1030 | | { |
1031 | 1031 | | or a, 4 |
1032 | 1032 | | } |
1033 | ? UnmeaningfulReadError: a in main | |
1033 | ? UnmeaningfulReadError: a | |
1034 | 1034 | |
1035 | 1035 | ### xor ### |
1036 | 1036 | |
1050 | 1050 | | { |
1051 | 1051 | | xor a, 4 |
1052 | 1052 | | } |
1053 | ? ForbiddenWriteError: a in main | |
1053 | ? ForbiddenWriteError: a | |
1054 | 1054 | |
1055 | 1055 | | routine main |
1056 | 1056 | | trashes z, n |
1057 | 1057 | | { |
1058 | 1058 | | xor a, 4 |
1059 | 1059 | | } |
1060 | ? UnmeaningfulReadError: a in main | |
1060 | ? UnmeaningfulReadError: a | |
1061 | 1061 | |
1062 | 1062 | ### shl ### |
1063 | 1063 | |
1077 | 1077 | | { |
1078 | 1078 | | shl a |
1079 | 1079 | | } |
1080 | ? ForbiddenWriteError: a in main | |
1080 | ? ForbiddenWriteError: a | |
1081 | 1081 | |
1082 | 1082 | | routine main |
1083 | 1083 | | inputs a |
1085 | 1085 | | { |
1086 | 1086 | | shl a |
1087 | 1087 | | } |
1088 | ? UnmeaningfulReadError: c in main | |
1088 | ? UnmeaningfulReadError: c | |
1089 | 1089 | |
1090 | 1090 | ### shr ### |
1091 | 1091 | |
1105 | 1105 | | { |
1106 | 1106 | | shr a |
1107 | 1107 | | } |
1108 | ? ForbiddenWriteError: a in main | |
1108 | ? ForbiddenWriteError: a | |
1109 | 1109 | |
1110 | 1110 | | routine main |
1111 | 1111 | | inputs a |
1113 | 1113 | | { |
1114 | 1114 | | shr a |
1115 | 1115 | | } |
1116 | ? UnmeaningfulReadError: c in main | |
1116 | ? UnmeaningfulReadError: c | |
1117 | 1117 | |
1118 | 1118 | ### call ### |
1119 | 1119 | |
1133 | 1133 | | { |
1134 | 1134 | | call foo |
1135 | 1135 | | } |
1136 | ? UnmeaningfulReadError: x in main | |
1136 | ? UnmeaningfulReadError: x | |
1137 | 1137 | |
1138 | 1138 | Note that if you call a routine that trashes a location, you also trash it. |
1139 | 1139 | |
1152 | 1152 | | ld x, 0 |
1153 | 1153 | | call foo |
1154 | 1154 | | } |
1155 | ? ForbiddenWriteError: lives in main | |
1155 | ? ForbiddenWriteError: lives | |
1156 | 1156 | |
1157 | 1157 | | byte lives |
1158 | 1158 | | |
1189 | 1189 | | ld x, 0 |
1190 | 1190 | | call foo |
1191 | 1191 | | } |
1192 | ? UnmeaningfulOutputError: lives in main | |
1192 | ? UnmeaningfulOutputError: lives | |
1193 | 1193 | |
1194 | 1194 | ...unless you write to it yourself afterwards. |
1195 | 1195 | |
1240 | 1240 | | call foo |
1241 | 1241 | | ld a, x |
1242 | 1242 | | } |
1243 | ? UnmeaningfulReadError: x in main | |
1243 | ? UnmeaningfulReadError: x | |
1244 | 1244 | |
1245 | 1245 | If a routine trashes locations, they are uninitialized in the caller after |
1246 | 1246 | calling it. |
1265 | 1265 | | call foo |
1266 | 1266 | | ld a, x |
1267 | 1267 | | } |
1268 | ? UnmeaningfulReadError: x in main | |
1268 | ? UnmeaningfulReadError: x | |
1269 | 1269 | |
1270 | 1270 | Calling an extern is just the same as calling a defined routine with the |
1271 | 1271 | same constraints. |
1293 | 1293 | | { |
1294 | 1294 | | call chrout |
1295 | 1295 | | } |
1296 | ? UnmeaningfulReadError: a in main | |
1296 | ? UnmeaningfulReadError: a | |
1297 | 1297 | |
1298 | 1298 | | routine chrout |
1299 | 1299 | | inputs a |
1307 | 1307 | | call chrout |
1308 | 1308 | | ld x, a |
1309 | 1309 | | } |
1310 | ? UnmeaningfulReadError: a in main | |
1310 | ? UnmeaningfulReadError: a | |
1311 | 1311 | |
1312 | 1312 | ### trash ### |
1313 | 1313 | |
1333 | 1333 | | ld a, 0 |
1334 | 1334 | | trash a |
1335 | 1335 | | } |
1336 | ? UnmeaningfulOutputError: a in foo | |
1336 | ? UnmeaningfulOutputError: a | |
1337 | 1337 | |
1338 | 1338 | | routine foo |
1339 | 1339 | | inputs a |
1344 | 1344 | | trash a |
1345 | 1345 | | st a, x |
1346 | 1346 | | } |
1347 | ? UnmeaningfulReadError: a in foo | |
1347 | ? UnmeaningfulReadError: a | |
1348 | 1348 | |
1349 | 1349 | ### if ### |
1350 | 1350 | |
1509 | 1509 | | trash x |
1510 | 1510 | | } |
1511 | 1511 | | } |
1512 | ? ForbiddenWriteError: x in foo | |
1512 | ? ForbiddenWriteError: x (in foo, line 10) | |
1513 | 1513 | |
1514 | 1514 | | routine foo |
1515 | 1515 | | inputs a, x, z |
1521 | 1521 | | trash x |
1522 | 1522 | | } |
1523 | 1523 | | } |
1524 | ? ForbiddenWriteError: a in foo | |
1524 | ? ForbiddenWriteError: a (in foo, line 10) | |
1525 | 1525 | |
1526 | 1526 | ### repeat ### |
1527 | 1527 | |
1574 | 1574 | | cmp x, 10 |
1575 | 1575 | | } until z |
1576 | 1576 | | } |
1577 | ? UnmeaningfulReadError: y in main | |
1577 | ? UnmeaningfulReadError: y | |
1578 | 1578 | |
1579 | 1579 | And if you trash the test expression (i.e. `z` in the below) inside the loop, |
1580 | 1580 | this is an error too. |
1591 | 1591 | | copy one, two |
1592 | 1592 | | } until z |
1593 | 1593 | | } |
1594 | ? UnmeaningfulReadError: z in main | |
1594 | ? UnmeaningfulReadError: z | |
1595 | 1595 | |
1596 | 1596 | The body of `repeat forever` can be empty. |
1597 | 1597 | |
1623 | 1623 | | { |
1624 | 1624 | | copy x, lives |
1625 | 1625 | | } |
1626 | ? UnmeaningfulReadError: x in main | |
1626 | ? UnmeaningfulReadError: x | |
1627 | 1627 | |
1628 | 1628 | Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes). |
1629 | 1629 | |
1651 | 1651 | | { |
1652 | 1652 | | copy 0, lives |
1653 | 1653 | | } |
1654 | ? ForbiddenWriteError: lives in main | |
1654 | ? ForbiddenWriteError: lives | |
1655 | 1655 | |
1656 | 1656 | a, z, and n are trashed, and must be declared as such |
1657 | 1657 | |
1661 | 1661 | | { |
1662 | 1662 | | copy 0, lives |
1663 | 1663 | | } |
1664 | ? ForbiddenWriteError: n in main | |
1664 | ? ForbiddenWriteError: n | |
1665 | 1665 | |
1666 | 1666 | a, z, and n are trashed, and must not be declared as outputs. |
1667 | 1667 | |
1671 | 1671 | | { |
1672 | 1672 | | copy 0, lives |
1673 | 1673 | | } |
1674 | ? UnmeaningfulOutputError: n in main | |
1674 | ? UnmeaningfulOutputError: n | |
1675 | 1675 | |
1676 | 1676 | Unless of course you subsequently initialize them. |
1677 | 1677 | |
1900 | 1900 | | { |
1901 | 1901 | | copy foo, vec |
1902 | 1902 | | } |
1903 | ? ConstantConstraintError: foo in main | |
1903 | ? ConstantConstraintError: foo | |
1904 | 1904 | |
1905 | 1905 | | vector routine |
1906 | 1906 | | inputs x |
1922 | 1922 | | { |
1923 | 1923 | | copy foo, vec |
1924 | 1924 | | } |
1925 | ? ConstantConstraintError: foo in main | |
1925 | ? ConstantConstraintError: foo | |
1926 | 1926 | |
1927 | 1927 | | vector routine |
1928 | 1928 | | inputs x |
1944 | 1944 | | { |
1945 | 1945 | | copy foo, vec |
1946 | 1946 | | } |
1947 | ? ConstantConstraintError: foo in main | |
1947 | ? ConstantConstraintError: foo | |
1948 | 1948 | |
1949 | 1949 | You can copy the address of a routine into a vector, if that vector is |
1950 | 1950 | declared appropriately. |
2073 | 2073 | | copy bar, foo |
2074 | 2074 | | call foo |
2075 | 2075 | | } |
2076 | ? UnmeaningfulOutputError: x in main | |
2076 | ? UnmeaningfulOutputError: x | |
2077 | 2077 | |
2078 | 2078 | `goto`, if present, must be in tail position (the final instruction in a routine.) |
2079 | 2079 | |
2199 | 2199 | | call sub |
2200 | 2200 | | ld a, x |
2201 | 2201 | | } |
2202 | ? UnmeaningfulReadError: x in main | |
2202 | ? UnmeaningfulReadError: x | |
2203 | 2203 | |
2204 | 2204 | | vector routine |
2205 | 2205 | | outputs x |