WIP
This commit is contained in:
parent
7fe362c968
commit
dc888b5958
4 changed files with 66 additions and 40 deletions
|
@ -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",
|
||||||
],
|
],
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue