From 2c92447947ceac9cdb4392bd97909030e379597b Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Wed, 17 Aug 2022 00:07:09 -0600
Subject: [PATCH] Get or2 working again via the assembler

---
 .../shoggoth/src/python/ichor/assembler.py    | 28 ++++++-
 .../shoggoth/src/python/ichor/bootstrap.py    | 81 ++++++++++++++-----
 .../shoggoth/src/python/ichor/interpreter.py  |  4 +-
 .../shoggoth/test/python/ichor/fixtures.py    |  5 +-
 .../test/python/ichor/test_bootstrap.py       | 37 +++++----
 5 files changed, 115 insertions(+), 40 deletions(-)

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", [