[NO TESTS] WIP
This commit is contained in:
parent
102210bcee
commit
7e0273c7ad
10 changed files with 328 additions and 333 deletions
|
@ -4,6 +4,7 @@ py_project(
|
||||||
main_deps = [
|
main_deps = [
|
||||||
py_requirement("prompt_toolkit"),
|
py_requirement("prompt_toolkit"),
|
||||||
py_requirement("yaspin"),
|
py_requirement("yaspin"),
|
||||||
|
py_requirement("pyrsistent"),
|
||||||
],
|
],
|
||||||
lib_deps = [
|
lib_deps = [
|
||||||
py_requirement("lark"),
|
py_requirement("lark"),
|
||||||
|
|
|
@ -4,22 +4,17 @@
|
||||||
ichor entrypoint
|
ichor entrypoint
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import Opcode, Interpreter, BOOTSTRAP, XOR3
|
from . import Opcode, Interpreter, BOOTSTRAP, XOR3, NOT1, TRUE, FALSE
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
vm = Interpreter(BOOTSTRAP)
|
vm = Interpreter(BOOTSTRAP)
|
||||||
ret = vm.run(
|
ret = vm.run([
|
||||||
[
|
Opcode.IDENTIFIERC(NOT1),
|
||||||
Opcode.IDENTIFIERC(XOR3),
|
Opcode.FUNREF(),
|
||||||
Opcode.FUNREF(),
|
Opcode.CALLF(1),
|
||||||
Opcode.CLOSUREF(1),
|
Opcode.RETURN(1)
|
||||||
Opcode.CLOSUREC(1),
|
], stack = [TRUE])
|
||||||
Opcode.CALLC(1),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
],
|
|
||||||
stack = [True, True, False]
|
|
||||||
)
|
|
||||||
print(ret)
|
print(ret)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ Hopefully no "real" interpreter ever uses this code, since it's obviously replac
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ichor.isa import Opcode
|
from ichor.isa import Opcode
|
||||||
from ichor.state import Module
|
from ichor.state import Module, Variant
|
||||||
|
|
||||||
|
|
||||||
BOOTSTRAP = Module()
|
BOOTSTRAP = Module()
|
||||||
|
@ -14,13 +14,30 @@ BOOL = BOOTSTRAP.define_type(
|
||||||
";bool;true(),false()",
|
";bool;true(),false()",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TRUE = Variant(BOOL, 'true', ())
|
||||||
|
FALSE = Variant(BOOL, 'false', ())
|
||||||
|
|
||||||
NOT1 = BOOTSTRAP.define_function(
|
NOT1 = BOOTSTRAP.define_function(
|
||||||
f";not;{BOOL};{BOOL}",
|
f";not;{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
# a: Bool
|
||||||
Opcode.FALSE(),
|
Opcode.IDENTIFIERC("bool"),
|
||||||
|
Opcode.TYPEREF(), # <typeref bool> a
|
||||||
|
Opcode.DUP(),
|
||||||
|
Opcode.IDENTIFIERC("true"),
|
||||||
|
Opcode.VARIANTREF(), # <variantref true:bool> <typeref bool> a
|
||||||
|
Opcode.DUP(),
|
||||||
|
Opcode.SLOT(0),
|
||||||
|
Opcode.ROT(2),
|
||||||
|
Opcode.VTEST(11),
|
||||||
|
|
||||||
|
Opcode.VARIANT(0),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
Opcode.TRUE(),
|
|
||||||
|
Opcode.DROP(1),
|
||||||
|
Opcode.IDENTIFIERC("false"),
|
||||||
|
Opcode.VARIANTREF(),
|
||||||
|
Opcode.VARIANT(0),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -28,124 +45,42 @@ NOT1 = BOOTSTRAP.define_function(
|
||||||
OR2 = BOOTSTRAP.define_function(
|
OR2 = BOOTSTRAP.define_function(
|
||||||
f";or;{BOOL},{BOOL};{BOOL}",
|
f";or;{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
Opcode.BREAK(),
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
Opcode.IF(target=6),
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.RETURN(1)
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
OR3 = BOOTSTRAP.define_function(
|
OR3 = BOOTSTRAP.define_function(
|
||||||
f";or;{BOOL},{BOOL},{BOOL};{BOOL}",
|
f";or;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
# A B C
|
Opcode.BREAK(),
|
||||||
Opcode.IDENTIFIERC(OR2),
|
|
||||||
Opcode.FUNREF(),
|
|
||||||
# FIXME: This could be tightened by using ROT maybe...
|
|
||||||
Opcode.SLOT(0),
|
|
||||||
Opcode.SLOT(1),
|
|
||||||
Opcode.SLOT(3),
|
|
||||||
Opcode.CALLF(2), # A|B
|
|
||||||
Opcode.SLOT(2),
|
|
||||||
Opcode.SLOT(3),
|
|
||||||
Opcode.CALLF(2), # A|B|C
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
AND2 = BOOTSTRAP.define_function(
|
AND2 = BOOTSTRAP.define_function(
|
||||||
f";and;{BOOL},{BOOL};{BOOL}",
|
f";and;{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
Opcode.BREAK(),
|
||||||
Opcode.IF(target=3),
|
|
||||||
Opcode.GOTO(target=5),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
AND3 = BOOTSTRAP.define_function(
|
AND3 = BOOTSTRAP.define_function(
|
||||||
f";and;{BOOL},{BOOL},{BOOL};{BOOL}",
|
f";and;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
# A B C
|
Opcode.BREAK(),
|
||||||
Opcode.IDENTIFIERC(AND2),
|
|
||||||
Opcode.FUNREF(),
|
|
||||||
Opcode.SLOT(0), # C <and2> A B C
|
|
||||||
Opcode.SLOT(1), # B C <and2> A B C
|
|
||||||
Opcode.SLOT(3), # <and2> B C <and2> A B C
|
|
||||||
Opcode.CALLF(2), # B&C <and2> A B C
|
|
||||||
Opcode.SLOT(2), # A B&C <and2> A B C
|
|
||||||
Opcode.SLOT(3), # <and2> A B&C <and2> A B C
|
|
||||||
Opcode.CALLF(2), # A&B&C <and2> A B C
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
XOR2 = BOOTSTRAP.define_function(
|
XOR2 = BOOTSTRAP.define_function(
|
||||||
f";xor;{BOOL},{BOOL};{BOOL}",
|
f";xor;{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
Opcode.IDENTIFIERC(AND2),
|
Opcode.BREAK(),
|
||||||
Opcode.FUNREF(),
|
|
||||||
Opcode.IDENTIFIERC(NOT1),
|
|
||||||
Opcode.FUNREF(),
|
|
||||||
|
|
||||||
Opcode.SLOT(0),
|
|
||||||
Opcode.SLOT(1),
|
|
||||||
Opcode.DUP(nargs=2),
|
|
||||||
|
|
||||||
# !A && B
|
|
||||||
Opcode.SLOT(3), # not
|
|
||||||
Opcode.CALLF(1),
|
|
||||||
Opcode.SLOT(2), # and
|
|
||||||
Opcode.CALLF(2),
|
|
||||||
Opcode.IF(target=14),
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
# !B && A
|
|
||||||
Opcode.ROT(2),
|
|
||||||
Opcode.SLOT(3), # not
|
|
||||||
Opcode.CALLF(1),
|
|
||||||
Opcode.SLOT(2), # and
|
|
||||||
Opcode.CALLF(2),
|
|
||||||
Opcode.IF(target=22),
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
XOR3 = BOOTSTRAP.define_function(
|
XOR3 = BOOTSTRAP.define_function(
|
||||||
f";xor;{BOOL},{BOOL},{BOOL};{BOOL}",
|
f";xor;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||||
[
|
[
|
||||||
Opcode.IDENTIFIERC(XOR2),
|
# A^B|B^C
|
||||||
Opcode.FUNREF(),
|
Opcode.BREAK(),
|
||||||
Opcode.IDENTIFIERC(OR2),
|
|
||||||
Opcode.FUNREF(),
|
|
||||||
|
|
||||||
Opcode.SLOT(0),
|
|
||||||
Opcode.SLOT(1),
|
|
||||||
Opcode.SLOT(2),
|
|
||||||
# A B C
|
|
||||||
Opcode.ROT(nargs=3), # C A B
|
|
||||||
Opcode.ROT(nargs=3), # B C A
|
|
||||||
Opcode.DUP(nargs=1), # B B C A
|
|
||||||
Opcode.ROT(nargs=4), # A B B C
|
|
||||||
Opcode.SLOT(3),
|
|
||||||
Opcode.CALLF(2), # A^B B C
|
|
||||||
Opcode.ROT(nargs=3), # C A^B B
|
|
||||||
Opcode.ROT(nargs=3), # B C A^B
|
|
||||||
Opcode.SLOT(3),
|
|
||||||
Opcode.CALLF(2), # B^C A^B
|
|
||||||
Opcode.SLOT(4),
|
|
||||||
Opcode.CALLF(2), # A^B|B^C
|
|
||||||
Opcode.RETURN(1),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,78 +11,10 @@ context (a virtual machine) which DOES have an easily introspected and serialize
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import typing as t
|
import typing as t
|
||||||
|
from textwrap import indent
|
||||||
|
|
||||||
from ichor.isa import Opcode
|
from ichor.isa import Opcode
|
||||||
from ichor.state import Closure, FunctionRef, Identifier, Module, Function, Type, TypeRef, VariantRef, Variant
|
from ichor.state import Closure, FunctionRef, Identifier, Module, Function, Type, TypeRef, VariantRef, Variant, Stackframe
|
||||||
|
|
||||||
|
|
||||||
def rotate(l):
|
|
||||||
return [l[-1]] + l[:-1]
|
|
||||||
|
|
||||||
|
|
||||||
class Stackframe(object):
|
|
||||||
def __init__(self,
|
|
||||||
fun: Function,
|
|
||||||
ip: int,
|
|
||||||
stack: t.Optional[t.List[t.Any]] = None,
|
|
||||||
parent: t.Optional["Stackframe"] = None):
|
|
||||||
self._fun = fun
|
|
||||||
self._ip = ip
|
|
||||||
self._stack = stack or []
|
|
||||||
self._parent = parent
|
|
||||||
|
|
||||||
def push(self, obj):
|
|
||||||
self._stack.insert(0, obj)
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
return self._stack.pop(0)
|
|
||||||
|
|
||||||
def call(self, fun: Function, ip) -> "Stackframe":
|
|
||||||
assert isinstance(fun, Function)
|
|
||||||
assert isinstance(ip, int)
|
|
||||||
self._ip += 1
|
|
||||||
nargs = len(fun.arguments)
|
|
||||||
args, self._stack = self._stack[:nargs], self._stack[nargs:]
|
|
||||||
return Stackframe(
|
|
||||||
fun,
|
|
||||||
ip,
|
|
||||||
stack=args,
|
|
||||||
parent=self,
|
|
||||||
)
|
|
||||||
|
|
||||||
def ret(self, nargs) -> "Stackframe":
|
|
||||||
assert nargs >= 0
|
|
||||||
assert isinstance(self._parent, Stackframe)
|
|
||||||
self._parent._stack = self._stack[:nargs] + self._parent._stack
|
|
||||||
return self._parent
|
|
||||||
|
|
||||||
def dup(self, nargs):
|
|
||||||
self._stack = self._stack[:nargs] + self._stack
|
|
||||||
|
|
||||||
def drop(self, nargs):
|
|
||||||
self._stack = self._stack[nargs:]
|
|
||||||
|
|
||||||
def rot(self, nargs):
|
|
||||||
self._stack = rotate(self._stack[:nargs]) + self._stack[nargs:]
|
|
||||||
|
|
||||||
def slot(self, n):
|
|
||||||
self.push(self._stack[len(self) - n - 1])
|
|
||||||
|
|
||||||
def goto(self, target: int):
|
|
||||||
self._ip = target
|
|
||||||
|
|
||||||
@property
|
|
||||||
def depth(self):
|
|
||||||
if self._parent == None:
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
return self._parent.depth + 1
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self._stack.__getitem__(key)
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self._stack)
|
|
||||||
|
|
||||||
|
|
||||||
class InterpreterError(Exception):
|
class InterpreterError(Exception):
|
||||||
|
@ -107,6 +39,7 @@ class Interpreter(object):
|
||||||
main_fun = mod.functions[main]
|
main_fun = mod.functions[main]
|
||||||
main_ip = mod.labels[main]
|
main_ip = mod.labels[main]
|
||||||
stackframe = Stackframe(main_fun, main_ip, stack)
|
stackframe = Stackframe(main_fun, main_ip, stack)
|
||||||
|
clock: int = 0
|
||||||
|
|
||||||
print(mod)
|
print(mod)
|
||||||
|
|
||||||
|
@ -115,13 +48,26 @@ class Interpreter(object):
|
||||||
# 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(stackframe), msg)
|
raise InterpreterError(mod, deepcopy(stackframe), msg)
|
||||||
|
|
||||||
|
def _debug():
|
||||||
|
b = []
|
||||||
|
b.append(f"clock {clock}:")
|
||||||
|
b.append(" stack:")
|
||||||
|
for offset, it in zip(range(len(stackframe), 0, -1), stackframe):
|
||||||
|
b.append(f" {offset: <3} {it}")
|
||||||
|
b.append(f" op: {op}")
|
||||||
|
print(indent("\n".join(b), " " * stackframe.depth))
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
op = mod.codepage[stackframe._ip]
|
op = mod.codepage[stackframe._ip]
|
||||||
print("{0}{1: <50} {2}: {3}".format(" " * stackframe.depth, str(stackframe._stack), stackframe._ip, op))
|
_debug()
|
||||||
|
clock += 1
|
||||||
|
|
||||||
match op:
|
match op:
|
||||||
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
|
||||||
|
or any(name in t.constructors for t in mod.types.values())):
|
||||||
_error("IDENTIFIERC references unknown entity")
|
_error("IDENTIFIERC references unknown entity")
|
||||||
|
|
||||||
stackframe.push(Identifier(name))
|
stackframe.push(Identifier(name))
|
||||||
|
@ -141,7 +87,7 @@ class Interpreter(object):
|
||||||
_error("VARIANTREF consumes an identifier and a typeref")
|
_error("VARIANTREF consumes an identifier and a typeref")
|
||||||
|
|
||||||
t: TypeRef = stackframe.pop()
|
t: TypeRef = stackframe.pop()
|
||||||
if not isinstance(id, TypeRef):
|
if not isinstance(t, TypeRef):
|
||||||
_error("VARIANTREF consumes an identifier and a typeref")
|
_error("VARIANTREF consumes an identifier and a typeref")
|
||||||
|
|
||||||
type = mod.types[t.name]
|
type = mod.types[t.name]
|
||||||
|
@ -164,7 +110,7 @@ class Interpreter(object):
|
||||||
|
|
||||||
# FIXME: Where does type variable to type binding occur?
|
# FIXME: Where does type variable to type binding occur?
|
||||||
# Certainly needs to be AT LEAST here, where we also need to be doing some typechecking
|
# Certainly needs to be AT LEAST here, where we also need to be doing some typechecking
|
||||||
v = Variant(armref.type.name, armref.arm, tuple(stackframe._stack[:n]))
|
v = Variant(armref.type.name, armref.arm, tuple(stackframe[:n]))
|
||||||
stackframe.drop(n)
|
stackframe.drop(n)
|
||||||
stackframe.push(v)
|
stackframe.push(v)
|
||||||
|
|
||||||
|
@ -265,7 +211,7 @@ class Interpreter(object):
|
||||||
|
|
||||||
c = Closure(
|
c = Closure(
|
||||||
sig,
|
sig,
|
||||||
stackframe._stack[:n]
|
stackframe[:n]
|
||||||
)
|
)
|
||||||
stackframe.drop(n)
|
stackframe.drop(n)
|
||||||
stackframe.push(c)
|
stackframe.push(c)
|
||||||
|
@ -282,7 +228,7 @@ class Interpreter(object):
|
||||||
|
|
||||||
c = Closure(
|
c = Closure(
|
||||||
c.funref,
|
c.funref,
|
||||||
stackframe._stack[:n] + c.frag
|
stackframe[:n] + c.frag
|
||||||
)
|
)
|
||||||
stackframe.drop(n)
|
stackframe.drop(n)
|
||||||
stackframe.push(c)
|
stackframe.push(c)
|
||||||
|
@ -300,7 +246,7 @@ class Interpreter(object):
|
||||||
# Extract the function signature
|
# Extract the function signature
|
||||||
|
|
||||||
# Push the closure's stack fragment
|
# Push the closure's stack fragment
|
||||||
stackframe._stack = c.frag + stackframe._stack
|
stackframe._stack = stackframe._stack.extendleft(c.frag)
|
||||||
|
|
||||||
# Perform a "normal" funref call
|
# Perform a "normal" funref call
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
"""The core VM/interpreter model."""
|
"""The core VM/interpreter model."""
|
||||||
|
|
||||||
|
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from ichor.isa import Opcode
|
from ichor.isa import Opcode
|
||||||
|
|
||||||
|
from pyrsistent import pdeque, PDeque
|
||||||
from lark import Lark, Transformer, v_args, Token
|
from lark import Lark, Transformer, v_args, Token
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ class Type(t.NamedTuple):
|
||||||
class Variant(t.NamedTuple):
|
class Variant(t.NamedTuple):
|
||||||
type: str
|
type: str
|
||||||
variant: str
|
variant: str
|
||||||
fields: t.Tuple[t.Any]
|
fields: t.Tuple
|
||||||
|
|
||||||
|
|
||||||
class Closure(t.NamedTuple):
|
class Closure(t.NamedTuple):
|
||||||
|
@ -190,10 +191,10 @@ class Module(t.NamedTuple):
|
||||||
def translate(start: int, end: int, i: Opcode):
|
def translate(start: int, end: int, i: Opcode):
|
||||||
# FIXME: Consolidate bounds checks somehow
|
# FIXME: Consolidate bounds checks somehow
|
||||||
match i:
|
match i:
|
||||||
case Opcode.IF(t):
|
case Opcode.VTEST(t):
|
||||||
d = t + start
|
d = t + start
|
||||||
assert start <= d < end
|
assert start <= d < end
|
||||||
return Opcode.IF(d)
|
return Opcode.VTEST(d)
|
||||||
|
|
||||||
case Opcode.GOTO(t):
|
case Opcode.GOTO(t):
|
||||||
d = t + start
|
d = t + start
|
||||||
|
@ -248,3 +249,75 @@ class Module(t.NamedTuple):
|
||||||
b.append(f" {marks[i]!r}:")
|
b.append(f" {marks[i]!r}:")
|
||||||
b.append(f" {i: >10}: {o}")
|
b.append(f" {i: >10}: {o}")
|
||||||
return "\n".join(b)
|
return "\n".join(b)
|
||||||
|
|
||||||
|
|
||||||
|
def rotate(l):
|
||||||
|
return [l[-1]] + l[:-1]
|
||||||
|
|
||||||
|
|
||||||
|
class Stackframe(object):
|
||||||
|
def __init__(self,
|
||||||
|
fun: Function,
|
||||||
|
ip: int,
|
||||||
|
stack: t.Optional[t.List[t.Any]] = None,
|
||||||
|
parent: t.Optional["Stackframe"] = None):
|
||||||
|
self._fun = fun
|
||||||
|
self._ip = ip
|
||||||
|
self._stack: t.List = stack or []
|
||||||
|
self._parent = parent
|
||||||
|
|
||||||
|
def push(self, obj):
|
||||||
|
self._stack.append(obj)
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
return self._stack.pop()
|
||||||
|
|
||||||
|
def dup(self, nargs):
|
||||||
|
self._stack.extend(self._stack[-nargs:])
|
||||||
|
|
||||||
|
def drop(self, nargs):
|
||||||
|
self._stack = self._stack[:-nargs]
|
||||||
|
|
||||||
|
def rot(self, nargs):
|
||||||
|
self._stack[nargs:].extend(rotate(self._stack[:nargs]))
|
||||||
|
|
||||||
|
def slot(self, n):
|
||||||
|
self.push(self._stack[n])
|
||||||
|
|
||||||
|
def call(self, fun: Function, ip) -> "Stackframe":
|
||||||
|
assert isinstance(fun, Function)
|
||||||
|
assert isinstance(ip, int)
|
||||||
|
self._ip += 1
|
||||||
|
nargs = len(fun.arguments)
|
||||||
|
args, self._stack = self._stack[:nargs], self._stack[nargs:]
|
||||||
|
return Stackframe(
|
||||||
|
fun,
|
||||||
|
ip,
|
||||||
|
stack=args,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
|
||||||
|
def ret(self, nargs) -> "Stackframe":
|
||||||
|
assert nargs >= 0
|
||||||
|
assert isinstance(self._parent, Stackframe)
|
||||||
|
self._parent._stack.extend(self._stack[:nargs])
|
||||||
|
return self._parent
|
||||||
|
|
||||||
|
def goto(self, target: int):
|
||||||
|
self._ip = target
|
||||||
|
|
||||||
|
@property
|
||||||
|
def depth(self):
|
||||||
|
if self._parent == None:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return self._parent.depth + 1
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._stack.__getitem__(key)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._stack)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._stack)
|
||||||
|
|
|
@ -7,8 +7,8 @@ import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
[[True], [False]],
|
[[TRUE], [FALSE]],
|
||||||
[[True], [False]],
|
[[FALSE], [TRUE]],
|
||||||
])
|
])
|
||||||
def test_not(vm, stack, ret):
|
def test_not(vm, stack, ret):
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
|
@ -19,124 +19,123 @@ def test_not(vm, stack, ret):
|
||||||
], stack = stack) == ret
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False], [False]],
|
# [[False, False], [False]],
|
||||||
[[True, False], [True]],
|
# [[True, False], [True]],
|
||||||
[[False, True], [True]],
|
# [[False, True], [True]],
|
||||||
[[True, True], [True]],
|
# [[True, True], [True]],
|
||||||
])
|
# ])
|
||||||
def test_or(vm, stack, ret):
|
# def test_or(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(OR2),
|
# Opcode.IDENTIFIERC(OR2),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CALLF(2),
|
# Opcode.CALLF(2),
|
||||||
Opcode.RETURN(1)
|
# Opcode.RETURN(1)
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False], [False]],
|
# [[False, False], [False]],
|
||||||
[[True, False], [False]],
|
# [[True, False], [False]],
|
||||||
[[False, True], [False]],
|
# [[False, True], [False]],
|
||||||
[[True, True], [True]],
|
# [[True, True], [True]],
|
||||||
])
|
# ])
|
||||||
def test_and(vm, stack, ret):
|
# def test_and(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(AND2),
|
# Opcode.IDENTIFIERC(AND2),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CALLF(2),
|
# Opcode.CALLF(2),
|
||||||
Opcode.RETURN(1)
|
# Opcode.RETURN(1)
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False], [False]],
|
# [[False, False], [False]],
|
||||||
[[True, False], [True]],
|
# [[True, False], [True]],
|
||||||
[[False, True], [True]],
|
# [[False, True], [True]],
|
||||||
[[True, True], [False]],
|
# [[True, True], [False]],
|
||||||
])
|
# ])
|
||||||
def test_xor2(vm, stack, ret):
|
# def test_xor2(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(XOR2),
|
# Opcode.IDENTIFIERC(XOR2),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CALLF(2),
|
# Opcode.CALLF(2),
|
||||||
Opcode.RETURN(1)
|
# Opcode.RETURN(1)
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False, False], [False]],
|
# [[False, False, False], [False]],
|
||||||
[[True, False, False], [True]],
|
# [[True, False, False], [True]],
|
||||||
[[False, True, False], [True]],
|
# [[False, True, False], [True]],
|
||||||
[[True, True, False], [True]],
|
# [[True, True, False], [True]],
|
||||||
[[True, True, True], [False]],
|
# [[True, True, True], [False]],
|
||||||
[[False, True, True], [True]],
|
# [[False, True, True], [True]],
|
||||||
[[False, False, True], [True]],
|
# [[False, False, True], [True]],
|
||||||
])
|
# ])
|
||||||
def test_xor3(vm, stack, ret):
|
# def test_xor3(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(XOR3),
|
# Opcode.IDENTIFIERC(XOR3),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CALLF(3),
|
# Opcode.CALLF(3),
|
||||||
Opcode.RETURN(1)
|
# Opcode.RETURN(1)
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[], [FunctionRef.parse(NOT1)]]
|
# [[], [FunctionRef.parse(NOT1)]]
|
||||||
])
|
# ])
|
||||||
def test_funref(vm, stack, ret):
|
# def test_funref(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(NOT1),
|
# Opcode.IDENTIFIERC(NOT1),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.RETURN(1)
|
# Opcode.RETURN(1)
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[], [True]]
|
# [[False], [True]]
|
||||||
])
|
# ])
|
||||||
def test_callf(vm, stack, ret):
|
# def test_callf(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.FALSE(),
|
# Opcode.IDENTIFIERC(NOT1),
|
||||||
Opcode.IDENTIFIERC(NOT1),
|
# Opcode.FUNREF(),
|
||||||
Opcode.FUNREF(),
|
# Opcode.CALLF(1),
|
||||||
Opcode.CALLF(1),
|
# Opcode.RETURN(1)
|
||||||
Opcode.RETURN(1)
|
# ], stack = stack) == ret
|
||||||
], stack = stack) == ret
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False], [False]],
|
# [[False, False], [False]],
|
||||||
[[True, False], [True]],
|
# [[True, False], [True]],
|
||||||
[[False, True], [True]],
|
# [[False, True], [True]],
|
||||||
[[True, True], [False]],
|
# [[True, True], [False]],
|
||||||
])
|
# ])
|
||||||
def test_callc(vm, stack, ret):
|
# def test_callc(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(XOR2),
|
# Opcode.IDENTIFIERC(XOR2),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CLOSUREF(1),
|
# Opcode.CLOSUREF(1),
|
||||||
Opcode.CALLC(1),
|
# Opcode.CALLC(1),
|
||||||
Opcode.RETURN(1),
|
# Opcode.RETURN(1),
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
# @pytest.mark.parametrize("stack,ret", [
|
||||||
[[False, False, False], [False]],
|
# [[False, False, False], [False]],
|
||||||
[[True, False, False], [True]],
|
# [[True, False, False], [True]],
|
||||||
[[False, True, False], [True]],
|
# [[False, True, False], [True]],
|
||||||
[[True, True, False], [True]],
|
# [[True, True, False], [True]],
|
||||||
[[True, True, True], [False]],
|
# [[True, True, True], [False]],
|
||||||
[[False, True, True], [True]],
|
# [[False, True, True], [True]],
|
||||||
[[False, False, True], [True]],
|
# [[False, False, True], [True]],
|
||||||
])
|
# ])
|
||||||
def test_closurec(vm, stack, ret):
|
# def test_closurec(vm, stack, ret):
|
||||||
assert vm.run([
|
# assert vm.run([
|
||||||
Opcode.IDENTIFIERC(XOR3),
|
# Opcode.IDENTIFIERC(XOR3),
|
||||||
Opcode.FUNREF(),
|
# Opcode.FUNREF(),
|
||||||
Opcode.CLOSUREF(1),
|
# Opcode.CLOSUREF(1),
|
||||||
Opcode.CLOSUREC(1),
|
# Opcode.CLOSUREC(1),
|
||||||
Opcode.CALLC(1),
|
# Opcode.CALLC(1),
|
||||||
Opcode.RETURN(1),
|
# Opcode.RETURN(1),
|
||||||
], stack = stack) == ret
|
# ], stack = stack) == ret
|
||||||
|
|
|
@ -8,60 +8,34 @@ from ichor import *
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_true(vm):
|
|
||||||
assert vm.run([Opcode.TRUE(), Opcode.RETURN(1)]) == [True]
|
|
||||||
|
|
||||||
|
|
||||||
def test_false(vm):
|
|
||||||
assert vm.run([Opcode.FALSE(), Opcode.RETURN(1)]) == [False]
|
|
||||||
|
|
||||||
|
|
||||||
def test_return(vm):
|
def test_return(vm):
|
||||||
assert vm.run([Opcode.FALSE(), Opcode.RETURN(0)]) == []
|
assert vm.run([Opcode.RETURN(0)], stack=[TRUE, FALSE]) == []
|
||||||
assert vm.run([Opcode.TRUE(), Opcode.FALSE(), Opcode.RETURN(1)]) == [False]
|
assert vm.run([Opcode.RETURN(1)], stack=[TRUE, FALSE]) == [TRUE]
|
||||||
assert vm.run([Opcode.TRUE(), Opcode.FALSE(), Opcode.RETURN(2)]) == [False, True]
|
assert vm.run([Opcode.RETURN(2)], stack=[TRUE, FALSE]) == [TRUE, FALSE]
|
||||||
|
|
||||||
|
|
||||||
def test_dup(vm):
|
def test_dup(vm):
|
||||||
assert vm.run([
|
assert vm.run([Opcode.DUP(1), Opcode.RETURN(3)], stack=[FALSE, TRUE]) == [FALSE, TRUE, TRUE]
|
||||||
Opcode.TRUE(),
|
assert vm.run([Opcode.DUP(2), Opcode.RETURN(4)], stack=[FALSE, TRUE]) == [FALSE, TRUE, FALSE, TRUE]
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.DUP(1),
|
|
||||||
Opcode.RETURN(3)
|
|
||||||
]) == [False, False, True]
|
|
||||||
|
|
||||||
assert vm.run([
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.DUP(2),
|
|
||||||
Opcode.RETURN(4)
|
|
||||||
]) == [False, True, False, True]
|
|
||||||
|
|
||||||
|
|
||||||
def test_rot(vm):
|
def test_rot(vm):
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.ROT(2),
|
Opcode.ROT(2),
|
||||||
Opcode.RETURN(2)
|
Opcode.RETURN(2)
|
||||||
]) == [True, False]
|
], stack=[FALSE, TRUE]) == [TRUE, FALSE]
|
||||||
|
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.ROT(3),
|
Opcode.ROT(3),
|
||||||
Opcode.RETURN(3)
|
Opcode.RETURN(3)
|
||||||
]) == [False, False, True]
|
], stack=[FALSE, TRUE, FALSE]) == [FALSE, FALSE, TRUE]
|
||||||
|
|
||||||
|
|
||||||
def test_drop(vm):
|
def test_drop(vm):
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
Opcode.TRUE(),
|
|
||||||
Opcode.FALSE(),
|
|
||||||
Opcode.DROP(1),
|
Opcode.DROP(1),
|
||||||
Opcode.RETURN(1)
|
Opcode.RETURN(1)
|
||||||
]) == [True]
|
], stack=[TRUE, FALSE]) == [TRUE]
|
||||||
|
|
||||||
|
|
||||||
def test_dup_too_many(vm):
|
def test_dup_too_many(vm):
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from ichor.state import FUNC, VAR
|
from ichor.state import FUNC, TYPE
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -18,5 +18,5 @@ def test_func_parses(sig, parse):
|
||||||
(";bool;true(),false()", ((), "bool", (("true", ()), ("false", ())))),
|
(";bool;true(),false()", ((), "bool", (("true", ()), ("false", ())))),
|
||||||
("A,B;pair;pair(a:A,b:B)", (("A", "B"), "pair", (("pair", (("a", "A"), ("b", "B"))),))),
|
("A,B;pair;pair(a:A,b:B)", (("A", "B"), "pair", (("pair", (("a", "A"), ("b", "B"))),))),
|
||||||
])
|
])
|
||||||
def test_var_parses(sig, parse):
|
def test_type_parses(sig, parse):
|
||||||
assert VAR.parse(sig) == parse
|
assert TYPE.parse(sig) == parse
|
||||||
|
|
72
projects/shoggoth/test/python/ichor/test_state.py
Normal file
72
projects/shoggoth/test/python/ichor/test_state.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Tests covering the Ichor ISA and state model."""
|
||||||
|
|
||||||
|
from ichor.impl import Stackframe
|
||||||
|
from ichor.state import *
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def frame():
|
||||||
|
return Stackframe(None, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stackframe_lifo(frame):
|
||||||
|
frame.push(0)
|
||||||
|
frame.push(1)
|
||||||
|
frame.push(2)
|
||||||
|
assert frame.pop() == 2
|
||||||
|
assert frame.pop() == 1
|
||||||
|
assert frame.pop() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_stackframe_dup(frame):
|
||||||
|
frame.push(0)
|
||||||
|
frame.push(1)
|
||||||
|
frame.push(2)
|
||||||
|
frame.push(3)
|
||||||
|
frame.push(4)
|
||||||
|
|
||||||
|
frame.dup(1)
|
||||||
|
assert len(frame) == 6
|
||||||
|
assert frame.pop() == 4
|
||||||
|
assert frame.pop() == 4
|
||||||
|
|
||||||
|
frame.dup(2)
|
||||||
|
assert frame.pop() == 3
|
||||||
|
assert frame.pop() == 2
|
||||||
|
assert frame.pop() == 3
|
||||||
|
assert frame.pop() == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_stackframe_drop(frame):
|
||||||
|
frame.push(0)
|
||||||
|
frame.push(1)
|
||||||
|
frame.push(2)
|
||||||
|
frame.push(3)
|
||||||
|
frame.push(4)
|
||||||
|
|
||||||
|
assert len(frame) == 5
|
||||||
|
|
||||||
|
frame.drop(2)
|
||||||
|
assert len(frame) == 3
|
||||||
|
assert frame.pop() == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_stackframe_slot(frame):
|
||||||
|
frame.push(0)
|
||||||
|
frame.push(1)
|
||||||
|
frame.push(2)
|
||||||
|
frame.push(3)
|
||||||
|
frame.push(4)
|
||||||
|
|
||||||
|
frame.slot(0)
|
||||||
|
assert frame.pop() == 0
|
||||||
|
|
||||||
|
frame.slot(1)
|
||||||
|
assert frame.pop() == 1
|
||||||
|
|
||||||
|
frame.slot(2)
|
||||||
|
assert frame.pop() == 2
|
|
@ -69,7 +69,7 @@ pycodestyle==2.8.0
|
||||||
pyflakes==2.4.0
|
pyflakes==2.4.0
|
||||||
Pygments==2.11.2
|
Pygments==2.11.2
|
||||||
pyparsing==3.0.6
|
pyparsing==3.0.6
|
||||||
pyrsistent==0.18.0
|
pyrsistent==0.18.1
|
||||||
pytest==6.2.5
|
pytest==6.2.5
|
||||||
pytest-cov==3.0.0
|
pytest-cov==3.0.0
|
||||||
pytest-postgresql==4.1.0
|
pytest-postgresql==4.1.0
|
||||||
|
|
Loading…
Reference in a new issue