From 50f4a4cdb12c703e407bcb743fbc5807fb49d762 Mon Sep 17 00:00:00 2001
From: "Reid D. 'arrdem' McKenzie" <me@arrdem.com>
Date: Tue, 7 Jun 2022 10:32:15 -0600
Subject: [PATCH] Tapping towards structs and variants

---
 projects/shoggoth/src/python/ichor/impl.py   | 19 ++++-
 projects/shoggoth/src/python/ichor/isa.py    | 78 ++++++++++++++++++--
 projects/shoggoth/src/python/ichor/typing.py | 21 ++++--
 3 files changed, 107 insertions(+), 11 deletions(-)

diff --git a/projects/shoggoth/src/python/ichor/impl.py b/projects/shoggoth/src/python/ichor/impl.py
index 1da2a5a..ac57eb9 100644
--- a/projects/shoggoth/src/python/ichor/impl.py
+++ b/projects/shoggoth/src/python/ichor/impl.py
@@ -236,7 +236,7 @@ class Interpreter(object):
                     if n + len(c.frag) != len(c.funref.args):
                         _error("CALLC target vionation; argument count missmatch")
                     if n > len(stack):
-                        _error("Stack size vionation")
+                        _error("Stack size violation")
 
                     # Extract the function signature
                     sig = c.funref
@@ -253,6 +253,23 @@ class Interpreter(object):
                     stack = stack.call(sig, ip)
                     continue
 
+                case Opcode.STRUCT(structref, n):
+                    if not (t := module.types.get(structref)):
+                        _error(f"STRUCT must reference a known type, {structref!r} is undefined")
+                    if n > len(stack):
+                        _error("Stack size violation")
+
+                    args = stack[:n]
+                    stack.drop(n)
+
+                    # FIXME: typecheck
+                    for arg, ftype in zip(args, t.children):
+                        pass
+
+                    inst = Struct(structref, [], dict(zip(t.field_names, args)))
+
+                    stack.push(inst)
+
 
                 case _:
                     raise Exception(f"Unhandled interpreter state {op}")
diff --git a/projects/shoggoth/src/python/ichor/isa.py b/projects/shoggoth/src/python/ichor/isa.py
index 2acb7c2..a980740 100644
--- a/projects/shoggoth/src/python/ichor/isa.py
+++ b/projects/shoggoth/src/python/ichor/isa.py
@@ -175,19 +175,58 @@ class Opcode:
     ####################################################################################################
     # Structures
     ####################################################################################################
+
+    # FIXME: This lacks any sort of way to do dynamic field references
+
+    class IDENTIFIERC(t.NamedTuple):
+        """() -> (CONST)
+
+        An inline constant which produces an identifier to the stack.
+
+        Identifiers name functions, fields and types but are not strings.
+        They are a VM-internal naming structure with reference to the module.
+
+        """
+
+        val: str
+
+    class TYPEREF(t.NamedTuple):
+        """(IDENTIFIER) -> (TYPEREF)
+
+        Produces a TYPEREF to the type named by the provided IDENTIFIER.
+
+        """
+
+    class FIELDREF(t.NamedTuple):
+        """(IDENTIFIER, TYPEREF) -> (FIELDREF)
+
+
+        Produces a FIELDREF to the field named by the provided IDENTIFIER.
+        The FIELDREF must be within and with reference to a sum type.
+
+        """
+
+    class VARIANTREF(t.NamedTuple):
+        """(IDENTIFIER, TYPEREF) -> (VARIANTREF)
+
+        Produce a VARIANTREF to an 'arm' of the given variant type.
+
+        """
+
     class STRUCT(t.NamedTuple):
-        """(*) -> (T)
+        """(STRUCTREF<S>, ...) -> (S)
 
         Consume the top N items of the stack, producing a struct of the type `structref`.
 
         The name and module path of the current function MUST match the name and module path of `structref`.
+        The arity of this opcode MUST match the arity of the struct.
+        The signature of the struct MUST match the signature fo the top N of the stack.
         """
 
-        structref: str
-        nargs: int
+        nargs: int = 0
 
     class FLOAD(t.NamedTuple):
-        """(A) -> (B)
+        """(FIELDREF<f ⊢ T ∈ S>, S) -> (T)
 
         Consume the struct reference at the top of the stack, producing the value of the referenced field.
 
@@ -196,7 +235,7 @@ class Opcode:
         fieldref: str
 
     class FSTORE(t.NamedTuple):
-        """(A, B) -> (A)
+        """(FIELDREF<f ⊢ T ∈ S>, S, T) -> (S)
 
         Consume the struct reference at the top of the stack and a value, producing a new copy of the struct in which
         that field has been updated to the new value.
@@ -205,6 +244,35 @@ class Opcode:
 
         fieldref: str
 
+    class VARIANT(t.NamedTuple):
+        """(VARIANTREF<a ⊢ A ⊂ B>, ...) -> (B)
+
+        Construct an instance of an 'arm' of a variant.
+        The type of the 'arm' is considered to be the type of the whole variant.
+
+        The name and module path of the current function MUST match the name and module path of `VARIANTREF`.
+        The arity of this opcode MUST match the arity of the arm.
+        The signature of the arm MUST match the signature fo the top N of the stack.
+        """
+
+        nargs: int = 0
+
+    class VTEST(t.NamedTuple):
+        """(VARIANTREF<a ⊢ A ⊂ B>, B) -> (bool)
+
+        Test whether B is a given arm of a variant A .
+
+        """
+
+    class VLOAD(t.NamedTuple):
+        """(VARIANTREF<a ⊢ A ⊂ B>, B) -> (A)
+
+        Load the value of the variant arm.
+        VLOAD errors (undefined) if B is not within the variant.
+        VLOAD errors (undefined) if the value in B is not an A - use VTEST as needed.
+
+        """
+
     ####################################################################################################
     # Arrays
     ####################################################################################################
diff --git a/projects/shoggoth/src/python/ichor/typing.py b/projects/shoggoth/src/python/ichor/typing.py
index 0ccddf2..fe8ac89 100644
--- a/projects/shoggoth/src/python/ichor/typing.py
+++ b/projects/shoggoth/src/python/ichor/typing.py
@@ -22,6 +22,14 @@ class ProductExpr(t.NamedTuple):
     children: t.Mapping[str, t.Any]
 
 
+# FIXME: How exactly
+class StructExpr(t.NamedTuple):
+    name: str
+    type_params: list
+    field_names: t.List[str]
+    children: t.List[t.Any]
+
+
 ####################################################################################################
 ####################################################################################################
 ####################################################################################################
@@ -50,8 +58,10 @@ class FunctionRef(t.NamedTuple):
 
 
 class Closure(t.NamedTuple):
-    target: t.Union["Closure", FunctionRef]
-    args: t.List[t.Any]
+    # 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.
+    funref: FunctionRef
+    frag: t.List[t.Any]
 
 
 class FunctionSignature(t.NamedTuple):
@@ -77,6 +87,7 @@ class FunctionSignature(t.NamedTuple):
         )
 
 
-class Closure(t.NamedTuple):
-    funref: FunctionRef
-    frag: t.List[t.Any]
+class Struct(t.NamedTuple):
+    name: str
+    type_params: list
+    children: t.Mapping[str, t.Any]