git @ Cat's Eye Technologies SixtyPical / 0a83d90
Tighten structure of AST more. Chris Pressey 4 years ago
4 changed file(s) with 138 addition(s) and 113 deletion(s). Raw diff Collapse all Expand all
00 # encoding: UTF-8
11
2 from sixtypical.ast import Program, Routine, Block, Instr
2 from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, BlockOp, IfOp
33 from sixtypical.model import (
44 TYPE_BYTE, TYPE_WORD,
55 TableType, BufferType, PointerType, VectorType, RoutineType,
311311 self.analyze_instr(i, context)
312312
313313 def analyze_instr(self, instr, context):
314 assert isinstance(instr, Instr)
314 if isinstance(instr, SingleOp):
315 return self.analyze_single_op(instr, context)
316 elif isinstance(instr, BlockOp):
317 return self.analyze_block_op(instr, context)
318 elif isinstance(instr, IfOp):
319 return self.analyze_if_op(instr, context)
320 else:
321 raise NotImplementedError
322
323 def analyze_single_op(self, instr, context):
324
315325 opcode = instr.opcode
316326 dest = instr.dest
317327 src = instr.src
428438 context.assert_writeable(ref)
429439 context.set_touched(ref)
430440 context.set_unmeaningful(ref)
431 elif opcode == 'if':
432 incoming_meaningful = set(context.each_meaningful())
433
434 context1 = context.clone()
435 context2 = context.clone()
436 self.analyze_block(instr.block1, context1)
437 if instr.block2 is not None:
438 self.analyze_block(instr.block2, context2)
439
440 outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
441 outgoing_trashes = incoming_meaningful - outgoing_meaningful
442
443 # TODO may we need to deal with touched separately here too?
444 # probably not; if it wasn't meaningful in the first place, it
445 # doesn't really matter if you modified it or not, coming out.
446 for ref in context1.each_meaningful():
447 if ref in outgoing_trashes:
448 continue
449 context2.assert_meaningful(
450 ref, exception_class=InconsistentInitializationError,
451 message='initialized in block 1 but not in block 2 of `if {}`'.format(src)
452 )
453 for ref in context2.each_meaningful():
454 if ref in outgoing_trashes:
455 continue
456 context1.assert_meaningful(
457 ref, exception_class=InconsistentInitializationError,
458 message='initialized in block 2 but not in block 1 of `if {}`'.format(src)
459 )
460
461 # merge the contexts. this used to be a method called `set_from`
462 context._touched = set(context1._touched) | set(context2._touched)
463 context.set_meaningful(*list(outgoing_meaningful))
464 context._writeable = set(context1._writeable) | set(context2._writeable)
465
466 for ref in outgoing_trashes:
467 context.set_touched(ref)
468 context.set_unmeaningful(ref)
469
470 elif opcode == 'repeat':
471 # it will always be executed at least once, so analyze it having
472 # been executed the first time.
473 self.analyze_block(instr.block, context)
474 if src is not None: # None indicates 'repeat forever'
475 context.assert_meaningful(src)
476
477 # now analyze it having been executed a second time, with the context
478 # of it having already been executed.
479 self.analyze_block(instr.block, context)
480 if src is not None:
481 context.assert_meaningful(src)
482
483441 elif opcode == 'copy':
484442 if dest == REG_A:
485443 raise ForbiddenWriteError("{} cannot be used as destination for copy".format(dest))
562520
563521 context.set_touched(REG_A, FLAG_Z, FLAG_N)
564522 context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
565
566 elif opcode == 'with-sei':
567 self.analyze_block(instr.block, context)
568523 elif opcode == 'goto':
569524 location = instr.location
570525 type_ = location.type
590545 context.set_unmeaningful(instr.dest)
591546 else:
592547 raise NotImplementedError(opcode)
548
549 def analyze_if_op(self, instr, context):
550 incoming_meaningful = set(context.each_meaningful())
551
552 context1 = context.clone()
553 context2 = context.clone()
554 self.analyze_block(instr.block1, context1)
555 if instr.block2 is not None:
556 self.analyze_block(instr.block2, context2)
557
558 outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
559 outgoing_trashes = incoming_meaningful - outgoing_meaningful
560
561 # TODO may we need to deal with touched separately here too?
562 # probably not; if it wasn't meaningful in the first place, it
563 # doesn't really matter if you modified it or not, coming out.
564 for ref in context1.each_meaningful():
565 if ref in outgoing_trashes:
566 continue
567 context2.assert_meaningful(
568 ref, exception_class=InconsistentInitializationError,
569 message='initialized in block 1 but not in block 2 of `if {}`'.format(instr.src)
570 )
571 for ref in context2.each_meaningful():
572 if ref in outgoing_trashes:
573 continue
574 context1.assert_meaningful(
575 ref, exception_class=InconsistentInitializationError,
576 message='initialized in block 2 but not in block 1 of `if {}`'.format(instr.src)
577 )
578
579 # merge the contexts. this used to be a method called `set_from`
580 context._touched = set(context1._touched) | set(context2._touched)
581 context.set_meaningful(*list(outgoing_meaningful))
582 context._writeable = set(context1._writeable) | set(context2._writeable)
583
584 for ref in outgoing_trashes:
585 context.set_touched(ref)
586 context.set_unmeaningful(ref)
587
588 def analyze_block_op(self, instr, context):
589 if instr.opcode == 'repeat':
590 # it will always be executed at least once, so analyze it having
591 # been executed the first time.
592 self.analyze_block(instr.block, context)
593 if instr.src is not None: # None indicates 'repeat forever'
594 context.assert_meaningful(instr.src)
595
596 # now analyze it having been executed a second time, with the context
597 # of it having already been executed.
598 self.analyze_block(instr.block, context)
599 if instr.src is not None:
600 context.assert_meaningful(instr.src)
601 elif instr.opcode == 'with-sei':
602 self.analyze_block(instr.block, context)
603 else:
604 raise NotImplementedError(opcode)
7272
7373
7474 class BlockOp(Instr):
75 value_attrs = ('opcode', 'dest', 'src', 'inverted')
75 value_attrs = ('opcode', 'src', 'inverted')
7676 child_attrs = ('block',)
7777
7878
7979 class IfOp(Instr):
80 value_attrs = ('opcode', 'dest', 'src', 'inverted')
80 value_attrs = ('src', 'inverted')
8181 child_attrs = ('block1', 'block2',)
00 # encoding: UTF-8
11
2 from sixtypical.ast import Program, Routine, Block, Instr
2 from sixtypical.ast import Program, Routine, Block, Instr, SingleOp, BlockOp, IfOp
33 from sixtypical.model import (
44 ConstantRef, LocationRef, IndexedRef, IndirectRef, AddressRef,
55 TYPE_BIT, TYPE_BYTE, TYPE_WORD,
141141 self.compile_instr(instr)
142142
143143 def compile_instr(self, instr):
144 assert isinstance(instr, Instr)
144 if isinstance(instr, SingleOp):
145 return self.compile_single_op(instr)
146 elif isinstance(instr, BlockOp):
147 return self.compile_block_op(instr)
148 elif isinstance(instr, IfOp):
149 return self.compile_if_op(instr)
150 else:
151 raise NotImplementedError
152
153 def compile_single_op(self, instr):
154
145155 opcode = instr.opcode
146156 dest = instr.dest
147157 src = instr.src
355365 self.emitter.emit(JMP(Indirect(label)))
356366 else:
357367 raise NotImplementedError
358 elif opcode == 'if':
359 cls = {
360 False: {
361 'c': BCC,
362 'z': BNE,
363 },
364 True: {
365 'c': BCS,
366 'z': BEQ,
367 },
368 }[instr.inverted].get(src.name)
369 if cls is None:
370 raise UnsupportedOpcodeError(instr)
371 else_label = Label('else_label')
372 self.emitter.emit(cls(Relative(else_label)))
373 self.compile_block(instr.block1)
374 if instr.block2 is not None:
375 end_label = Label('end_label')
376 self.emitter.emit(JMP(Absolute(end_label)))
377 self.emitter.resolve_label(else_label)
378 self.compile_block(instr.block2)
379 self.emitter.resolve_label(end_label)
380 else:
381 self.emitter.resolve_label(else_label)
382 elif opcode == 'repeat':
383 top_label = self.emitter.make_label()
384 self.compile_block(instr.block)
385 if src is None: # indicates 'repeat forever'
386 self.emitter.emit(JMP(Absolute(top_label)))
387 else:
388 cls = {
389 False: {
390 'c': BCC,
391 'z': BNE,
392 },
393 True: {
394 'c': BCS,
395 'z': BEQ,
396 },
397 }[instr.inverted].get(src.name)
398 if cls is None:
399 raise UnsupportedOpcodeError(instr)
400 self.emitter.emit(cls(Relative(top_label)))
401 elif opcode == 'with-sei':
402 self.emitter.emit(SEI())
403 self.compile_block(instr.block)
404 self.emitter.emit(CLI())
405368 elif opcode == 'copy':
406369 if isinstance(src, (LocationRef, ConstantRef)) and isinstance(dest, IndirectRef):
407370 if src.type == TYPE_BYTE and isinstance(dest.ref.type, PointerType):
531494 pass
532495 else:
533496 raise NotImplementedError(opcode)
497
498 def compile_if_op(self, instr):
499 cls = {
500 False: {
501 'c': BCC,
502 'z': BNE,
503 },
504 True: {
505 'c': BCS,
506 'z': BEQ,
507 },
508 }[instr.inverted].get(instr.src.name)
509 if cls is None:
510 raise UnsupportedOpcodeError(instr)
511 else_label = Label('else_label')
512 self.emitter.emit(cls(Relative(else_label)))
513 self.compile_block(instr.block1)
514 if instr.block2 is not None:
515 end_label = Label('end_label')
516 self.emitter.emit(JMP(Absolute(end_label)))
517 self.emitter.resolve_label(else_label)
518 self.compile_block(instr.block2)
519 self.emitter.resolve_label(end_label)
520 else:
521 self.emitter.resolve_label(else_label)
522
523 def compile_block_op(self, instr):
524 if instr.opcode == 'repeat':
525 top_label = self.emitter.make_label()
526 self.compile_block(instr.block)
527 if instr.src is None: # indicates 'repeat forever'
528 self.emitter.emit(JMP(Absolute(top_label)))
529 else:
530 cls = {
531 False: {
532 'c': BCC,
533 'z': BNE,
534 },
535 True: {
536 'c': BCS,
537 'z': BEQ,
538 },
539 }[instr.inverted].get(instr.src.name)
540 if cls is None:
541 raise UnsupportedOpcodeError(instr)
542 self.emitter.emit(cls(Relative(top_label)))
543 elif instr.opcode == 'with-sei':
544 self.emitter.emit(SEI())
545 self.compile_block(instr.block)
546 self.emitter.emit(CLI())
547 else:
548 raise NotImplementedError(opcode)
351351 block2 = None
352352 if self.scanner.consume('else'):
353353 block2 = self.block()
354 return IfOp(opcode='if', dest=None, src=src,
355 block1=block1, block2=block2, inverted=inverted)
354 return IfOp(src=src, block1=block1, block2=block2, inverted=inverted)
356355 elif self.scanner.consume('repeat'):
357356 inverted = False
358357 src = None
363362 src = self.locexpr()
364363 else:
365364 self.scanner.expect('forever')
366 return BlockOp(opcode='repeat', dest=None, src=src,
367 block=block, inverted=inverted)
365 return BlockOp(opcode='repeat', src=src, block=block, inverted=inverted)
368366 elif self.scanner.token in ("ld",):
369367 # the same as add, sub, cmp etc below, except supports an indlocexpr for the src
370368 opcode = self.scanner.token
416414 self.scanner.expect("interrupts")
417415 self.scanner.expect("off")
418416 block = self.block()
419 return BlockOp(opcode='with-sei', dest=None, src=None, block=block)
417 return BlockOp(opcode='with-sei', src=None, block=block)
420418 elif self.scanner.consume("trash"):
421419 dest = self.locexpr()
422420 return SingleOp(opcode='trash', src=None, dest=dest)