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