git @ Cat's Eye Technologies SixtyPical / cc433e9
Try to improve error messages, thus breaking many unit tests. Chris Pressey 6 years ago
2 changed file(s) with 81 addition(s) and 75 deletion(s). Raw diff Collapse all Expand all
1212 pass
1313
1414
15 class UninitializedAccessError(StaticAnalysisError):
16 pass
17
18
19 class UninitializedOutputError(StaticAnalysisError):
15 class UnmeaningfulReadError(StaticAnalysisError):
16 pass
17
18
19 class UnmeaningfulOutputError(StaticAnalysisError):
2020 pass
2121
2222
2424 pass
2525
2626
27 class IllegalWriteError(StaticAnalysisError):
28 pass
29
30
31 class UsageClashError(StaticAnalysisError):
27 class ForbiddenWriteError(StaticAnalysisError):
28 pass
29
30
31 class InconsistentConstraintsError(StaticAnalysisError):
3232 pass
3333
3434
4444 pass
4545
4646
47 class Context():
47 class Context(object):
4848 """
4949 A location is touched if it was changed (or even potentially
5050 changed) during this routine, or some routine called by this routine.
5656 A location is writeable if it was listed in the outputs and trashes
5757 lists of this routine.
5858 """
59 def __init__(self, inputs, outputs, trashes):
59 def __init__(self, routine, inputs, outputs, trashes):
60 self.routine = routine
6061 self._touched = set()
6162 self._meaningful = set()
6263 self._writeable = set()
7374 self._writeable.add(ref)
7475
7576 def clone(self):
76 c = Context([], [], [])
77 c = Context(self.routine, [], [], [])
7778 c._touched = set(self._touched)
7879 c._meaningful = set(self._meaningful)
7980 c._writeable = set(self._writeable)
8081 return c
8182
8283 def set_from(self, c):
84 assert c.routine == self.routine
8385 self._touched = set(c._touched)
8486 self._meaningful = set(c._meaningful)
8587 self._writeable = set(c._writeable)
9395 yield ref
9496
9597 def assert_meaningful(self, *refs, **kwargs):
96 exception_class = kwargs.get('exception_class', UninitializedAccessError)
98 exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
9799 for ref in refs:
98100 if isinstance(ref, ConstantRef):
99101 pass
100102 elif isinstance(ref, LocationRef):
101103 if ref not in self._meaningful:
102 raise exception_class(ref.name)
104 message = '%s in %s' % (ref.name, self.routine.name)
105 raise exception_class(message)
103106 else:
104 raise ValueError(ref)
105
106 def assert_writeable(self, *refs):
107 raise NotImplementedError(ref)
108
109 def assert_writeable(self, *refs, **kwargs):
110 exception_class = kwargs.get('exception_class', ForbiddenWriteError)
107111 for ref in refs:
108112 if ref not in self._writeable:
109 raise IllegalWriteError(ref.name)
113 message = '%s in %s' % (ref.name, self.routine.name)
114 raise exception_class(message)
110115
111116 def set_touched(self, *refs):
112117 for ref in refs:
150155 # it's an extern, that's fine
151156 return
152157 type = routine.location.type
153 context = Context(type.inputs, type.outputs, type.trashes)
158 context = Context(routine, type.inputs, type.outputs, type.trashes)
154159 self.analyze_block(routine.block, context, routines)
155160 if not self.has_encountered_goto:
156161 for ref in type.outputs:
157 context.assert_meaningful(ref, exception_class=UninitializedOutputError)
162 context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
158163 for ref in context.each_touched():
159164 if ref not in type.outputs and ref not in type.trashes:
160 raise IllegalWriteError(ref.name)
165 message = '%s in %s' % (ref.name, routine.name)
166 raise ForbiddenWriteError(message)
161167 self.current_routine = None
162168
163169 def analyze_block(self, block, context, routines):
7272 | {
7373 | ld x, 0
7474 | }
75 ? IllegalWriteError: x
75 ? ForbiddenWriteError: x in main
7676
7777 | routine main
7878 | outputs x, z, n
323323 | st off, c
324324 | add a, lives
325325 | }
326 ? UninitializedAccessError: lives
326 ? UnmeaningfulReadError: lives in main
327327
328328 | byte lives
329329 | routine main
334334 | st off, c
335335 | add a, lives
336336 | }
337 ? UninitializedAccessError: a
337 ? UnmeaningfulReadError: a in main
338338
339339 Can't `add` to a memory location that isn't writeable.
340340
345345 | st off, c
346346 | add a, 0
347347 | }
348 ? IllegalWriteError: a
348 ? ForbiddenWriteError: a in main
349349
350350 ### sub ###
351351
370370 | st off, c
371371 | sub a, lives
372372 | }
373 ? UninitializedAccessError: lives
373 ? UnmeaningfulReadError: lives in main
374374
375375 | byte lives
376376 | routine main
381381 | st off, c
382382 | sub a, lives
383383 | }
384 ? UninitializedAccessError: a
384 ? UnmeaningfulReadError: a in main
385385
386386 Can't `sub` to a memory location that isn't writeable.
387387
392392 | st off, c
393393 | sub a, 0
394394 | }
395 ? IllegalWriteError: a
395 ? ForbiddenWriteError: a in main
396396
397397 ### inc ###
398398
404404 | {
405405 | inc x
406406 | }
407 ? UninitializedAccessError: x
407 ? UnmeaningfulReadError: x in main
408408
409409 | routine main
410410 | inputs x
412412 | {
413413 | inc x
414414 | }
415 ? IllegalWriteError: x
415 ? ForbiddenWriteError: x in main
416416
417417 | routine main
418418 | inputs x
433433 | {
434434 | dec x
435435 | }
436 ? UninitializedAccessError: x
436 ? UnmeaningfulReadError: x in main
437437
438438 | routine main
439439 | inputs x
441441 | {
442442 | dec x
443443 | }
444 ? IllegalWriteError: x
444 ? ForbiddenWriteError: x in main
445445
446446 | routine main
447447 | inputs x
470470 | {
471471 | cmp a, 4
472472 | }
473 ? IllegalWriteError: c
473 ? ForbiddenWriteError: c in main
474474
475475 | routine main
476476 | trashes z, c, n
477477 | {
478478 | cmp a, 4
479479 | }
480 ? UninitializedAccessError: a
480 ? UnmeaningfulReadError: a in main
481481
482482 ### and ###
483483
497497 | {
498498 | and a, 4
499499 | }
500 ? IllegalWriteError: a
500 ? ForbiddenWriteError: a in main
501501
502502 | routine main
503503 | trashes z, n
504504 | {
505505 | and a, 4
506506 | }
507 ? UninitializedAccessError: a
507 ? UnmeaningfulReadError: a in main
508508
509509 ### or ###
510510
524524 | {
525525 | or a, 4
526526 | }
527 ? IllegalWriteError: a
527 ? ForbiddenWriteError: a in main
528528
529529 | routine main
530530 | trashes z, n
531531 | {
532532 | or a, 4
533533 | }
534 ? UninitializedAccessError: a
534 ? UnmeaningfulReadError: a in main
535535
536536 ### xor ###
537537
551551 | {
552552 | xor a, 4
553553 | }
554 ? IllegalWriteError: a
554 ? ForbiddenWriteError: a in main
555555
556556 | routine main
557557 | trashes z, n
558558 | {
559559 | xor a, 4
560560 | }
561 ? UninitializedAccessError: a
561 ? UnmeaningfulReadError: a in main
562562
563563 ### shl ###
564564
578578 | {
579579 | shl a
580580 | }
581 ? IllegalWriteError: a
581 ? ForbiddenWriteError: a in main
582582
583583 | routine main
584584 | inputs a
586586 | {
587587 | shl a
588588 | }
589 ? UninitializedAccessError: c
589 ? UnmeaningfulReadError: c in main
590590
591591 ### shr ###
592592
606606 | {
607607 | shr a
608608 | }
609 ? IllegalWriteError: a
609 ? ForbiddenWriteError: a in main
610610
611611 | routine main
612612 | inputs a
614614 | {
615615 | shr a
616616 | }
617 ? UninitializedAccessError: c
617 ? UnmeaningfulReadError: c in main
618618
619619 ### call ###
620620
634634 | {
635635 | call foo
636636 | }
637 ? UninitializedAccessError: x
637 ? UnmeaningfulReadError: x in main
638638
639639 Note that if you call a routine that trashes a location, you also trash it.
640640
653653 | ld x, 0
654654 | call foo
655655 | }
656 ? IllegalWriteError: lives
656 ? ForbiddenWriteError: lives in main
657657
658658 | byte lives
659659 |
690690 | ld x, 0
691691 | call foo
692692 | }
693 ? UninitializedOutputError: lives
693 ? UnmeaningfulOutputError: lives in main
694694
695695 ...unless you write to it yourself afterwards.
696696
741741 | call foo
742742 | ld a, x
743743 | }
744 ? UninitializedAccessError: x
744 ? UnmeaningfulReadError: x in main
745745
746746 If a routine trashes locations, they are uninitialized in the caller after
747747 calling it.
766766 | call foo
767767 | ld a, x
768768 | }
769 ? UninitializedAccessError: x
769 ? UnmeaningfulReadError: x in main
770770
771771 Calling an extern is just the same as calling a defined routine with the
772772 same constraints.
794794 | {
795795 | call chrout
796796 | }
797 ? UninitializedAccessError: a
797 ? UnmeaningfulReadError: a in main
798798
799799 | routine chrout
800800 | inputs a
808808 | call chrout
809809 | ld x, a
810810 | }
811 ? UninitializedAccessError: a
811 ? UnmeaningfulReadError: a in main
812812
813813 ### if ###
814814
963963 | cmp x, 10
964964 | } until z
965965 | }
966 ? UninitializedAccessError: y
966 ? UnmeaningfulReadError: y in main
967967
968968 ### copy ###
969969
986986 | {
987987 | copy x, lives
988988 | }
989 ? UninitializedAccessError: x
989 ? UnmeaningfulReadError: x in main
990990
991991 Can't `copy` to a memory location that doesn't appear in (outputs ∪ trashes).
992992
10141014 | {
10151015 | copy 0, lives
10161016 | }
1017 ? IllegalWriteError: lives
1017 ? ForbiddenWriteError: lives in main
10181018
10191019 a, z, and n are trashed, and must be declared as such
10201020
10241024 | {
10251025 | copy 0, lives
10261026 | }
1027 ? IllegalWriteError: a
1027 ? ForbiddenWriteError: a in main
10281028
10291029 a, z, and n are trashed, and must not be declared as outputs.
10301030
10341034 | {
10351035 | copy 0, lives
10361036 | }
1037 ? UninitializedOutputError: a
1037 ? UnmeaningfulOutputError: a in main
10381038
10391039 Unless of course you subsequently initialize them.
10401040
11461146 | copy bar, foo
11471147 | call foo
11481148 | }
1149 ? UninitializedOutputError: x
1149 ? UnmeaningfulOutputError: x in main
11501150
11511151 `goto`, if present, must be in tail position (the final instruction in a routine.)
11521152
12491249
12501250 Jumping through the vector does indeed output the things the vector says it does.
12511251
1252 | vector foo trashes a, x, z, n
1253 |
1254 | routine bar trashes a, x, z, n {
1255 | ld x, 200
1256 | }
1257 |
1258 | routine sub inputs bar trashes foo, a, x, z, n {
1259 | ld x, 0
1260 | copy bar, foo
1261 | goto foo
1262 | }
1263 |
1264 | routine main inputs bar outputs a trashes z, n {
1265 | call sub
1266 | ld a, x
1267 | }
1268 ? UninitializedOutputError: x
1252 > | vector foo trashes a, x, z, n
1253 > |
1254 > | routine bar trashes a, x, z, n {
1255 > | ld x, 200
1256 > | }
1257 > |
1258 > | routine sub inputs bar trashes foo, a, x, z, n {
1259 > | ld x, 0
1260 > | copy bar, foo
1261 > | goto foo
1262 > | }
1263 > |
1264 > | routine main inputs bar outputs a trashes z, n {
1265 > | call sub
1266 > | ld a, x
1267 > | }
1268 > ? UnmeaningfulReadError: x in main
12691269
12701270 Ack, I have become a bit confused...