Shuffling a bunch of stuff around
This commit is contained in:
parent
6d96a4491f
commit
c59034383c
5 changed files with 63 additions and 140 deletions
|
@ -1,5 +1,12 @@
|
||||||
# noqa
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from .bootstrap import * # noqa
|
"""
|
||||||
from .impl import * # noqa
|
Import everything out of the Ichor modules.
|
||||||
from .isa import * # noqa
|
|
||||||
|
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
|
||||||
|
|
|
@ -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.
|
Hopefully no "real" interpreter ever uses this code, since it's obviously replaceable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .isa import Module, Opcode
|
from ichor.isa import Opcode
|
||||||
from .typing import ProductExpr, SumExpr
|
from ichor.state import Module
|
||||||
|
|
||||||
|
|
||||||
BOOTSTRAP = Module()
|
BOOTSTRAP = Module()
|
||||||
|
|
|
@ -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 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):
|
def rotate(l):
|
||||||
|
@ -39,7 +33,7 @@ class Stackframe(object):
|
||||||
def pop(self):
|
def pop(self):
|
||||||
return self.stack.pop(0)
|
return self.stack.pop(0)
|
||||||
|
|
||||||
def call(self, signature: FunctionRef, ip):
|
def call(self, signature: FunctionRef, ip) -> "Stackframe":
|
||||||
self.ip += 1
|
self.ip += 1
|
||||||
nargs = len(signature.args)
|
nargs = len(signature.args)
|
||||||
args, self.stack = self.stack[:nargs], self.stack[nargs:]
|
args, self.stack = self.stack[:nargs], self.stack[nargs:]
|
||||||
|
@ -51,7 +45,7 @@ class Stackframe(object):
|
||||||
depth=self.depth+1
|
depth=self.depth+1
|
||||||
)
|
)
|
||||||
|
|
||||||
def ret(self, nargs):
|
def ret(self, nargs) -> "Stackframe":
|
||||||
self.parent.stack = self.stack[:nargs] + self.parent.stack
|
self.parent.stack = self.stack[:nargs] + self.parent.stack
|
||||||
return self.parent
|
return self.parent
|
||||||
|
|
||||||
|
@ -88,95 +82,95 @@ class Interpreter(object):
|
||||||
def run(self, opcodes, stack=[]):
|
def run(self, opcodes, stack=[]):
|
||||||
"""Directly interpret some opcodes in the configured environment."""
|
"""Directly interpret some opcodes in the configured environment."""
|
||||||
|
|
||||||
stack = Stackframe(stack=stack)
|
stackframe = Stackframe(stack=stack)
|
||||||
mod = self.bootstrap.copy()
|
mod = self.bootstrap.copy()
|
||||||
stack.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
|
stackframe.ip = mod.functions[mod.define_function(";<main>;;", opcodes)]
|
||||||
|
|
||||||
print(mod)
|
print(mod)
|
||||||
|
|
||||||
def _error(msg=None):
|
def _error(msg=None):
|
||||||
# Note this is pretty expensive because we have to snapshot the stack BEFORE we do anything
|
# 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
|
# 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:
|
while True:
|
||||||
op = mod.opcodes[stack.ip]
|
op = mod.opcodes[stackframe.ip]
|
||||||
print("{0}{1: <50} {2}: {3}".format(" " * stack.depth, str(stack.stack), stack.ip, op))
|
print("{0}{1: <50} {2}: {3}".format(" " * stackframe.depth, str(stackframe.stack), stackframe.ip, op))
|
||||||
|
|
||||||
match op:
|
match op:
|
||||||
case Opcode.TRUE():
|
case Opcode.TRUE():
|
||||||
stack.push(True)
|
stackframe.push(True)
|
||||||
|
|
||||||
case Opcode.FALSE():
|
case Opcode.FALSE():
|
||||||
stack.push(False)
|
stackframe.push(False)
|
||||||
|
|
||||||
case Opcode.IF(target):
|
case Opcode.IF(target):
|
||||||
if len(stack) < 1:
|
if len(stackframe) < 1:
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
val = stack.pop()
|
val = stackframe.pop()
|
||||||
if val not in [True, False]:
|
if val not in [True, False]:
|
||||||
_error("Type violation")
|
_error("Type violation")
|
||||||
|
|
||||||
if val is False:
|
if val is False:
|
||||||
stack.ip = target
|
stackframe.ip = target
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case Opcode.GOTO(n):
|
case Opcode.GOTO(n):
|
||||||
if (n < 0):
|
if (n < 0):
|
||||||
_error("Illegal branch target")
|
_error("Illegal branch target")
|
||||||
|
|
||||||
stack.ip = n
|
stackframe.ip = n
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case Opcode.DUP(n):
|
case Opcode.DUP(n):
|
||||||
if (n > len(stack)):
|
if (n > len(stackframe)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
stack.dup(n)
|
stackframe.dup(n)
|
||||||
|
|
||||||
case Opcode.ROT(n):
|
case Opcode.ROT(n):
|
||||||
if (n > len(stack)):
|
if (n > len(stackframe)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
stack.rot(n)
|
stackframe.rot(n)
|
||||||
|
|
||||||
case Opcode.DROP(n):
|
case Opcode.DROP(n):
|
||||||
if (n > len(stack)):
|
if (n > len(stackframe)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
stack.drop(n)
|
stackframe.drop(n)
|
||||||
|
|
||||||
case Opcode.SLOT(n):
|
case Opcode.SLOT(n):
|
||||||
if (n < 0):
|
if (n < 0):
|
||||||
_error("SLOT must have a positive reference")
|
_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")
|
_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):
|
case Opcode.IDENTIFIERC(name):
|
||||||
if not (name in mod.functions or name in mod.types):
|
if not (name in mod.functions or name in mod.types):
|
||||||
_error("IDENTIFIERC references unknown entity")
|
_error("IDENTIFIERC references unknown entity")
|
||||||
|
|
||||||
stack.push(Identifier(name))
|
stackframe.push(Identifier(name))
|
||||||
|
|
||||||
case Opcode.FUNREF():
|
case Opcode.FUNREF():
|
||||||
id = stack.pop()
|
id = stackframe.pop()
|
||||||
if not isinstance(id, Identifier):
|
if not isinstance(id, Identifier):
|
||||||
_error("FUNREF consumes an IDENTIFIER")
|
_error("FUNREF consumes an IDENTIFIER")
|
||||||
try:
|
try:
|
||||||
# FIXME: Verify this statically
|
# FIXME: Verify this statically
|
||||||
stack.push(FunctionRef.parse(id.name))
|
stackframe.push(FunctionRef.parse(id.name))
|
||||||
except:
|
except:
|
||||||
_error("Invalid function ref")
|
_error("Invalid function ref")
|
||||||
|
|
||||||
case Opcode.CALLF(n):
|
case Opcode.CALLF(n):
|
||||||
sig = stack.pop()
|
sig = stackframe.pop()
|
||||||
if not isinstance(sig, FunctionRef):
|
if not isinstance(sig, FunctionRef):
|
||||||
_error("CALLF requires a funref at top of stack")
|
_error("CALLF requires a funref at top of stack")
|
||||||
if n != len(sig.args):
|
if n != len(sig.args):
|
||||||
_error("CALLF target violation; argument count missmatch")
|
_error("CALLF target violation; argument count missmatch")
|
||||||
if n > len(stack):
|
if n > len(stackframe):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -184,69 +178,69 @@ class Interpreter(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
_error("Unknown target")
|
_error("Unknown target")
|
||||||
|
|
||||||
stack = stack.call(sig, ip)
|
stackframe = stackframe.call(sig, ip)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case Opcode.RETURN(n):
|
case Opcode.RETURN(n):
|
||||||
if (n > len(stack)):
|
if (n > len(stackframe)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
if stack.depth == 0:
|
if stackframe.depth == 0:
|
||||||
return stack[:n]
|
return stackframe[:n]
|
||||||
|
|
||||||
sig = FunctionRef.parse(stack.name)
|
sig = FunctionRef.parse(stackframe.name)
|
||||||
if (len(sig.ret) != n):
|
if (len(sig.ret) != n):
|
||||||
_error("Signature violation")
|
_error("Signature violation")
|
||||||
|
|
||||||
stack = stack.ret(n)
|
stackframe = stackframe.ret(n)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case Opcode.CLOSUREF(n):
|
case Opcode.CLOSUREF(n):
|
||||||
sig = stack.pop()
|
sig = stackframe.pop()
|
||||||
if not isinstance(sig, FunctionRef):
|
if not isinstance(sig, FunctionRef):
|
||||||
_error("CLOSUREF requires a funref at top of stack")
|
_error("CLOSUREF requires a funref at top of stack")
|
||||||
if not n <= len(sig.args):
|
if not n <= len(sig.args):
|
||||||
_error("CLOSUREF target violation; too many parameters provided")
|
_error("CLOSUREF target violation; too many parameters provided")
|
||||||
if n > len(stack):
|
if n > len(stackframe):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
c = Closure(
|
c = Closure(
|
||||||
sig,
|
sig,
|
||||||
stack.stack[:n]
|
stackframe.stack[:n]
|
||||||
)
|
)
|
||||||
stack.drop(n)
|
stackframe.drop(n)
|
||||||
stack.push(c)
|
stackframe.push(c)
|
||||||
|
|
||||||
case Opcode.CLOSUREC(n):
|
case Opcode.CLOSUREC(n):
|
||||||
c = stack.pop()
|
c = stackframe.pop()
|
||||||
if not isinstance(c, Closure):
|
if not isinstance(c, Closure):
|
||||||
_error("CLOSUREC requires a closure at top of stack")
|
_error("CLOSUREC requires a closure at top of stack")
|
||||||
if n + len(c.frag) > len(c.funref.args):
|
if n + len(c.frag) > len(c.funref.args):
|
||||||
_error("CLOSUREC target violation; too many parameters provided")
|
_error("CLOSUREC target violation; too many parameters provided")
|
||||||
if n > len(stack):
|
if n > len(stackframe):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
c = Closure(
|
c = Closure(
|
||||||
c.funref,
|
c.funref,
|
||||||
stack.stack[:n] + c.frag
|
stackframe.stack[:n] + c.frag
|
||||||
)
|
)
|
||||||
stack.drop(n)
|
stackframe.drop(n)
|
||||||
stack.push(c)
|
stackframe.push(c)
|
||||||
|
|
||||||
case Opcode.CALLC(n):
|
case Opcode.CALLC(n):
|
||||||
c = stack.pop()
|
c = stackframe.pop()
|
||||||
if not isinstance(c, Closure):
|
if not isinstance(c, Closure):
|
||||||
_error("CALLC requires a closure at top of stack")
|
_error("CALLC requires a closure at top of stack")
|
||||||
if n + len(c.frag) != len(c.funref.args):
|
if n + len(c.frag) != len(c.funref.args):
|
||||||
_error("CALLC target vionation; argument count missmatch")
|
_error("CALLC target vionation; argument count missmatch")
|
||||||
if n > len(stack):
|
if n > len(stackframe):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
# Extract the function signature
|
# Extract the function signature
|
||||||
sig = c.funref
|
sig = c.funref
|
||||||
|
|
||||||
# Push the closure's stack fragment
|
# Push the closure's stack fragment
|
||||||
stack.stack = c.frag + stack.stack
|
stackframe.stack = c.frag + stackframe.stack
|
||||||
|
|
||||||
# Perform a "normal" funref call
|
# Perform a "normal" funref call
|
||||||
try:
|
try:
|
||||||
|
@ -254,28 +248,10 @@ class Interpreter(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
_error("Unknown target")
|
_error("Unknown target")
|
||||||
|
|
||||||
stack = stack.call(sig, ip)
|
stackframe = stackframe.call(sig, ip)
|
||||||
continue
|
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 _:
|
case _:
|
||||||
raise Exception(f"Unhandled interpreter state {op}")
|
raise Exception(f"Unhandled interpreter state {op}")
|
||||||
|
|
||||||
stack.ip += 1
|
stackframe.ip += 1
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from .typing import *
|
|
||||||
|
|
||||||
|
|
||||||
class Opcode:
|
class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
@ -231,61 +229,3 @@ class Opcode:
|
||||||
class BREAK(t.NamedTuple):
|
class BREAK(t.NamedTuple):
|
||||||
"""Abort the interpreter."""
|
"""Abort the interpreter."""
|
||||||
pass
|
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)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from ichor import *
|
from ichor import Interpreter, BOOTSTRAP
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue