[NO TESTS] WIP
This commit is contained in:
parent
25234f8f00
commit
13b9afb195
10 changed files with 328 additions and 333 deletions
|
@ -4,6 +4,7 @@ py_project(
|
|||
main_deps = [
|
||||
py_requirement("prompt_toolkit"),
|
||||
py_requirement("yaspin"),
|
||||
py_requirement("pyrsistent"),
|
||||
],
|
||||
lib_deps = [
|
||||
py_requirement("lark"),
|
||||
|
|
|
@ -4,22 +4,17 @@
|
|||
ichor entrypoint
|
||||
"""
|
||||
|
||||
from . import Opcode, Interpreter, BOOTSTRAP, XOR3
|
||||
from . import Opcode, Interpreter, BOOTSTRAP, XOR3, NOT1, TRUE, FALSE
|
||||
|
||||
|
||||
def main():
|
||||
vm = Interpreter(BOOTSTRAP)
|
||||
ret = vm.run(
|
||||
[
|
||||
Opcode.IDENTIFIERC(XOR3),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CLOSUREF(1),
|
||||
Opcode.CLOSUREC(1),
|
||||
Opcode.CALLC(1),
|
||||
Opcode.RETURN(1),
|
||||
],
|
||||
stack = [True, True, False]
|
||||
)
|
||||
ret = vm.run([
|
||||
Opcode.IDENTIFIERC(NOT1),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(1),
|
||||
Opcode.RETURN(1)
|
||||
], stack = [TRUE])
|
||||
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.state import Module
|
||||
from ichor.state import Module, Variant
|
||||
|
||||
|
||||
BOOTSTRAP = Module()
|
||||
|
@ -14,13 +14,30 @@ BOOL = BOOTSTRAP.define_type(
|
|||
";bool;true(),false()",
|
||||
)
|
||||
|
||||
TRUE = Variant(BOOL, 'true', ())
|
||||
FALSE = Variant(BOOL, 'false', ())
|
||||
|
||||
NOT1 = BOOTSTRAP.define_function(
|
||||
f";not;{BOOL};{BOOL}",
|
||||
[
|
||||
Opcode.IF(target=3),
|
||||
Opcode.FALSE(),
|
||||
# a: Bool
|
||||
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.TRUE(),
|
||||
|
||||
Opcode.DROP(1),
|
||||
Opcode.IDENTIFIERC("false"),
|
||||
Opcode.VARIANTREF(),
|
||||
Opcode.VARIANT(0),
|
||||
Opcode.RETURN(1),
|
||||
],
|
||||
)
|
||||
|
@ -28,124 +45,42 @@ NOT1 = BOOTSTRAP.define_function(
|
|||
OR2 = BOOTSTRAP.define_function(
|
||||
f";or;{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
Opcode.IF(target=3),
|
||||
Opcode.TRUE(),
|
||||
Opcode.RETURN(1),
|
||||
Opcode.IF(target=6),
|
||||
Opcode.TRUE(),
|
||||
Opcode.RETURN(1),
|
||||
Opcode.FALSE(),
|
||||
Opcode.RETURN(1)
|
||||
Opcode.BREAK(),
|
||||
],
|
||||
)
|
||||
|
||||
OR3 = BOOTSTRAP.define_function(
|
||||
f";or;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
# A B C
|
||||
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),
|
||||
Opcode.BREAK(),
|
||||
]
|
||||
)
|
||||
|
||||
AND2 = BOOTSTRAP.define_function(
|
||||
f";and;{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
Opcode.IF(target=3),
|
||||
Opcode.IF(target=3),
|
||||
Opcode.GOTO(target=5),
|
||||
Opcode.FALSE(),
|
||||
Opcode.RETURN(1),
|
||||
Opcode.TRUE(),
|
||||
Opcode.RETURN(1),
|
||||
Opcode.BREAK(),
|
||||
],
|
||||
)
|
||||
|
||||
AND3 = BOOTSTRAP.define_function(
|
||||
f";and;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
# A B C
|
||||
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),
|
||||
Opcode.BREAK(),
|
||||
],
|
||||
)
|
||||
|
||||
XOR2 = BOOTSTRAP.define_function(
|
||||
f";xor;{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
Opcode.IDENTIFIERC(AND2),
|
||||
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),
|
||||
Opcode.BREAK(),
|
||||
],
|
||||
)
|
||||
|
||||
XOR3 = BOOTSTRAP.define_function(
|
||||
f";xor;{BOOL},{BOOL},{BOOL};{BOOL}",
|
||||
[
|
||||
Opcode.IDENTIFIERC(XOR2),
|
||||
Opcode.FUNREF(),
|
||||
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),
|
||||
# A^B|B^C
|
||||
Opcode.BREAK(),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -11,78 +11,10 @@ context (a virtual machine) which DOES have an easily introspected and serialize
|
|||
|
||||
from copy import deepcopy
|
||||
import typing as t
|
||||
from textwrap import indent
|
||||
|
||||
from ichor.isa import Opcode
|
||||
from ichor.state import Closure, FunctionRef, Identifier, Module, Function, Type, TypeRef, VariantRef, Variant
|
||||
|
||||
|
||||
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)
|
||||
from ichor.state import Closure, FunctionRef, Identifier, Module, Function, Type, TypeRef, VariantRef, Variant, Stackframe
|
||||
|
||||
|
||||
class InterpreterError(Exception):
|
||||
|
@ -107,6 +39,7 @@ class Interpreter(object):
|
|||
main_fun = mod.functions[main]
|
||||
main_ip = mod.labels[main]
|
||||
stackframe = Stackframe(main_fun, main_ip, stack)
|
||||
clock: int = 0
|
||||
|
||||
print(mod)
|
||||
|
||||
|
@ -115,13 +48,26 @@ class Interpreter(object):
|
|||
# And the stack object isn't immutable or otherwise designed for cheap snapshotting
|
||||
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:
|
||||
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:
|
||||
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")
|
||||
|
||||
stackframe.push(Identifier(name))
|
||||
|
@ -141,7 +87,7 @@ class Interpreter(object):
|
|||
_error("VARIANTREF consumes an identifier and a typeref")
|
||||
|
||||
t: TypeRef = stackframe.pop()
|
||||
if not isinstance(id, TypeRef):
|
||||
if not isinstance(t, TypeRef):
|
||||
_error("VARIANTREF consumes an identifier and a typeref")
|
||||
|
||||
type = mod.types[t.name]
|
||||
|
@ -164,7 +110,7 @@ class Interpreter(object):
|
|||
|
||||
# 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
|
||||
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.push(v)
|
||||
|
||||
|
@ -265,7 +211,7 @@ class Interpreter(object):
|
|||
|
||||
c = Closure(
|
||||
sig,
|
||||
stackframe._stack[:n]
|
||||
stackframe[:n]
|
||||
)
|
||||
stackframe.drop(n)
|
||||
stackframe.push(c)
|
||||
|
@ -282,7 +228,7 @@ class Interpreter(object):
|
|||
|
||||
c = Closure(
|
||||
c.funref,
|
||||
stackframe._stack[:n] + c.frag
|
||||
stackframe[:n] + c.frag
|
||||
)
|
||||
stackframe.drop(n)
|
||||
stackframe.push(c)
|
||||
|
@ -300,7 +246,7 @@ class Interpreter(object):
|
|||
# Extract the function signature
|
||||
|
||||
# Push the closure's stack fragment
|
||||
stackframe._stack = c.frag + stackframe._stack
|
||||
stackframe._stack = stackframe._stack.extendleft(c.frag)
|
||||
|
||||
# Perform a "normal" funref call
|
||||
try:
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
"""The core VM/interpreter model."""
|
||||
|
||||
|
||||
import typing as t
|
||||
|
||||
from ichor.isa import Opcode
|
||||
|
||||
from pyrsistent import pdeque, PDeque
|
||||
from lark import Lark, Transformer, v_args, Token
|
||||
|
||||
|
||||
|
@ -154,7 +155,7 @@ class Type(t.NamedTuple):
|
|||
class Variant(t.NamedTuple):
|
||||
type: str
|
||||
variant: str
|
||||
fields: t.Tuple[t.Any]
|
||||
fields: t.Tuple
|
||||
|
||||
|
||||
class Closure(t.NamedTuple):
|
||||
|
@ -190,10 +191,10 @@ class Module(t.NamedTuple):
|
|||
def translate(start: int, end: int, i: Opcode):
|
||||
# FIXME: Consolidate bounds checks somehow
|
||||
match i:
|
||||
case Opcode.IF(t):
|
||||
case Opcode.VTEST(t):
|
||||
d = t + start
|
||||
assert start <= d < end
|
||||
return Opcode.IF(d)
|
||||
return Opcode.VTEST(d)
|
||||
|
||||
case Opcode.GOTO(t):
|
||||
d = t + start
|
||||
|
@ -248,3 +249,75 @@ class Module(t.NamedTuple):
|
|||
b.append(f" {marks[i]!r}:")
|
||||
b.append(f" {i: >10}: {o}")
|
||||
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", [
|
||||
[[True], [False]],
|
||||
[[True], [False]],
|
||||
[[TRUE], [FALSE]],
|
||||
[[FALSE], [TRUE]],
|
||||
])
|
||||
def test_not(vm, stack, ret):
|
||||
assert vm.run([
|
||||
|
@ -19,124 +19,123 @@ def test_not(vm, stack, ret):
|
|||
], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [True]],
|
||||
[[False, True], [True]],
|
||||
[[True, True], [True]],
|
||||
])
|
||||
def test_or(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(OR2),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(2),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False], [False]],
|
||||
# [[True, False], [True]],
|
||||
# [[False, True], [True]],
|
||||
# [[True, True], [True]],
|
||||
# ])
|
||||
# def test_or(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(OR2),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CALLF(2),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [False]],
|
||||
[[False, True], [False]],
|
||||
[[True, True], [True]],
|
||||
])
|
||||
def test_and(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(AND2),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(2),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False], [False]],
|
||||
# [[True, False], [False]],
|
||||
# [[False, True], [False]],
|
||||
# [[True, True], [True]],
|
||||
# ])
|
||||
# def test_and(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(AND2),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CALLF(2),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [True]],
|
||||
[[False, True], [True]],
|
||||
[[True, True], [False]],
|
||||
])
|
||||
def test_xor2(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(XOR2),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(2),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False], [False]],
|
||||
# [[True, False], [True]],
|
||||
# [[False, True], [True]],
|
||||
# [[True, True], [False]],
|
||||
# ])
|
||||
# def test_xor2(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(XOR2),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CALLF(2),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False, False], [False]],
|
||||
[[True, False, False], [True]],
|
||||
[[False, True, False], [True]],
|
||||
[[True, True, False], [True]],
|
||||
[[True, True, True], [False]],
|
||||
[[False, True, True], [True]],
|
||||
[[False, False, True], [True]],
|
||||
])
|
||||
def test_xor3(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(XOR3),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(3),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False, False], [False]],
|
||||
# [[True, False, False], [True]],
|
||||
# [[False, True, False], [True]],
|
||||
# [[True, True, False], [True]],
|
||||
# [[True, True, True], [False]],
|
||||
# [[False, True, True], [True]],
|
||||
# [[False, False, True], [True]],
|
||||
# ])
|
||||
# def test_xor3(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(XOR3),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CALLF(3),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[], [FunctionRef.parse(NOT1)]]
|
||||
])
|
||||
def test_funref(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(NOT1),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[], [FunctionRef.parse(NOT1)]]
|
||||
# ])
|
||||
# def test_funref(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(NOT1),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[], [True]]
|
||||
])
|
||||
def test_callf(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.IDENTIFIERC(NOT1),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CALLF(1),
|
||||
Opcode.RETURN(1)
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False], [True]]
|
||||
# ])
|
||||
# def test_callf(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(NOT1),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CALLF(1),
|
||||
# Opcode.RETURN(1)
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [True]],
|
||||
[[False, True], [True]],
|
||||
[[True, True], [False]],
|
||||
])
|
||||
def test_callc(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(XOR2),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CLOSUREF(1),
|
||||
Opcode.CALLC(1),
|
||||
Opcode.RETURN(1),
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False], [False]],
|
||||
# [[True, False], [True]],
|
||||
# [[False, True], [True]],
|
||||
# [[True, True], [False]],
|
||||
# ])
|
||||
# def test_callc(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(XOR2),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CLOSUREF(1),
|
||||
# Opcode.CALLC(1),
|
||||
# Opcode.RETURN(1),
|
||||
# ], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stack,ret", [
|
||||
[[False, False, False], [False]],
|
||||
[[True, False, False], [True]],
|
||||
[[False, True, False], [True]],
|
||||
[[True, True, False], [True]],
|
||||
[[True, True, True], [False]],
|
||||
[[False, True, True], [True]],
|
||||
[[False, False, True], [True]],
|
||||
])
|
||||
def test_closurec(vm, stack, ret):
|
||||
assert vm.run([
|
||||
Opcode.IDENTIFIERC(XOR3),
|
||||
Opcode.FUNREF(),
|
||||
Opcode.CLOSUREF(1),
|
||||
Opcode.CLOSUREC(1),
|
||||
Opcode.CALLC(1),
|
||||
Opcode.RETURN(1),
|
||||
], stack = stack) == ret
|
||||
# @pytest.mark.parametrize("stack,ret", [
|
||||
# [[False, False, False], [False]],
|
||||
# [[True, False, False], [True]],
|
||||
# [[False, True, False], [True]],
|
||||
# [[True, True, False], [True]],
|
||||
# [[True, True, True], [False]],
|
||||
# [[False, True, True], [True]],
|
||||
# [[False, False, True], [True]],
|
||||
# ])
|
||||
# def test_closurec(vm, stack, ret):
|
||||
# assert vm.run([
|
||||
# Opcode.IDENTIFIERC(XOR3),
|
||||
# Opcode.FUNREF(),
|
||||
# Opcode.CLOSUREF(1),
|
||||
# Opcode.CLOSUREC(1),
|
||||
# Opcode.CALLC(1),
|
||||
# Opcode.RETURN(1),
|
||||
# ], stack = stack) == ret
|
||||
|
|
|
@ -8,60 +8,34 @@ from ichor import *
|
|||
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):
|
||||
assert vm.run([Opcode.FALSE(), Opcode.RETURN(0)]) == []
|
||||
assert vm.run([Opcode.TRUE(), Opcode.FALSE(), Opcode.RETURN(1)]) == [False]
|
||||
assert vm.run([Opcode.TRUE(), Opcode.FALSE(), Opcode.RETURN(2)]) == [False, True]
|
||||
assert vm.run([Opcode.RETURN(0)], stack=[TRUE, FALSE]) == []
|
||||
assert vm.run([Opcode.RETURN(1)], stack=[TRUE, FALSE]) == [TRUE]
|
||||
assert vm.run([Opcode.RETURN(2)], stack=[TRUE, FALSE]) == [TRUE, FALSE]
|
||||
|
||||
|
||||
def test_dup(vm):
|
||||
assert vm.run([
|
||||
Opcode.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]
|
||||
assert vm.run([Opcode.DUP(1), Opcode.RETURN(3)], stack=[FALSE, TRUE]) == [FALSE, TRUE, TRUE]
|
||||
assert vm.run([Opcode.DUP(2), Opcode.RETURN(4)], stack=[FALSE, TRUE]) == [FALSE, TRUE, FALSE, TRUE]
|
||||
|
||||
|
||||
def test_rot(vm):
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.ROT(2),
|
||||
Opcode.RETURN(2)
|
||||
]) == [True, False]
|
||||
], stack=[FALSE, TRUE]) == [TRUE, FALSE]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.ROT(3),
|
||||
Opcode.RETURN(3)
|
||||
]) == [False, False, True]
|
||||
], stack=[FALSE, TRUE, FALSE]) == [FALSE, FALSE, TRUE]
|
||||
|
||||
|
||||
def test_drop(vm):
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.DROP(1),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
], stack=[TRUE, FALSE]) == [TRUE]
|
||||
|
||||
|
||||
def test_dup_too_many(vm):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from ichor.state import FUNC, VAR
|
||||
from ichor.state import FUNC, TYPE
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -18,5 +18,5 @@ def test_func_parses(sig, parse):
|
|||
(";bool;true(),false()", ((), "bool", (("true", ()), ("false", ())))),
|
||||
("A,B;pair;pair(a:A,b:B)", (("A", "B"), "pair", (("pair", (("a", "A"), ("b", "B"))),))),
|
||||
])
|
||||
def test_var_parses(sig, parse):
|
||||
assert VAR.parse(sig) == parse
|
||||
def test_type_parses(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
|
||||
Pygments==2.11.2
|
||||
pyparsing==3.0.6
|
||||
pyrsistent==0.18.0
|
||||
pyrsistent==0.18.1
|
||||
pytest==6.2.5
|
||||
pytest-cov==3.0.0
|
||||
pytest-postgresql==4.1.0
|
||||
|
|
Loading…
Reference in a new issue