Shuffling a bunch of stuff around

This commit is contained in:
Reid 'arrdem' McKenzie 2022-06-13 20:23:43 -06:00
parent bc7cb3e909
commit 8feb3f3989
5 changed files with 63 additions and 140 deletions

View file

@ -1,5 +1,12 @@
# noqa
#!/usr/bin/env python
from .bootstrap import * # noqa
from .impl import * # noqa
from .isa import * # noqa
"""
Import everything out of the Ichor modules.
FIXME: A real public API here would be great, but somewhat un-pythonic.
"""
from ichor.bootstrap import * # noqa
from ichor.impl import * # noqa
from ichor.isa import * # noqa
from ichor.typing import * # noqa

View file

@ -4,8 +4,8 @@ Some utterly trivial functions and types that allow me to begin testing the VM.
Hopefully no "real" interpreter ever uses this code, since it's obviously replaceable.
"""
from .isa import Module, Opcode
from .typing import ProductExpr, SumExpr
from ichor.isa import Opcode
from ichor.state import Module
BOOTSTRAP = Module()

View file

@ -9,16 +9,10 @@ context (a virtual machine) which DOES have an easily introspected and serialize
"""
import sys
from ichor.typing import Identifier
assert sys.version_info > (3, 10, 0), "`match` support is required"
from copy import deepcopy
from .isa import Closure, FunctionRef, Opcode, Identifier
from ichor.isa import Opcode
from ichor.typing import Closure, FunctionRef, Identifier
def rotate(l):
@ -39,7 +33,7 @@ class Stackframe(object):
def pop(self):
return self.stack.pop(0)
def call(self, signature: FunctionRef, ip):
def call(self, signature: FunctionRef, ip) -> "Stackframe":
self.ip += 1
nargs = len(signature.args)
args, self.stack = self.stack[:nargs], self.stack[nargs:]
@ -51,7 +45,7 @@ class Stackframe(object):
depth=self.depth+1
)
def ret(self, nargs):
def ret(self, nargs) -> "Stackframe":
self.parent.stack = self.stack[:nargs] + self.parent.stack
return self.parent
@ -88,95 +82,95 @@ class Interpreter(object):
def run(self, opcodes, stack=[]):
"""Directly interpret some opcodes in the configured environment."""
stack = Stackframe(stack=stack)
stackframe = Stackframe(stack=stack)
mod = self.bootstrap.copy()
stack.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
stackframe.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
print(mod)
def _error(msg=None):
# Note this is pretty expensive because we have to snapshot the stack BEFORE we do anything
# And the stack object isn't immutable or otherwise designed for cheap snapshotting
raise InterpreterError(mod, deepcopy(stack), msg)
raise InterpreterError(mod, deepcopy(stackframe), msg)
while True:
op = mod.opcodes[stack.ip]
print("{0}{1: <50} {2}: {3}".format(" " * stack.depth, str(stack.stack), stack.ip, op))
op = mod.opcodes[stackframe.ip]
print("{0}{1: <50} {2}: {3}".format(" " * stackframe.depth, str(stackframe.stack), stackframe.ip, op))
match op:
case Opcode.TRUE():
stack.push(True)
stackframe.push(True)
case Opcode.FALSE():
stack.push(False)
stackframe.push(False)
case Opcode.IF(target):
if len(stack) < 1:
if len(stackframe) < 1:
_error("Stack size violation")
val = stack.pop()
val = stackframe.pop()
if val not in [True, False]:
_error("Type violation")
if val is False:
stack.ip = target
stackframe.ip = target
continue
case Opcode.GOTO(n):
if (n < 0):
_error("Illegal branch target")
stack.ip = n
stackframe.ip = n
continue
case Opcode.DUP(n):
if (n > len(stack)):
if (n > len(stackframe)):
_error("Stack size violation")
stack.dup(n)
stackframe.dup(n)
case Opcode.ROT(n):
if (n > len(stack)):
if (n > len(stackframe)):
_error("Stack size violation")
stack.rot(n)
stackframe.rot(n)
case Opcode.DROP(n):
if (n > len(stack)):
if (n > len(stackframe)):
_error("Stack size violation")
stack.drop(n)
stackframe.drop(n)
case Opcode.SLOT(n):
if (n < 0):
_error("SLOT must have a positive reference")
if (n > len(stack.stack) - 1):
if (n > len(stackframe.stack) - 1):
_error("SLOT reference out of range")
stack.push(stack.stack[len(stack) - n - 1])
stackframe.push(stackframe.stack[len(stackframe) - n - 1])
case Opcode.IDENTIFIERC(name):
if not (name in mod.functions or name in mod.types):
_error("IDENTIFIERC references unknown entity")
stack.push(Identifier(name))
stackframe.push(Identifier(name))
case Opcode.FUNREF():
id = stack.pop()
id = stackframe.pop()
if not isinstance(id, Identifier):
_error("FUNREF consumes an IDENTIFIER")
try:
# FIXME: Verify this statically
stack.push(FunctionRef.parse(id.name))
stackframe.push(FunctionRef.parse(id.name))
except:
_error("Invalid function ref")
case Opcode.CALLF(n):
sig = stack.pop()
sig = stackframe.pop()
if not isinstance(sig, FunctionRef):
_error("CALLF requires a funref at top of stack")
if n != len(sig.args):
_error("CALLF target violation; argument count missmatch")
if n > len(stack):
if n > len(stackframe):
_error("Stack size violation")
try:
@ -184,69 +178,69 @@ class Interpreter(object):
except KeyError:
_error("Unknown target")
stack = stack.call(sig, ip)
stackframe = stackframe.call(sig, ip)
continue
case Opcode.RETURN(n):
if (n > len(stack)):
if (n > len(stackframe)):
_error("Stack size violation")
if stack.depth == 0:
return stack[:n]
if stackframe.depth == 0:
return stackframe[:n]
sig = FunctionRef.parse(stack.name)
sig = FunctionRef.parse(stackframe.name)
if (len(sig.ret) != n):
_error("Signature violation")
stack = stack.ret(n)
stackframe = stackframe.ret(n)
continue
case Opcode.CLOSUREF(n):
sig = stack.pop()
sig = stackframe.pop()
if not isinstance(sig, FunctionRef):
_error("CLOSUREF requires a funref at top of stack")
if not n <= len(sig.args):
_error("CLOSUREF target violation; too many parameters provided")
if n > len(stack):
if n > len(stackframe):
_error("Stack size violation")
c = Closure(
sig,
stack.stack[:n]
stackframe.stack[:n]
)
stack.drop(n)
stack.push(c)
stackframe.drop(n)
stackframe.push(c)
case Opcode.CLOSUREC(n):
c = stack.pop()
c = stackframe.pop()
if not isinstance(c, Closure):
_error("CLOSUREC requires a closure at top of stack")
if n + len(c.frag) > len(c.funref.args):
_error("CLOSUREC target violation; too many parameters provided")
if n > len(stack):
if n > len(stackframe):
_error("Stack size violation")
c = Closure(
c.funref,
stack.stack[:n] + c.frag
stackframe.stack[:n] + c.frag
)
stack.drop(n)
stack.push(c)
stackframe.drop(n)
stackframe.push(c)
case Opcode.CALLC(n):
c = stack.pop()
c = stackframe.pop()
if not isinstance(c, Closure):
_error("CALLC requires a closure at top of stack")
if n + len(c.frag) != len(c.funref.args):
_error("CALLC target vionation; argument count missmatch")
if n > len(stack):
if n > len(stackframe):
_error("Stack size violation")
# Extract the function signature
sig = c.funref
# Push the closure's stack fragment
stack.stack = c.frag + stack.stack
stackframe.stack = c.frag + stackframe.stack
# Perform a "normal" funref call
try:
@ -254,28 +248,10 @@ class Interpreter(object):
except KeyError:
_error("Unknown target")
stack = stack.call(sig, ip)
stackframe = stackframe.call(sig, ip)
continue
case Opcode.STRUCT(structref, n):
if not (t := module.types.get(structref)):
_error(f"STRUCT must reference a known type, {structref!r} is undefined")
if n > len(stack):
_error("Stack size violation")
args = stack[:n]
stack.drop(n)
# FIXME: typecheck
for arg, ftype in zip(args, t.children):
pass
inst = Struct(structref, [], dict(zip(t.field_names, args)))
stack.push(inst)
case _:
raise Exception(f"Unhandled interpreter state {op}")
stack.ip += 1
stackframe.ip += 1

View file

@ -3,8 +3,6 @@
import typing as t
from .typing import *
class Opcode:
####################################################################################################
@ -231,61 +229,3 @@ class Opcode:
class BREAK(t.NamedTuple):
"""Abort the interpreter."""
pass
class Module(t.NamedTuple):
opcodes: list = []
functions: dict = {}
types: dict = {}
constants: dict = {}
def copy(self):
return Module(
self.opcodes.copy(),
self.functions.copy(),
self.types.copy(),
self.constants.copy(),
)
@staticmethod
def translate(start: int, end: int, i: "Opcode"):
# FIXME: Consolidate bounds checks somehow
match i:
case Opcode.IF(t):
d = t + start
assert start <= d < end
return Opcode.IF(d)
case Opcode.GOTO(t):
d = t + start
assert start <= d < end
return Opcode.GOTO(d)
case _:
return i
def define_function(self, name, opcodes):
try:
sig = FunctionRef.parse(name)
assert sig.name
except:
raise ValueError("Illegal name provided")
start = len(self.opcodes)
self.functions[name] = start
for op in opcodes:
self.opcodes.append(self.translate(start, start + len(opcodes), op))
return name
def define_type(self, name, signature):
self.types[name] = signature
return name
def __str__(self):
b = []
marks = {v: k for k, v in self.functions.items()}
for i, o in zip(range(1<<64), self.opcodes):
if(i in marks):
b.append(f"{marks[i]}:")
b.append(f"{i: >10}: {o}")
return "\n".join(b)

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3
from ichor import *
from ichor import Interpreter, BOOTSTRAP
import pytest