94 lines
2.6 KiB
Python
94 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from dataclasses import dataclass
|
|
from random import choices
|
|
from string import ascii_lowercase, digits
|
|
from typing import Generator, List, Union, Optional, Sequence
|
|
|
|
from ichor import isa
|
|
|
|
|
|
def gensym(prefix = None) -> isa.Label:
|
|
frag = ''.join(choices(ascii_lowercase + digits, k=8))
|
|
return isa.Label(f"{prefix or 'gensym'}_{frag}")
|
|
|
|
|
|
class FuncBuilder(object):
|
|
def __init__(self) -> None:
|
|
self._opcodes = []
|
|
|
|
def _write(self, op):
|
|
self._opcodes.append(op)
|
|
|
|
def write(self, op: Union[isa.Opcode, isa.Label, Sequence[isa.Opcode]]):
|
|
|
|
def flatten(o):
|
|
for e in o:
|
|
if isinstance(e, (isa.Opcode, isa.Label)):
|
|
yield e
|
|
else:
|
|
yield from flatten(e)
|
|
|
|
if isinstance(op, (isa.Opcode, isa.Label)):
|
|
self._opcodes.append(op)
|
|
else:
|
|
for e in op:
|
|
self.write(e)
|
|
|
|
def make_label(self, prefix: Optional[str] = ""):
|
|
return gensym(prefix)
|
|
|
|
def build(self) -> List[isa.Opcode]:
|
|
"""Assemble the written body into fully resolved opcodes."""
|
|
|
|
# The trivial two-pass assembler. First pass removes labels from the
|
|
# opcode stream, marking where they occurred.
|
|
|
|
labels = {}
|
|
unassembled = []
|
|
for op in self._opcodes:
|
|
match op:
|
|
case isa.Label(_) as l:
|
|
assert l not in labels # isa.Label marks must be unique.
|
|
labels[l] = len(unassembled)
|
|
case o:
|
|
unassembled.append(o)
|
|
|
|
# Second pass rewrites instructions (which can reference forwards OR
|
|
# backwards labels) with real targets instead of labels.
|
|
assembled = []
|
|
for op in unassembled:
|
|
match op:
|
|
case isa.GOTO(isa.Label(_) as l):
|
|
assembled.append(isa.GOTO(labels[l]))
|
|
|
|
case isa.VTEST(isa.Label(_) as l):
|
|
assembled.append(isa.VTEST(labels[l]))
|
|
|
|
case o:
|
|
assembled.append(o)
|
|
|
|
return assembled
|
|
|
|
|
|
def assemble(builder_cls=FuncBuilder, /, **opcodes: List[isa.Opcode]) -> List[isa.Opcode]:
|
|
builder = builder_cls()
|
|
for o in opcodes:
|
|
builder.write(o)
|
|
return builder.build()
|
|
|
|
|
|
class LocalBuilder(FuncBuilder):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._stack = 0
|
|
self._labels = {}
|
|
|
|
def _write(self, op):
|
|
pass
|
|
|
|
def write_local(self, label: isa.Label):
|
|
pass
|
|
|
|
def get_local(self, label: isa.Label):
|
|
pass
|