[NO TESTS] WIP
This commit is contained in:
parent
bcd50fe57f
commit
0fbb6e4f88
3 changed files with 63 additions and 15 deletions
|
@ -3,7 +3,7 @@
|
|||
|
||||
from importlib.resources import files
|
||||
|
||||
from attrs import define, field
|
||||
from attrs import define
|
||||
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()
|
||||
|
||||
|
||||
@define
|
||||
@define(frozen=True)
|
||||
class Symbol:
|
||||
name: str
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ to implement a flat, stackless, serializable interpreter.
|
|||
|
||||
import typing as t
|
||||
|
||||
from milkshake import Symbol
|
||||
|
||||
from .models import *
|
||||
from .util import *
|
||||
|
||||
|
@ -18,24 +20,24 @@ class Operator:
|
|||
"""Stack operators."""
|
||||
|
||||
|
||||
class Task:
|
||||
class Op:
|
||||
@attrs.define()
|
||||
class Complete:
|
||||
"""Signals program termination."""
|
||||
|
||||
pass
|
||||
@attrs.define()
|
||||
class Drop:
|
||||
"""Drop or void the top of the data stack."""
|
||||
|
||||
@attrs.define()
|
||||
class Eval:
|
||||
"""Evaluate the current term."""
|
||||
|
||||
pass
|
||||
|
||||
@attrs.define()
|
||||
class Apply:
|
||||
"""Apply an operator to the stack."""
|
||||
class Invoke:
|
||||
"""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()
|
||||
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:
|
||||
case ["native", native_fn, arg_exprs, vararg_expr]:
|
||||
# AST entries
|
||||
case ["py", native_expr, arg_exprs, vararg_expr]:
|
||||
pass
|
||||
|
||||
case ["invoke", fn_expr, arg_exprs, vararg_expr]:
|
||||
case ["lambda", arg_expr, body_expr]:
|
||||
# "now this one;s hard"
|
||||
pass
|
||||
|
||||
case ["var", name]:
|
||||
case ["do", *body_exprs]:
|
||||
pass
|
||||
|
||||
case int() | float() | str() | bool():
|
||||
case ["if", test_expr, then_expr, else_expr]:
|
||||
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):
|
||||
vm = state or Vm([], {}, [])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from collections import deque
|
||||
import typing as t
|
||||
|
||||
import attrs
|
||||
|
@ -40,5 +41,5 @@ class Frame:
|
|||
|
||||
pc: list = attrs.field(factory=lambda: [0])
|
||||
frame_ns: dict = attrs.field(factory=dict)
|
||||
op_stack: list = attrs.field(factory=list)
|
||||
data_stack: list = attrs.field(factory=list)
|
||||
op_stack: deque = attrs.field(factory=deque)
|
||||
data_stack: deque = attrs.field(factory=deque)
|
||||
|
|
Loading…
Reference in a new issue