diff --git a/projects/shoggoth/src/python/ichor/assembler.py b/projects/shoggoth/src/python/ichor/assembler.py index 93bd599..fb36db9 100644 --- a/projects/shoggoth/src/python/ichor/assembler.py +++ b/projects/shoggoth/src/python/ichor/assembler.py @@ -26,6 +26,7 @@ class FuncBuilder(object): if not self._straightline: return + start_stack = self._stack match op: case isa.Label(_): # Labels abort @@ -35,6 +36,15 @@ class FuncBuilder(object): # Control ops abort self._straightline = False + case isa.CLOSUREC(n) | isa.CLOSUREF(n): + self._stack -= n + + case isa.TYPEREF(): + pass + + case isa.ARMREF(): + self._stack -= 1 + case isa.DROP(n): self._stack -= n @@ -48,6 +58,8 @@ class FuncBuilder(object): self._stack -= getattr(op, "nargs", 0) self._stack += 1 + print(op, "pre", start_stack, "post", self._stack) + def write(self, op: Union[isa.Opcode, isa.Label, Sequence[isa.Opcode]]): def flatten(o): @@ -58,15 +70,24 @@ class FuncBuilder(object): yield from flatten(e) if isinstance(op, (isa.Opcode, isa.Label)): - self._opcodes.append(op) + self._write(op) else: - for e in op: + for e in flatten(op): self.write(e) def make_label(self, prefix: Optional[str] = ""): return gensym(prefix) + def mark_argument(self, prefix: Optional[str] = ""): + if not self._opcodes: + s = gensym(prefix) + self._stack += 1 + self._slots[s] = self._stack - 1 + return s + else: + raise Exception("Cannot mark arguments after writing opcodes") + def mark_label(self, prefix: Optional[str] = ""): l = self.make_label(prefix=prefix) self.write(l) @@ -80,7 +101,8 @@ class FuncBuilder(object): elif self._straightline: s = gensym(prefix) - self._slots[s] = self._stack + assert self._stack > 0 + self._slots[s] = self._stack - 1 return s else: diff --git a/projects/shoggoth/src/python/ichor/bootstrap.py b/projects/shoggoth/src/python/ichor/bootstrap.py index eaf5b47..37e8baf 100644 --- a/projects/shoggoth/src/python/ichor/bootstrap.py +++ b/projects/shoggoth/src/python/ichor/bootstrap.py @@ -5,6 +5,7 @@ Hopefully no "real" interpreter ever uses this code, since it's obviously replac """ from ichor import isa +from ichor.assembler import FuncBuilder from ichor.state import Module, Variant @@ -42,45 +43,87 @@ NOT1 = BOOTSTRAP.define_function( ], ) +_b = FuncBuilder() + +# Capture args +_x = _b.mark_argument() +_y = _b.mark_argument() + +# Const +_b.write(isa.IDENTIFIERC("bool")) +_b.write(isa.TYPEREF()) +_bool_t = _b.mark_slot() + +_b.write(isa.SLOT(_bool_t)) +_b.write(isa.IDENTIFIERC("true")) +_b.write(isa.ARMREF()) +_true_t = _b.mark_slot() + +_b.write(isa.SLOT(_bool_t)) +_b.write(isa.IDENTIFIERC("false")) +_b.write(isa.ARMREF()) +_false_t = _b.mark_slot() + +# x: Bool, x: Bool -> Bool +_true_l = _b.make_label() +_b.write(isa.SLOT(_x)) +_b.write(isa.SLOT(_true_t)) +_b.write(isa.ATEST(_true_l)) + +_b.write(isa.SLOT(_y)) +_b.write(isa.SLOT(_true_t)) +_b.write(isa.ATEST(_true_l)) + +_b.write(isa.SLOT(_false_t)) +_b.write(isa.ARM(0)) +_b.write(isa.RETURN()) + +_b.write(_true_l) +_b.write(isa.SLOT(_true_t)) +_b.write(isa.ARM(0)) +_b.write(isa.RETURN()) + OR2 = BOOTSTRAP.define_function( f";or;{BOOL},{BOOL};{BOOL}", - [ - isa.BREAK(), - ], + _b.build() ) +_b = FuncBuilder() +_b.write(isa.BREAK()) + OR3 = BOOTSTRAP.define_function( f";or;{BOOL},{BOOL},{BOOL};{BOOL}", - [ - isa.BREAK(), - ] + _b.build() ) +_b = FuncBuilder() +_b.write(isa.BREAK()) + AND2 = BOOTSTRAP.define_function( f";and;{BOOL},{BOOL};{BOOL}", - [ - isa.BREAK(), - ], + _b.build() ) +_b = FuncBuilder() +_b.write(isa.BREAK()) + AND3 = BOOTSTRAP.define_function( f";and;{BOOL},{BOOL},{BOOL};{BOOL}", - [ - isa.BREAK(), - ], + _b.build() ) +_b = FuncBuilder() +_b.write(isa.BREAK()) + XOR2 = BOOTSTRAP.define_function( f";xor;{BOOL},{BOOL};{BOOL}", - [ - isa.BREAK(), - ], + _b.build() ) +_b = FuncBuilder() +_b.write(isa.BREAK()) + XOR3 = BOOTSTRAP.define_function( f";xor;{BOOL},{BOOL},{BOOL};{BOOL}", - [ - # A^B|B^C - isa.BREAK(), - ] + _b.build() ) diff --git a/projects/shoggoth/src/python/ichor/interpreter.py b/projects/shoggoth/src/python/ichor/interpreter.py index 2361019..cd83676 100644 --- a/projects/shoggoth/src/python/ichor/interpreter.py +++ b/projects/shoggoth/src/python/ichor/interpreter.py @@ -264,11 +264,11 @@ class Interpreter(BaseInterpreter): def handle_atest(self, state: InterpreterState, opcode: isa.ATEST) -> InterpreterState: armref: VariantRef = state.stackframe.pop() if not isinstance(armref, VariantRef): - return self.handle_fault(state, opcode, "VTEST must be given a variant reference") + return self.handle_fault(state, opcode, "ATEST must be given a variant reference") inst: Variant = state.stackframe.pop() if not isinstance(inst, Variant): - return self.handle_fault(state, opcode, "VTEST must be given an instance of a variant") + return self.handle_fault(state, opcode, "ATEST must be given an instance of a variant") if inst.type == armref.type.name and inst.variant == armref.arm: state.stackframe.goto(opcode.target) diff --git a/projects/shoggoth/test/python/ichor/fixtures.py b/projects/shoggoth/test/python/ichor/fixtures.py index 08c6a5b..3f5d101 100644 --- a/projects/shoggoth/test/python/ichor/fixtures.py +++ b/projects/shoggoth/test/python/ichor/fixtures.py @@ -19,8 +19,9 @@ class LoggingInterpreter(Interpreter): b.append(" stack:") for offset, it in zip(range(0, len(state.stackframe), 1), state.stackframe): b.append(f" {offset: <3} {it}") - b.append(f" op: {opcode}") - print(indent("\n".join(b), " " * state.stackframe.depth)) + + b.append(f" op: {opcode}") + print(indent("\n".join(b), " " * state.stackframe.depth)) return state diff --git a/projects/shoggoth/test/python/ichor/test_bootstrap.py b/projects/shoggoth/test/python/ichor/test_bootstrap.py index f42e7b7..2754a1c 100644 --- a/projects/shoggoth/test/python/ichor/test_bootstrap.py +++ b/projects/shoggoth/test/python/ichor/test_bootstrap.py @@ -3,7 +3,13 @@ from .fixtures import * # noqa from ichor import isa -from ichor.bootstrap import FALSE, NOT1, TRUE +from ichor.bootstrap import ( + FALSE, + NOT1, + OR2, + OR2_INSTRS, + TRUE, +) import pytest @@ -20,19 +26,22 @@ def test_not(vm, stack, ret): ], stack = stack) == ret -# @pytest.mark.parametrize("stack,ret", [ -# [[False, False], [False]], -# [[True, False], [True]], -# [[False, True], [True]], -# [[True, True], [True]], -# ]) -# def test_or(vm, stack, ret): -# assert vm.run([ -# isa.IDENTIFIERC(OR2), -# isa.FUNREF(), -# isa.CALLF(2), -# isa.RETURN(1) -# ], stack = stack) == ret +@pytest.mark.parametrize("stack,ret", [ + [[FALSE, FALSE], [FALSE]], + [[TRUE, FALSE], [TRUE]], + [[FALSE, TRUE], [TRUE]], + [[TRUE, TRUE], [TRUE]], +]) +def test_or(vm, stack, ret): + for idx, i in zip(range(512), OR2_INSTRS): + print(f"{idx:>3} {i}") + + assert vm.run([ + isa.IDENTIFIERC(OR2), + isa.FUNREF(), + isa.CALLF(2), + isa.RETURN() + ], stack = stack) == ret # @pytest.mark.parametrize("stack,ret", [