Eliminate CALLS, add SLOT & DEBUG, rewrite bootstrap
This commit is contained in:
parent
17f40a198f
commit
e3e3d7613c
5 changed files with 197 additions and 115 deletions
|
@ -39,8 +39,16 @@ OR3 = BOOTSTRAP.define_function(
|
||||||
";or;bool,bool,bool;bool",
|
";or;bool,bool,bool;bool",
|
||||||
[
|
[
|
||||||
# A B C
|
# A B C
|
||||||
Opcode.CALLS(OR2), # A|B C
|
Opcode.IDENTIFIERC(OR2),
|
||||||
Opcode.CALLS(OR2), # A|B|C
|
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.RETURN(1),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -62,8 +70,15 @@ AND3 = BOOTSTRAP.define_function(
|
||||||
";and;bool,bool,bool;bool",
|
";and;bool,bool,bool;bool",
|
||||||
[
|
[
|
||||||
# A B C
|
# A B C
|
||||||
Opcode.CALLS(AND2), # A&B C
|
Opcode.IDENTIFIERC(AND2),
|
||||||
Opcode.CALLS(AND2), # A&B&C
|
Opcode.FUNREF(),
|
||||||
|
Opcode.SLOT(0),
|
||||||
|
Opcode.SLOT(1),
|
||||||
|
Opcode.SLOT(3),
|
||||||
|
Opcode.CALLF(2), # A&B C
|
||||||
|
Opcode.SLOT(2),
|
||||||
|
Opcode.SLOT(3),
|
||||||
|
Opcode.CALLF(2), # A&B&C
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -71,18 +86,30 @@ AND3 = BOOTSTRAP.define_function(
|
||||||
XOR2 = BOOTSTRAP.define_function(
|
XOR2 = BOOTSTRAP.define_function(
|
||||||
";xor;bool,bool;bool",
|
";xor;bool,bool;bool",
|
||||||
[
|
[
|
||||||
|
Opcode.IDENTIFIERC(AND2),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.IDENTIFIERC(NOT1),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
|
||||||
|
Opcode.SLOT(0),
|
||||||
|
Opcode.SLOT(1),
|
||||||
Opcode.DUP(nargs=2),
|
Opcode.DUP(nargs=2),
|
||||||
|
|
||||||
# !A && B
|
# !A && B
|
||||||
Opcode.CALLS(NOT1),
|
Opcode.SLOT(3), # not
|
||||||
Opcode.CALLS(AND2),
|
Opcode.CALLF(1),
|
||||||
Opcode.IF(target=6),
|
Opcode.SLOT(2), # and
|
||||||
|
Opcode.CALLF(2),
|
||||||
|
Opcode.IF(target=14),
|
||||||
Opcode.TRUE(),
|
Opcode.TRUE(),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
# !B && A
|
# !B && A
|
||||||
Opcode.ROT(2),
|
Opcode.ROT(2),
|
||||||
Opcode.CALLS(NOT1),
|
Opcode.SLOT(3), # not
|
||||||
Opcode.CALLS(AND2),
|
Opcode.CALLF(1),
|
||||||
Opcode.IF(target=12),
|
Opcode.SLOT(2), # and
|
||||||
|
Opcode.CALLF(2),
|
||||||
|
Opcode.IF(target=22),
|
||||||
Opcode.TRUE(),
|
Opcode.TRUE(),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
Opcode.FALSE(),
|
Opcode.FALSE(),
|
||||||
|
@ -94,16 +121,27 @@ XOR2 = BOOTSTRAP.define_function(
|
||||||
XOR3 = BOOTSTRAP.define_function(
|
XOR3 = BOOTSTRAP.define_function(
|
||||||
";xor;bool,bool,bool;bool",
|
";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
|
# A B C
|
||||||
Opcode.ROT(nargs=3), # C A B
|
Opcode.ROT(nargs=3), # C A B
|
||||||
Opcode.ROT(nargs=3), # B C A
|
Opcode.ROT(nargs=3), # B C A
|
||||||
Opcode.DUP(nargs=1), # B B C A
|
Opcode.DUP(nargs=1), # B B C A
|
||||||
Opcode.ROT(nargs=4), # A B B C
|
Opcode.ROT(nargs=4), # A B B C
|
||||||
Opcode.CALLS(XOR2), # 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), # C A^B B
|
||||||
Opcode.ROT(nargs=3), # B C A^B
|
Opcode.ROT(nargs=3), # B C A^B
|
||||||
Opcode.CALLS(XOR2), # B^C A^B
|
Opcode.SLOT(3),
|
||||||
Opcode.CALLS(OR2), # A^B|B^C
|
Opcode.CALLF(2), # B^C A^B
|
||||||
|
Opcode.SLOT(4),
|
||||||
|
Opcode.CALLF(2), # A^B|B^C
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,12 +11,14 @@ context (a virtual machine) which DOES have an easily introspected and serialize
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from ichor.typing import Identifier
|
||||||
|
|
||||||
|
|
||||||
assert sys.version_info > (3, 10, 0), "`match` support is required"
|
assert sys.version_info > (3, 10, 0), "`match` support is required"
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from .isa import Closure, FunctionRef, Opcode
|
from .isa import Closure, FunctionRef, Opcode, Identifier
|
||||||
|
|
||||||
|
|
||||||
def rotate(l):
|
def rotate(l):
|
||||||
|
@ -120,6 +122,13 @@ class Interpreter(object):
|
||||||
stack.ip = target
|
stack.ip = target
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
case Opcode.GOTO(n):
|
||||||
|
if (n < 0):
|
||||||
|
_error("Illegal branch target")
|
||||||
|
|
||||||
|
stack.ip = n
|
||||||
|
continue
|
||||||
|
|
||||||
case Opcode.DUP(n):
|
case Opcode.DUP(n):
|
||||||
if (n > len(stack)):
|
if (n > len(stack)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
@ -138,45 +147,26 @@ class Interpreter(object):
|
||||||
|
|
||||||
stack.drop(n)
|
stack.drop(n)
|
||||||
|
|
||||||
case Opcode.CALLS(dest):
|
case Opcode.SLOT(n):
|
||||||
try:
|
|
||||||
sig = FunctionRef.parse(dest)
|
|
||||||
except:
|
|
||||||
_error("Invalid target")
|
|
||||||
|
|
||||||
try:
|
|
||||||
ip = mod.functions[dest]
|
|
||||||
except KeyError:
|
|
||||||
_error("Unknown target")
|
|
||||||
|
|
||||||
stack = stack.call(sig, ip)
|
|
||||||
continue
|
|
||||||
|
|
||||||
case Opcode.RETURN(n):
|
|
||||||
if (n > len(stack)):
|
|
||||||
_error("Stack size violation")
|
|
||||||
|
|
||||||
if stack.depth == 0:
|
|
||||||
return stack[:n]
|
|
||||||
|
|
||||||
sig = FunctionRef.parse(stack.name)
|
|
||||||
if (len(sig.ret) != n):
|
|
||||||
_error("Signature violation")
|
|
||||||
|
|
||||||
stack = stack.ret(n)
|
|
||||||
continue
|
|
||||||
|
|
||||||
case Opcode.GOTO(n, _):
|
|
||||||
if (n < 0):
|
if (n < 0):
|
||||||
_error("Illegal branch target")
|
_error("SLOT must have a positive reference")
|
||||||
|
if (n > len(stack.stack) - 1):
|
||||||
|
_error("SLOT reference out of range")
|
||||||
|
stack.push(stack.stack[len(stack) - n - 1])
|
||||||
|
|
||||||
stack.ip = n
|
case Opcode.IDENTIFIERC(name):
|
||||||
continue
|
if not (name in mod.functions or name in mod.types):
|
||||||
|
_error("IDENTIFIERC references unknown entity")
|
||||||
|
|
||||||
case Opcode.FUNREF(funref):
|
stack.push(Identifier(name))
|
||||||
|
|
||||||
|
case Opcode.FUNREF():
|
||||||
|
id = stack.pop()
|
||||||
|
if not isinstance(id, Identifier):
|
||||||
|
_error("FUNREF consumes an IDENTIFIER")
|
||||||
try:
|
try:
|
||||||
# FIXME: Verify this statically
|
# FIXME: Verify this statically
|
||||||
stack.push(FunctionRef.parse(funref))
|
stack.push(FunctionRef.parse(id.name))
|
||||||
except:
|
except:
|
||||||
_error("Invalid function ref")
|
_error("Invalid function ref")
|
||||||
|
|
||||||
|
@ -197,6 +187,20 @@ class Interpreter(object):
|
||||||
stack = stack.call(sig, ip)
|
stack = stack.call(sig, ip)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
case Opcode.RETURN(n):
|
||||||
|
if (n > len(stack)):
|
||||||
|
_error("Stack size violation")
|
||||||
|
|
||||||
|
if stack.depth == 0:
|
||||||
|
return stack[:n]
|
||||||
|
|
||||||
|
sig = FunctionRef.parse(stack.name)
|
||||||
|
if (len(sig.ret) != n):
|
||||||
|
_error("Signature violation")
|
||||||
|
|
||||||
|
stack = stack.ret(n)
|
||||||
|
continue
|
||||||
|
|
||||||
case Opcode.CLOSUREF(n):
|
case Opcode.CLOSUREF(n):
|
||||||
sig = stack.pop()
|
sig = stack.pop()
|
||||||
if not isinstance(sig, FunctionRef):
|
if not isinstance(sig, FunctionRef):
|
||||||
|
|
|
@ -10,6 +10,8 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Logic
|
# Logic
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
# FIXME: This should become an instantiation of the BOOL enum
|
||||||
class TRUE(t.NamedTuple):
|
class TRUE(t.NamedTuple):
|
||||||
"""() -> (bool)
|
"""() -> (bool)
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ class Opcode:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# FIXME: This should become an instantiation of the BOOL enum
|
||||||
class FALSE(t.NamedTuple):
|
class FALSE(t.NamedTuple):
|
||||||
"""() -> (bool)
|
"""() -> (bool)
|
||||||
|
|
||||||
|
@ -24,6 +27,7 @@ class Opcode:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# FIXME: This should become a `VTEST` macro ... or may be replaceable
|
||||||
class IF(t.NamedTuple):
|
class IF(t.NamedTuple):
|
||||||
"""(bool) -> ()
|
"""(bool) -> ()
|
||||||
|
|
||||||
|
@ -35,12 +39,23 @@ class Opcode:
|
||||||
|
|
||||||
# not, and, or, xor etc. can all be functions given if.
|
# not, and, or, xor etc. can all be functions given if.
|
||||||
|
|
||||||
|
class GOTO(t.NamedTuple):
|
||||||
|
"""() -> ()
|
||||||
|
|
||||||
|
Branch to another point within the same bytecode segment. The target MUST be within the same module range as the
|
||||||
|
current function. Branching does NOT update the name or module of the current function.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
target: int
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Stack manipulation
|
# Stack manipulation
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# https://wiki.laptop.org/go/Forth_stack_operators
|
# https://wiki.laptop.org/go/Forth_stack_operators
|
||||||
# https://www.forth.com/starting-forth/2-stack-manipulation-operators-arithmetic/
|
# https://www.forth.com/starting-forth/2-stack-manipulation-operators-arithmetic/
|
||||||
# https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-6.html#jvms-6.5.swap
|
# https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-6.html#jvms-6.5.swap
|
||||||
|
|
||||||
class DUP(t.NamedTuple):
|
class DUP(t.NamedTuple):
|
||||||
"""(A, B, ...) -> (A, B, ...)
|
"""(A, B, ...) -> (A, B, ...)
|
||||||
|
|
||||||
|
@ -68,62 +83,39 @@ class Opcode:
|
||||||
|
|
||||||
nargs: int = 1
|
nargs: int = 1
|
||||||
|
|
||||||
####################################################################################################
|
class SLOT(t.NamedTuple):
|
||||||
# Functional abstraction
|
"""(..., A) -> (A, ..., A)
|
||||||
####################################################################################################
|
|
||||||
class CALLS(t.NamedTuple):
|
|
||||||
"""(... A) -> (... B)
|
|
||||||
|
|
||||||
Call [static]
|
Copy the Nth (counting up from 0 at the bottom of the stack) item to the top of the stack.
|
||||||
|
Intended to allow users to emulate (immutable) frame local slots for reused values.
|
||||||
Branch to `target` pushing the current point onto the call stack.
|
|
||||||
The callee will see a stack containg only the provided `nargs`.
|
|
||||||
A subsequent RETURN will return execution to the next point.
|
|
||||||
|
|
||||||
Executing a `CALL` pushes the name and module path of the current function.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
CALLS is equvalent to `FUNREF; CALLF`
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
funref: str
|
|
||||||
|
|
||||||
class RETURN(t.NamedTuple):
|
|
||||||
"""(... A) -> ()
|
|
||||||
|
|
||||||
Return to the source of the last `CALL`. The returnee will see the top `nargs` values of the present stack
|
|
||||||
appended to theirs. All other values on the stack will be discarded.
|
|
||||||
|
|
||||||
Executing a `RETURN` pops (restores) the name and module path of the current function back to that of the caller.
|
|
||||||
|
|
||||||
If the call stack is empty, `RETURN` will exit the interpreter.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
nargs: int
|
|
||||||
|
|
||||||
class GOTO(t.NamedTuple):
|
|
||||||
"""() -> ()
|
|
||||||
|
|
||||||
Branch to another point within the same bytecode segment. The target MUST be within the same module range as the
|
|
||||||
current function. Branching does NOT update the name or module of the current function.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
target: int
|
target: int
|
||||||
anywhere: bool = False
|
|
||||||
|
####################################################################################################
|
||||||
|
# Functional abstraction
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
class IDENTIFIERC(t.NamedTuple):
|
||||||
|
"""() -> (IDENTIFIER)
|
||||||
|
|
||||||
|
An inline constant which produces an identifier to the stack.
|
||||||
|
|
||||||
|
Identifiers name functions, fields and types but are not strings.
|
||||||
|
They are a VM-internal naming structure with reference to the module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
val: str
|
||||||
|
|
||||||
class FUNREF(t.NamedTuple):
|
class FUNREF(t.NamedTuple):
|
||||||
"""() -> (`FUNREF<... A to ... B>`)
|
"""(IDENTIFIER) -> (`FUNREF<... A to ... B>`)
|
||||||
|
|
||||||
Construct a reference to a static codepoint.
|
Construct a reference to a static codepoint.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
funref: str
|
|
||||||
|
|
||||||
class CALLF(t.NamedTuple):
|
class CALLF(t.NamedTuple):
|
||||||
"""(`FUNREF<... A to ... B>`, ... A) -> (... B)
|
"""(`FUNREF<... A to ... B>`, ... A) -> (... B)
|
||||||
|
|
||||||
|
@ -139,6 +131,21 @@ class Opcode:
|
||||||
|
|
||||||
nargs: int = 0
|
nargs: int = 0
|
||||||
|
|
||||||
|
class RETURN(t.NamedTuple):
|
||||||
|
"""(... A) -> ()
|
||||||
|
|
||||||
|
Return to the source of the last `CALL`. The returnee will see the top `nargs` values of the present stack
|
||||||
|
appended to theirs. All other values on the stack will be discarded.
|
||||||
|
|
||||||
|
Executing a `RETURN` pops (restores) the name and module path of the current function back to that of the caller.
|
||||||
|
|
||||||
|
If the call stack is empty, `RETURN` will exit the interpreter.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
nargs: int
|
||||||
|
|
||||||
|
|
||||||
class CLOSUREF(t.NamedTuple):
|
class CLOSUREF(t.NamedTuple):
|
||||||
"""(`FUNREF<A, ... B to ... C>`, A) -> (`CLOSURE<... B to ... C>`)
|
"""(`FUNREF<A, ... B to ... C>`, A) -> (`CLOSURE<... B to ... C>`)
|
||||||
|
|
||||||
|
@ -176,19 +183,7 @@ class Opcode:
|
||||||
# Structures
|
# Structures
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
# FIXME: This lacks any sort of way to do dynamic field references
|
# FIXME: This lacks any sort of way to do dynamic type/field references
|
||||||
|
|
||||||
class IDENTIFIERC(t.NamedTuple):
|
|
||||||
"""() -> (CONST)
|
|
||||||
|
|
||||||
An inline constant which produces an identifier to the stack.
|
|
||||||
|
|
||||||
Identifiers name functions, fields and types but are not strings.
|
|
||||||
They are a VM-internal naming structure with reference to the module.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
val: str
|
|
||||||
|
|
||||||
class TYPEREF(t.NamedTuple):
|
class TYPEREF(t.NamedTuple):
|
||||||
"""(IDENTIFIER) -> (TYPEREF)
|
"""(IDENTIFIER) -> (TYPEREF)
|
||||||
|
@ -316,6 +311,10 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class BREAK(t.NamedTuple):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Module(t.NamedTuple):
|
class Module(t.NamedTuple):
|
||||||
opcodes: list = []
|
opcodes: list = []
|
||||||
functions: dict = {}
|
functions: dict = {}
|
||||||
|
@ -339,7 +338,7 @@ class Module(t.NamedTuple):
|
||||||
assert start <= d < end
|
assert start <= d < end
|
||||||
return Opcode.IF(d)
|
return Opcode.IF(d)
|
||||||
|
|
||||||
case Opcode.GOTO(t, anywhere=False):
|
case Opcode.GOTO(t):
|
||||||
d = t + start
|
d = t + start
|
||||||
assert start <= d < end
|
assert start <= d < end
|
||||||
return Opcode.GOTO(d)
|
return Opcode.GOTO(d)
|
||||||
|
|
|
@ -91,3 +91,7 @@ class Struct(t.NamedTuple):
|
||||||
name: str
|
name: str
|
||||||
type_params: list
|
type_params: list
|
||||||
children: t.Mapping[str, t.Any]
|
children: t.Mapping[str, t.Any]
|
||||||
|
|
||||||
|
|
||||||
|
class Identifier(t.NamedTuple):
|
||||||
|
name: str
|
||||||
|
|
|
@ -11,7 +11,12 @@ import pytest
|
||||||
[[True], [False]],
|
[[True], [False]],
|
||||||
])
|
])
|
||||||
def test_not(vm, stack, ret):
|
def test_not(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(NOT1), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(NOT1),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.CALLF(1),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -21,7 +26,12 @@ def test_not(vm, stack, ret):
|
||||||
[[True, True], [True]],
|
[[True, True], [True]],
|
||||||
])
|
])
|
||||||
def test_or(vm, stack, ret):
|
def test_or(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(OR2), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(OR2),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.CALLF(2),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -31,7 +41,12 @@ def test_or(vm, stack, ret):
|
||||||
[[True, True], [True]],
|
[[True, True], [True]],
|
||||||
])
|
])
|
||||||
def test_and(vm, stack, ret):
|
def test_and(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(AND2), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(AND2),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.CALLF(2),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -41,7 +56,12 @@ def test_and(vm, stack, ret):
|
||||||
[[True, True], [False]],
|
[[True, True], [False]],
|
||||||
])
|
])
|
||||||
def test_xor2(vm, stack, ret):
|
def test_xor2(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(XOR2), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(XOR2),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.CALLF(2),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -54,21 +74,36 @@ def test_xor2(vm, stack, ret):
|
||||||
[[False, False, True], [True]],
|
[[False, False, True], [True]],
|
||||||
])
|
])
|
||||||
def test_xor3(vm, stack, ret):
|
def test_xor3(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(XOR3), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(XOR3),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.CALLF(3),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], 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([Opcode.FUNREF(NOT1), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([
|
||||||
|
Opcode.IDENTIFIERC(NOT1),
|
||||||
|
Opcode.FUNREF(),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
[[], [True]]
|
[[], [True]]
|
||||||
])
|
])
|
||||||
def test_callf(vm, stack, ret):
|
def test_callf(vm, stack, ret):
|
||||||
assert vm.run([Opcode.FALSE(), Opcode.FUNREF(NOT1), Opcode.CALLF(1), Opcode.RETURN(1)], stack = 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", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -79,7 +114,8 @@ def test_callf(vm, stack, ret):
|
||||||
])
|
])
|
||||||
def test_callc(vm, stack, ret):
|
def test_callc(vm, stack, ret):
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
Opcode.FUNREF(XOR2),
|
Opcode.IDENTIFIERC(XOR2),
|
||||||
|
Opcode.FUNREF(),
|
||||||
Opcode.CLOSUREF(1),
|
Opcode.CLOSUREF(1),
|
||||||
Opcode.CALLC(1),
|
Opcode.CALLC(1),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
|
@ -97,7 +133,8 @@ def test_callc(vm, stack, ret):
|
||||||
])
|
])
|
||||||
def test_closurec(vm, stack, ret):
|
def test_closurec(vm, stack, ret):
|
||||||
assert vm.run([
|
assert vm.run([
|
||||||
Opcode.FUNREF(XOR3),
|
Opcode.IDENTIFIERC(XOR3),
|
||||||
|
Opcode.FUNREF(),
|
||||||
Opcode.CLOSUREF(1),
|
Opcode.CLOSUREF(1),
|
||||||
Opcode.CLOSUREC(1),
|
Opcode.CLOSUREC(1),
|
||||||
Opcode.CALLC(1),
|
Opcode.CALLC(1),
|
||||||
|
|
Loading…
Reference in a new issue