From 102210bcee2a2949dfebc80e75ba41aaa84ac7b4 Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Thu, 16 Jun 2022 10:55:18 -0600
Subject: [PATCH] Get VARAINT/VTEST implemented

---
 projects/shoggoth/src/python/ichor/impl.py  | 73 ++++++++++++++++-----
 projects/shoggoth/src/python/ichor/state.py | 25 +++++--
 2 files changed, 75 insertions(+), 23 deletions(-)

diff --git a/projects/shoggoth/src/python/ichor/impl.py b/projects/shoggoth/src/python/ichor/impl.py
index 35c9678..d8200e5 100644
--- a/projects/shoggoth/src/python/ichor/impl.py
+++ b/projects/shoggoth/src/python/ichor/impl.py
@@ -13,7 +13,7 @@ from copy import deepcopy
 import typing as t
 
 from ichor.isa import Opcode
-from ichor.state import Closure, FunctionRef, Identifier, Module, Function
+from ichor.state import Closure, FunctionRef, Identifier, Module, Function, Type, TypeRef, VariantRef, Variant
 
 
 def rotate(l):
@@ -120,22 +120,65 @@ class Interpreter(object):
             print("{0}{1: <50} {2}: {3}".format("  " * stackframe.depth, str(stackframe._stack), stackframe._ip, op))
 
             match op:
-                case Opcode.TRUE():
-                    stackframe.push(True)
+                case Opcode.IDENTIFIERC(name):
+                    if not (name in mod.functions or name in mod.types):
+                        _error("IDENTIFIERC references unknown entity")
 
-                case Opcode.FALSE():
-                    stackframe.push(False)
+                    stackframe.push(Identifier(name))
 
-                case Opcode.IF(target):
-                    if len(stackframe) < 1:
+                case Opcode.TYPEREF():
+                    id = stackframe.pop()
+                    if not isinstance(id, Identifier):
+                        _error("TYPEREF consumes an identifier")
+                    if not id.name in mod.types:
+                        _error("TYPEREF must be given a valid type identifier")
+
+                    stackframe.push(TypeRef(id.name))
+
+                case Opcode.VARIANTREF():
+                    id: Identifier = stackframe.pop()
+                    if not isinstance(id, Identifier):
+                        _error("VARIANTREF consumes an identifier and a typeref")
+
+                    t: TypeRef = stackframe.pop()
+                    if not isinstance(id, TypeRef):
+                        _error("VARIANTREF consumes an identifier and a typeref")
+
+                    type = mod.types[t.name]
+                    if id.name not in type.constructors:
+                        _error(f"VARIANTREF given {id.name!r} which does not name a constructor within {type!r}")
+
+                    stackframe.push(VariantRef(t, id.name))
+
+                case Opcode.VARIANT(n):
+                    armref: VariantRef = stackframe.pop()
+                    if not isinstance(armref, VariantRef):
+                        _error("VARIANT must be given a valid constructor reference")
+
+                    ctor = mod.types[armref.type.name].constructors[armref.arm]
+                    if n != len(ctor):
+                        _error("VARIANT given n-args inconsistent with the type constructor")
+
+                    if n > len(stackframe):
                         _error("Stack size violation")
 
-                    val = stackframe.pop()
-                    if val not in [True, False]:
-                        _error("Type violation")
+                    # FIXME: Where does type variable to type binding occur?
+                    # Certainly needs to be AT LEAST here, where we also need to be doing some typechecking
+                    v = Variant(armref.type.name, armref.arm, tuple(stackframe._stack[:n]))
+                    stackframe.drop(n)
+                    stackframe.push(v)
 
-                    if val is False:
-                        stackframe.goto(target)
+                case Opcode.VTEST(n):
+                    armref: VariantRef = stackframe.pop()
+                    if not isinstance(armref, VariantRef):
+                        _error("VTEST must be given a variant reference")
+
+                    inst: Variant = stackframe.pop()
+                    if not isinstance(inst, Variant):
+                        _error("VTEST must be given an instance of a variant")
+
+                    if inst.type == armref.type.name and inst.variant == armref.arm:
+                        stackframe.goto(n)
                         continue
 
                 case Opcode.GOTO(n):
@@ -169,12 +212,6 @@ class Interpreter(object):
                         _error("SLOT reference out of range")
                     stackframe.slot(n)
 
-                case Opcode.IDENTIFIERC(name):
-                    if not (name in mod.functions or name in mod.types):
-                        _error("IDENTIFIERC references unknown entity")
-
-                    stackframe.push(Identifier(name))
-
                 case Opcode.FUNREF():
                     id = stackframe.pop()
                     if not isinstance(id, Identifier):
diff --git a/projects/shoggoth/src/python/ichor/state.py b/projects/shoggoth/src/python/ichor/state.py
index e5699c6..a369e28 100644
--- a/projects/shoggoth/src/python/ichor/state.py
+++ b/projects/shoggoth/src/python/ichor/state.py
@@ -95,7 +95,7 @@ class Function(t.NamedTuple):
         return f"{self.name};{len(self.arguments)};{len(self.returns)}"
 
 
-class VarT(FuncT):
+class TypeT(FuncT):
     @v_args(inline=True)
     def var(self, constraints, name, arms):
         return (constraints, name, arms)
@@ -119,29 +119,44 @@ class VarT(FuncT):
 
 
 
-VAR = Lark(GRAMMAR, start="var", parser='lalr', transformer=VarT())
+TYPE = Lark(GRAMMAR, start="var", parser='lalr', transformer=TypeT())
+
+
+class TypeRef(t.NamedTuple):
+    name: str
+
+
+class VariantRef(t.NamedTuple):
+    type: TypeRef
+    arm: str
 
 
 class Type(t.NamedTuple):
     name: str
-    constructors: t.List[t.Tuple[str, t.List[str]]]
+    constructors: t.Dict[str, t.List[str]]
     typevars: t.List[t.Any] = []
     typeconstraints: t.List[t.Any] = []
     metadata: dict = {}
 
     @classmethod
     def build(cls, name: str):
-        constraints, name, arms = VAR.parse(name)
+        constraints, name, arms = TYPE.parse(name)
         # FIXME: Constraints probably needs some massaging
         # FIXME: Need to get typevars from somewhere
         # They both probably live in the same list
-        return cls(name, arms, typeconstraints=constraints)
+        return cls(name, dict(arms), typeconstraints=constraints)
 
     @property
     def signature(self):
         return self.name
 
 
+class Variant(t.NamedTuple):
+    type: str
+    variant: str
+    fields: t.Tuple[t.Any]
+
+
 class Closure(t.NamedTuple):
     # Note that a closure over a closure is equivalent to a single closure which extends the captured stack fragment, so
     # there's no need for a union here as we can simply convert nested closures.