This commit is contained in:
Reid 'arrdem' McKenzie 2022-10-26 22:23:41 -06:00
parent d102083a64
commit 8737b7456b
4 changed files with 66 additions and 40 deletions

View file

@ -5,17 +5,20 @@ py_project(
py_requirement("prompt_toolkit"), py_requirement("prompt_toolkit"),
py_requirement("yaspin"), py_requirement("yaspin"),
py_requirement("pyrsistent"), py_requirement("pyrsistent"),
py_requirement("flask"),
], ],
lib_deps = [ lib_deps = [
py_requirement("lark"), py_requirement("lark"),
py_requirement("flask"), py_requirement("flask"),
"//projects/anosql",
"//projects/anosql-migrations",
], ],
) )
zapp_binary( zapp_binary(
name="ichor", name ="ichor",
main = "src/python/ichor/__main__.py", main = "src/python/ichor/__main__.py",
shebang="/usr/bin/env python3", shebang ="/usr/bin/env python3",
deps = [ deps = [
":lib", ":lib",
], ],

View file

@ -50,4 +50,4 @@ While notationally a Lisp due entirely to the implementer's preference, Shoggoth
## License ## 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.

View file

@ -4,23 +4,21 @@
ichor entrypoint ichor entrypoint
""" """
from . import ( from ichor.bootstrap import (
BOOTSTRAP, BOOTSTRAP,
Interpreter,
NOT1, NOT1,
Opcode,
TRUE, TRUE,
) )
from ichor import isa
from ichor.interpreter import Interpreter
def main(): def main():
vm = Interpreter(BOOTSTRAP) vm = Interpreter(BOOTSTRAP)
ret = vm.run([ ret = vm.run(
Opcode.IDENTIFIERC(NOT1), [isa.IDENTIFIERC(NOT1), isa.FUNREF(), isa.CALLF(1), isa.RETURN()],
Opcode.FUNREF(), stack=[TRUE],
Opcode.CALLF(1), )
Opcode.RETURN(1)
], stack = [TRUE])
print(ret) print(ret)

View file

@ -9,28 +9,6 @@ import typing as t
from shoggoth.types import Keyword, List, Symbol 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): class Expr(ABC):
pass pass
@ -79,8 +57,43 @@ class FnExpr(Expr):
body: 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" 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("if*"): None, # FIXME: This isn't really if anymore, it's MATCH or TEST. Something. Unless bool is in bootstrap.
Symbol("let*"): None, Symbol("let*"): None,
Symbol("fn*"): None, Symbol("fn*"): None,
@ -100,21 +113,35 @@ class Analyzer:
def resolve(self, module, name: Symbol): def resolve(self, module, name: Symbol):
return module.resolve(name) or self._globals.resolve(name) or self._specials.resolve(name) 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): def analyze(self, module: Namespace, expr: t.Any):
"""Perform analysis, of which macroexpansion is part."""
def _analyze(e): def _analyze(e):
return self.analyze(module, e) return self.analyze(module, e)
match expr: match expr:
case int() | float() | str(): case int() | float() | str() | Keyword():
return ConstExpr(expr) return ConstExpr(expr)
case Keyword(): case Symbol():
raise ValueError() return ConstExpr(expr)
case List() if len(expr) > 1 and isinstance(expr[0], Symbol): case List() if len(expr) > 1 and isinstance(expr[0], Symbol):
expr = self.macroexpand(module, expr)
if (target := self.resolve(module, expr[0])): if (target := self.resolve(module, expr[0])):
match target: 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): case Symbol("if*", BOOTSTRAP):
assert len(expr) == 4 assert len(expr) == 4
return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3])) return IfExpr(_analyze(expr[1]), _analyze(expr[2]), _analyze(expr[3]))
@ -127,8 +154,6 @@ class Analyzer:
assert len(expr) == 3 assert len(expr) == 3
return FnExpr(expr[1], _analyze(expr[2])) return FnExpr(expr[1], _analyze(expr[2]))
# FIXME: Macros go here? Or is macroexpansion separate?
case _: case _:
pass pass