From 8737b7456bbf0cd057b3345ed69588c802a2b9a1 Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Wed, 26 Oct 2022 22:23:41 -0600
Subject: [PATCH] WIP

---
 projects/shoggoth/BUILD                       |  7 +-
 projects/shoggoth/README.md                   |  2 +-
 .../shoggoth/src/python/ichor/__main__.py     | 16 ++--
 .../shoggoth/src/python/shoggoth/analyzer.py  | 81 ++++++++++++-------
 4 files changed, 66 insertions(+), 40 deletions(-)

diff --git a/projects/shoggoth/BUILD b/projects/shoggoth/BUILD
index 22b3cf9..563d7bb 100644
--- a/projects/shoggoth/BUILD
+++ b/projects/shoggoth/BUILD
@@ -5,17 +5,20 @@ py_project(
         py_requirement("prompt_toolkit"),
         py_requirement("yaspin"),
         py_requirement("pyrsistent"),
+        py_requirement("flask"),
     ],
     lib_deps = [
         py_requirement("lark"),
         py_requirement("flask"),
+        "//projects/anosql",
+        "//projects/anosql-migrations",
     ],
 )
 
 zapp_binary(
-    name="ichor",
+    name ="ichor",
     main = "src/python/ichor/__main__.py",
-    shebang="/usr/bin/env python3",
+    shebang ="/usr/bin/env python3",
     deps = [
         ":lib",
     ],
diff --git a/projects/shoggoth/README.md b/projects/shoggoth/README.md
index 2a1d082..c03b7c4 100644
--- a/projects/shoggoth/README.md
+++ b/projects/shoggoth/README.md
@@ -50,4 +50,4 @@ While notationally a Lisp due entirely to the implementer's preference, Shoggoth
 
 ## License
 
-While the source for this project happens to be published no grant of rights is made.
+While the source for this project happens to be published, no grant of rights or right to usage is made.
diff --git a/projects/shoggoth/src/python/ichor/__main__.py b/projects/shoggoth/src/python/ichor/__main__.py
index f74cd07..db49042 100644
--- a/projects/shoggoth/src/python/ichor/__main__.py
+++ b/projects/shoggoth/src/python/ichor/__main__.py
@@ -4,23 +4,21 @@
 ichor entrypoint
 """
 
-from . import (
+from ichor.bootstrap import (
     BOOTSTRAP,
-    Interpreter,
     NOT1,
-    Opcode,
     TRUE,
 )
+from ichor import isa
+from ichor.interpreter import Interpreter
 
 
 def main():
     vm = Interpreter(BOOTSTRAP)
-    ret = vm.run([
-        Opcode.IDENTIFIERC(NOT1),
-        Opcode.FUNREF(),
-        Opcode.CALLF(1),
-        Opcode.RETURN(1)
-    ], stack = [TRUE])
+    ret = vm.run(
+        [isa.IDENTIFIERC(NOT1), isa.FUNREF(), isa.CALLF(1), isa.RETURN()],
+        stack=[TRUE],
+    )
     print(ret)
 
 
diff --git a/projects/shoggoth/src/python/shoggoth/analyzer.py b/projects/shoggoth/src/python/shoggoth/analyzer.py
index 11e660e..d11a985 100644
--- a/projects/shoggoth/src/python/shoggoth/analyzer.py
+++ b/projects/shoggoth/src/python/shoggoth/analyzer.py
@@ -9,28 +9,6 @@ import typing as t
 from shoggoth.types import Keyword, List, Symbol
 
 
-@dataclass
-class Namespace:
-    name: Symbol
-    mappings: t.Mapping[Symbol, t.Any]
-
-    def resolve(self, name: Symbol) -> t.Optional[Symbol]:
-        if name in self.mappings:
-            return name.qualify(self.name.name)
-
-    def __contains__(self, item):
-        return item.namespace == self.name and item.unqualified() in self.mappings
-
-    def __getitem__(self, key: Symbol):
-        return self.mappings[key.unqualified()]
-
-    def get(self, key: Symbol, default=None):
-        if key in self:
-            return self[key]
-        else:
-            return default
-
-
 class Expr(ABC):
     pass
 
@@ -79,8 +57,43 @@ class FnExpr(Expr):
     body: Expr
 
 
+# Related reading -
+# - http://www-formal.stanford.edu/jmc/elephant/elephant.html
+# - https://www.cs.utah.edu/plt/scope-sets/
+#
+# FIXME: Do I make the multiverse directly durable? Or does that live elsewhere?
+@dataclass
+class Multiverse:
+    """A namespace with branches.
+
+    We can think about a universe or multiverse of program states as existing within histories of names and evaluations.
+
+    When we evaluate a form, four things can happen:
+    - We can do program effects (I/O, generate heat, consume time)
+    - Produce a value (returned or raised/thrown error)
+    - Produce definition(s) with which other evaluation will occur
+    - Produce context with which other evaluation will occur
+
+    In a sense, there exists a 'multiverse' of naming into which all top level values are entered.
+
+    Each alteration to the 'multiverse' produces a new 'universe' of names - you could call this a fork or session -
+    which maps a name in that universe to a value in the multiverse.
+
+    A universe is a sequential or causal history.
+    Traveling backwards in time and deviating produces a different universe.
+    Truncating or discarding that history produces a different although in some sense equivalent universe.
+
+    """
+
+    soup: t.Dict[]
+
+    @dataclass
+    class Namespace:
+        pass
+
+
 BOOTSTRAP = "lang.shoggoth.v0.bootstrap"
-SPECIALS = Namespace(Symbol(BOOTSTRAP), {
+SPECIALS = Multiverse.Namespace(Symbol(BOOTSTRAP), {
     Symbol("if*"): None, # FIXME: This isn't really if anymore, it's MATCH or TEST. Something. Unless bool is in bootstrap.
     Symbol("let*"): None,
     Symbol("fn*"): None,
@@ -100,21 +113,35 @@ class Analyzer:
     def resolve(self, module, name: Symbol):
         return module.resolve(name) or self._globals.resolve(name) or self._specials.resolve(name)
 
+    def macroexpand1(self, module, form):
+        """macroexpand¹"""
+
+    def macroexpand(self, module, form):
+        """Perform macroexpansion."""
+
     def analyze(self, module: Namespace, expr: t.Any):
+        """Perform analysis, of which macroexpansion is part."""
 
         def _analyze(e):
             return self.analyze(module, e)
 
         match expr:
-            case int() | float() | str():
+            case int() | float() | str() | Keyword():
                 return ConstExpr(expr)
 
-            case Keyword():
-                raise ValueError()
+            case Symbol():
+                return ConstExpr(expr)
 
             case List() if len(expr) > 1 and isinstance(expr[0], Symbol):
+                expr = self.macroexpand(module, expr)
                 if (target := self.resolve(module, expr[0])):
                     match target:
+                        # FIXME: Macros go here? Or is macroexpansion separate?
+                        # Macroexpansion needs to go here
+
+                        # in-ns or equivalent needs to go here
+                        # ns / module needs to go here
+
                         case Symbol("if*", BOOTSTRAP):
                             assert len(expr) == 4
                             return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3]))
@@ -127,8 +154,6 @@ class Analyzer:
                             assert len(expr) == 3
                             return FnExpr(expr[1], _analyze(expr[2]))
 
-                        # FIXME: Macros go here? Or is macroexpansion separate?
-
                         case _:
                             pass