[NO TESTS] WIP

This commit is contained in:
Reid 'arrdem' McKenzie 2022-06-27 22:35:21 -06:00
parent 25234f8f00
commit 13b9afb195
10 changed files with 328 additions and 333 deletions

View file

@ -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"),

View file

@ -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)

View file

@ -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),
] ]
) )

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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):

View file

@ -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

View 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

View file

@ -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