[NO TESTS] WIP

This commit is contained in:
Reid 'arrdem' McKenzie 2023-05-10 22:42:17 -06:00
parent bcd50fe57f
commit 0fbb6e4f88
3 changed files with 63 additions and 15 deletions

View file

@ -3,7 +3,7 @@
from importlib.resources import files from importlib.resources import files
from attrs import define, field from attrs import define
from lark import Lark, Tree, Token, Transformer, v_args from lark import Lark, Tree, Token, Transformer, v_args
@ -11,7 +11,7 @@ with files(__package__).joinpath("grammar.lark").open("r", encoding="utf-8") as
GRAMMAR = fp.read() GRAMMAR = fp.read()
@define @define(frozen=True)
class Symbol: class Symbol:
name: str name: str

View file

@ -8,6 +8,8 @@ to implement a flat, stackless, serializable interpreter.
import typing as t import typing as t
from milkshake import Symbol
from .models import * from .models import *
from .util import * from .util import *
@ -18,24 +20,24 @@ class Operator:
"""Stack operators.""" """Stack operators."""
class Task: class Op:
@attrs.define() @attrs.define()
class Complete: class Complete:
"""Signals program termination.""" """Signals program termination."""
pass @attrs.define()
class Drop:
"""Drop or void the top of the data stack."""
@attrs.define() @attrs.define()
class Eval: class Eval:
"""Evaluate the current term.""" """Evaluate the current term."""
pass
@attrs.define() @attrs.define()
class Apply: class Invoke:
"""Apply an operator to the stack.""" """Consuming the top N items of the stack of the format [... <fn>, <... args>] invoke fn with args."""
operator: "Operator" = attrs.field() n: int
@attrs.define() @attrs.define()
class Next: class Next:
@ -67,19 +69,64 @@ def step(vm: Vm, cont: Cont, frame: Frame):
""" """
if frame.op_stack:
expr = frame.op_stack.pop()
else:
expr = get_in(vm.log, frame.pc)
match expr: match expr:
case ["native", native_fn, arg_exprs, vararg_expr]: # AST entries
case ["py", native_expr, arg_exprs, vararg_expr]:
pass pass
case ["invoke", fn_expr, arg_exprs, vararg_expr]: case ["lambda", arg_expr, body_expr]:
# "now this one;s hard"
pass pass
case ["var", name]: case ["do", *body_exprs]:
pass pass
case int() | float() | str() | bool(): case ["if", test_expr, then_expr, else_expr]:
pass pass
case ["if", test_expr, then_expr]:
pass
# Generalized "simple" invoke expression
case [fn_expr, *arg_exprs]:
pass
# Literals/terminals
case Symbol(name):
# Resolve a value from the scope(s)
for frame in reversed(cont.frames):
if name in frame.frame_ns:
frame.data_stack.append(frame.frame_ns[name])
break
else:
raise NameError(f"{name!r} is not bound")
case int() | float() | str() | bool() | dict() | set():
# Literals push
frame.data_stack.append(expr)
# Semi-VM instructions
case Op.Drop():
frame.data_stack.pop()
case Op.Invoke(n):
data = [frame.data_stack.pop() for _ in range(n)]
fn = data[-1]
args = data[-1::]
# Need to trampoline the call through the wrapping loop
return Op.Frame(fn, args)
# FIXME: Do we still need this?
case Op.Complete():
return expr
def run(program, state=None): def run(program, state=None):
vm = state or Vm([], {}, []) vm = state or Vm([], {}, [])

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from collections import deque
import typing as t import typing as t
import attrs import attrs
@ -40,5 +41,5 @@ class Frame:
pc: list = attrs.field(factory=lambda: [0]) pc: list = attrs.field(factory=lambda: [0])
frame_ns: dict = attrs.field(factory=dict) frame_ns: dict = attrs.field(factory=dict)
op_stack: list = attrs.field(factory=list) op_stack: deque = attrs.field(factory=deque)
data_stack: list = attrs.field(factory=list) data_stack: deque = attrs.field(factory=deque)