diff --git a/projects/shoggoth/BUILD b/projects/shoggoth/BUILD index d694b36..22b3cf9 100644 --- a/projects/shoggoth/BUILD +++ b/projects/shoggoth/BUILD @@ -4,6 +4,7 @@ py_project( main_deps = [ py_requirement("prompt_toolkit"), py_requirement("yaspin"), + py_requirement("pyrsistent"), ], lib_deps = [ py_requirement("lark"), diff --git a/projects/shoggoth/src/python/ichor/__main__.py b/projects/shoggoth/src/python/ichor/__main__.py index db61d41..f4bc616 100644 --- a/projects/shoggoth/src/python/ichor/__main__.py +++ b/projects/shoggoth/src/python/ichor/__main__.py @@ -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) diff --git a/projects/shoggoth/src/python/ichor/bootstrap.py b/projects/shoggoth/src/python/ichor/bootstrap.py index 90ad4f8..d34b42f 100644 --- a/projects/shoggoth/src/python/ichor/bootstrap.py +++ b/projects/shoggoth/src/python/ichor/bootstrap.py @@ -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(), # a + Opcode.DUP(), + Opcode.IDENTIFIERC("true"), + Opcode.VARIANTREF(), # 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 A B C - Opcode.SLOT(1), # B C A B C - Opcode.SLOT(3), # B C A B C - Opcode.CALLF(2), # B&C A B C - Opcode.SLOT(2), # A B&C A B C - Opcode.SLOT(3), # A B&C A B C - Opcode.CALLF(2), # A&B&C 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(), ] ) diff --git a/projects/shoggoth/src/python/ichor/impl.py b/projects/shoggoth/src/python/ichor/impl.py index d8200e5..3fefde8 100644 --- a/projects/shoggoth/src/python/ichor/impl.py +++ b/projects/shoggoth/src/python/ichor/impl.py @@ -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: diff --git a/projects/shoggoth/src/python/ichor/state.py b/projects/shoggoth/src/python/ichor/state.py index a369e28..cb8782f 100644 --- a/projects/shoggoth/src/python/ichor/state.py +++ b/projects/shoggoth/src/python/ichor/state.py @@ -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) diff --git a/projects/shoggoth/test/python/ichor/test_bootstrap.py b/projects/shoggoth/test/python/ichor/test_bootstrap.py index f6b6321..283c3f3 100644 --- a/projects/shoggoth/test/python/ichor/test_bootstrap.py +++ b/projects/shoggoth/test/python/ichor/test_bootstrap.py @@ -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 diff --git a/projects/shoggoth/test/python/ichor/test_interpreter.py b/projects/shoggoth/test/python/ichor/test_interpreter.py index b502a0b..57d9dc4 100644 --- a/projects/shoggoth/test/python/ichor/test_interpreter.py +++ b/projects/shoggoth/test/python/ichor/test_interpreter.py @@ -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): diff --git a/projects/shoggoth/test/python/ichor/test_parsers.py b/projects/shoggoth/test/python/ichor/test_parsers.py index 1b9a6c6..40ac275 100644 --- a/projects/shoggoth/test/python/ichor/test_parsers.py +++ b/projects/shoggoth/test/python/ichor/test_parsers.py @@ -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 diff --git a/projects/shoggoth/test/python/ichor/test_state.py b/projects/shoggoth/test/python/ichor/test_state.py new file mode 100644 index 0000000..dffadb0 --- /dev/null +++ b/projects/shoggoth/test/python/ichor/test_state.py @@ -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 diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt index aaeea2a..4c1089a 100644 --- a/tools/python/requirements.txt +++ b/tools/python/requirements.txt @@ -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