[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 = [
py_requirement("prompt_toolkit"),
py_requirement("yaspin"),
py_requirement("pyrsistent"),
],
lib_deps = [
py_requirement("lark"),

View file

@ -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),
ret = vm.run([
Opcode.IDENTIFIERC(NOT1),
Opcode.FUNREF(),
Opcode.CLOSUREF(1),
Opcode.CLOSUREC(1),
Opcode.CALLC(1),
Opcode.RETURN(1),
],
stack = [True, True, False]
)
Opcode.CALLF(1),
Opcode.RETURN(1)
], stack = [TRUE])
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.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(),
]
)

View file

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

View file

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

View file

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

View file

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

View file

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

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