Get CLOSUREF/CALLC working
This commit is contained in:
parent
e8c12be6e8
commit
aef6cc088d
5 changed files with 94 additions and 31 deletions
|
@ -11,10 +11,12 @@ def main():
|
||||||
vm = Interpreter(BOOTSTRAP)
|
vm = Interpreter(BOOTSTRAP)
|
||||||
ret = vm.run(
|
ret = vm.run(
|
||||||
[
|
[
|
||||||
Opcode.CALLS(XOR3),
|
Opcode.FUNREF(XOR2),
|
||||||
Opcode.RETURN(1)
|
Opcode.CLOSUREF(1),
|
||||||
|
Opcode.CALLC(1),
|
||||||
|
Opcode.RETURN(1),
|
||||||
],
|
],
|
||||||
stack = [True, False, False]
|
stack = [True, False]
|
||||||
)
|
)
|
||||||
print(ret)
|
print(ret)
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,11 @@ context (a virtual machine) which DOES have an easily introspected and serialize
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
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 FunctionRef, Opcode
|
from .isa import FunctionRef, Opcode, Closure
|
||||||
|
|
||||||
|
|
||||||
def rotate(l):
|
def rotate(l):
|
||||||
|
@ -183,8 +182,8 @@ class Interpreter(object):
|
||||||
sig = stack.pop()
|
sig = stack.pop()
|
||||||
if not isinstance(sig, FunctionRef):
|
if not isinstance(sig, FunctionRef):
|
||||||
_error("CALLF requires a funref at top of stack")
|
_error("CALLF requires a funref at top of stack")
|
||||||
if not n == len(sig.args):
|
if n != len(sig.args):
|
||||||
_error("CALLF target violation; not enough arguments provided")
|
_error("CALLF target violation; argument count missmatch")
|
||||||
if n > len(stack):
|
if n > len(stack):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
|
@ -196,6 +195,47 @@ class Interpreter(object):
|
||||||
stack = stack.call(sig, ip)
|
stack = stack.call(sig, ip)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
case Opcode.CLOSUREF(n):
|
||||||
|
sig = stack.pop()
|
||||||
|
if not isinstance(sig, FunctionRef):
|
||||||
|
_error("CLOSUREF requires a funref at top of stack")
|
||||||
|
if not n <= len(sig.args):
|
||||||
|
_error("CLOSUREF target violation; too many parameters provided")
|
||||||
|
if n > len(stack):
|
||||||
|
_error("Stack size violation")
|
||||||
|
|
||||||
|
c = Closure(
|
||||||
|
sig,
|
||||||
|
stack.stack[:n]
|
||||||
|
)
|
||||||
|
stack.drop(n)
|
||||||
|
stack.push(c)
|
||||||
|
|
||||||
|
case Opcode.CALLC(n):
|
||||||
|
c = stack.pop()
|
||||||
|
if not isinstance(c, Closure):
|
||||||
|
_error("CALLC requires a closure at top of stack")
|
||||||
|
if n + len(c.frag) != len(c.funref.args):
|
||||||
|
_error("CALLC target vionation; argument count missmatch")
|
||||||
|
if n > len(stack):
|
||||||
|
_error("Stack size vionation")
|
||||||
|
|
||||||
|
# Extract the function signature
|
||||||
|
sig = c.funref
|
||||||
|
|
||||||
|
# Push the closure's stack fragment
|
||||||
|
stack.stack = c.frag + stack.stack
|
||||||
|
|
||||||
|
# Perform a "normal" funref call
|
||||||
|
try:
|
||||||
|
ip = mod.functions[sig.raw]
|
||||||
|
except KeyError:
|
||||||
|
_error("Unknown target")
|
||||||
|
|
||||||
|
stack = stack.call(sig, ip)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
raise Exception(f"Unhandled interpreter state {op}")
|
raise Exception(f"Unhandled interpreter state {op}")
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
"""The instruction set for Shogoth."""
|
"""The instruction set for Shogoth."""
|
||||||
|
|
||||||
|
|
||||||
from typing import NamedTuple
|
import typing as t
|
||||||
|
|
||||||
from .typing import FunctionRef
|
from .typing import *
|
||||||
|
|
||||||
|
|
||||||
class Opcode:
|
class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Logic
|
# Logic
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class TRUE(NamedTuple):
|
class TRUE(t.NamedTuple):
|
||||||
"""() -> (bool)
|
"""() -> (bool)
|
||||||
Push the constant TRUE onto the stack.
|
Push the constant TRUE onto the stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class FALSE(NamedTuple):
|
class FALSE(t.NamedTuple):
|
||||||
"""() -> (bool)
|
"""() -> (bool)
|
||||||
Push the constant FALSE onto the stack.
|
Push the constant FALSE onto the stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class IF(NamedTuple):
|
class IF(t.NamedTuple):
|
||||||
"""(bool) -> ()
|
"""(bool) -> ()
|
||||||
Branch to another point if the top item of the stack is TRUE.
|
Branch to another point if the top item of the stack is TRUE.
|
||||||
Otherwise fall through.
|
Otherwise fall through.
|
||||||
|
@ -33,21 +33,21 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Stack manipulation
|
# Stack manipulation
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class DUP(NamedTuple):
|
class DUP(t.NamedTuple):
|
||||||
"""(A, B, ...) -> (A, B, ...)
|
"""(A, B, ...) -> (A, B, ...)
|
||||||
Duplicate the top N items of the stack.
|
Duplicate the top N items of the stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nargs: int = 1
|
nargs: int = 1
|
||||||
|
|
||||||
class ROT(NamedTuple):
|
class ROT(t.NamedTuple):
|
||||||
"""(A, B, ... Z) -> (Z, A, B, ...)
|
"""(A, B, ... Z) -> (Z, A, B, ...)
|
||||||
Rotate the top N elements of the stack.
|
Rotate the top N elements of the stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nargs: int = 2
|
nargs: int = 2
|
||||||
|
|
||||||
class DROP(NamedTuple):
|
class DROP(t.NamedTuple):
|
||||||
"""(*) -> ()
|
"""(*) -> ()
|
||||||
Drop the top N items of the stack.
|
Drop the top N items of the stack.
|
||||||
"""
|
"""
|
||||||
|
@ -57,7 +57,7 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Functional abstraction
|
# Functional abstraction
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class CALLS(NamedTuple):
|
class CALLS(t.NamedTuple):
|
||||||
"""(... A) -> (... B)
|
"""(... A) -> (... B)
|
||||||
Call [static]
|
Call [static]
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class Opcode:
|
||||||
|
|
||||||
funref: str
|
funref: str
|
||||||
|
|
||||||
class RETURN(NamedTuple):
|
class RETURN(t.NamedTuple):
|
||||||
"""(... A) -> ()
|
"""(... A) -> ()
|
||||||
Return to the source of the last `CALL`.
|
Return to the source of the last `CALL`.
|
||||||
The returnee will see the top `nargs` values of the present stack appended to theirs.
|
The returnee will see the top `nargs` values of the present stack appended to theirs.
|
||||||
|
@ -87,7 +87,7 @@ class Opcode:
|
||||||
|
|
||||||
nargs: int
|
nargs: int
|
||||||
|
|
||||||
class GOTO(NamedTuple):
|
class GOTO(t.NamedTuple):
|
||||||
"""() -> ()
|
"""() -> ()
|
||||||
Branch to another point within the same bytecode segment.
|
Branch to another point within the same bytecode segment.
|
||||||
The target MUST be within the same module range as the current function.
|
The target MUST be within the same module range as the current function.
|
||||||
|
@ -97,14 +97,14 @@ class Opcode:
|
||||||
target: int
|
target: int
|
||||||
anywhere: bool = False
|
anywhere: bool = False
|
||||||
|
|
||||||
class FUNREF(NamedTuple):
|
class FUNREF(t.NamedTuple):
|
||||||
"""() -> (`FUNREF<... A to ... B>`)
|
"""() -> (`FUNREF<... A to ... B>`)
|
||||||
Construct a reference to a static codepoint.
|
Construct a reference to a static codepoint.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
funref: str
|
funref: str
|
||||||
|
|
||||||
class CALLF(NamedTuple):
|
class CALLF(t.NamedTuple):
|
||||||
"""(`FUNREF<... A to ... B>`, ... A) -> (... B)
|
"""(`FUNREF<... A to ... B>`, ... A) -> (... B)
|
||||||
Call [funref]
|
Call [funref]
|
||||||
|
|
||||||
|
@ -117,19 +117,20 @@ class Opcode:
|
||||||
|
|
||||||
nargs: int = 0
|
nargs: int = 0
|
||||||
|
|
||||||
class CLOSUREF(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>`)
|
||||||
Construct a closure over the function reference at the top of the stack.
|
Construct a closure over the function reference at the top of the stack.
|
||||||
This may produce nullary closures.
|
This may produce nullary closures.
|
||||||
"""
|
"""
|
||||||
|
nargs: int = 0
|
||||||
|
|
||||||
class CLOSUREC(NamedTuple):
|
class CLOSUREC(t.NamedTuple):
|
||||||
"""(`CLOSURE<A, ... B to ... C>`, A) -> (`CLOSURE<... B to ... C>`)
|
"""(`CLOSURE<A, ... B to ... C>`, A) -> (`CLOSURE<... B to ... C>`)
|
||||||
Further close over the closure at the top of the stack.
|
Further close over the closure at the top of the stack.
|
||||||
This may produce nullary closures.
|
This may produce nullary closures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class CALLC(NamedTuple):
|
class CALLC(t.NamedTuple):
|
||||||
"""(`CLOSURE<... A to ... B>`, ... A) -> (... B)
|
"""(`CLOSURE<... A to ... B>`, ... A) -> (... B)
|
||||||
Call [closure]
|
Call [closure]
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Structures
|
# Structures
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class STRUCT(NamedTuple):
|
class STRUCT(t.NamedTuple):
|
||||||
"""(*) -> (T)
|
"""(*) -> (T)
|
||||||
Consume the top N items of the stack, producing a struct of the type `structref`.
|
Consume the top N items of the stack, producing a struct of the type `structref`.
|
||||||
|
|
||||||
|
@ -155,14 +156,14 @@ class Opcode:
|
||||||
structref: str
|
structref: str
|
||||||
nargs: int
|
nargs: int
|
||||||
|
|
||||||
class FLOAD(NamedTuple):
|
class FLOAD(t.NamedTuple):
|
||||||
"""(A) -> (B)
|
"""(A) -> (B)
|
||||||
Consume the struct reference at the top of the stack, producing the value of the referenced field.
|
Consume the struct reference at the top of the stack, producing the value of the referenced field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fieldref: str
|
fieldref: str
|
||||||
|
|
||||||
class FSTORE(NamedTuple):
|
class FSTORE(t.NamedTuple):
|
||||||
"""(A) -> (B)
|
"""(A) -> (B)
|
||||||
Consume the struct reference at the top of the stack, producing the value of the referenced field.
|
Consume the struct reference at the top of the stack, producing the value of the referenced field.
|
||||||
"""
|
"""
|
||||||
|
@ -172,7 +173,7 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Arrays
|
# Arrays
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class ARRAY(NamedTuple):
|
class ARRAY(t.NamedTuple):
|
||||||
"""(*) -> (ARRAY<Y>)
|
"""(*) -> (ARRAY<Y>)
|
||||||
Consume the top N items of the stack, producing an array of the type `typeref`.
|
Consume the top N items of the stack, producing an array of the type `typeref`.
|
||||||
"""
|
"""
|
||||||
|
@ -180,13 +181,13 @@ class Opcode:
|
||||||
typeref: str
|
typeref: str
|
||||||
nargs: int
|
nargs: int
|
||||||
|
|
||||||
class ALOAD(NamedTuple):
|
class ALOAD(t.NamedTuple):
|
||||||
"""(NAT, ARRAY<T>) -> (T)
|
"""(NAT, ARRAY<T>) -> (T)
|
||||||
Consume a reference to an array and an index, producing the value at that index.
|
Consume a reference to an array and an index, producing the value at that index.
|
||||||
FIXME: Or a fault/signal.
|
FIXME: Or a fault/signal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ASTORE(NamedTuple):
|
class ASTORE(t.NamedTuple):
|
||||||
"""(T, NAT, ARRAY<T>) -> (ARRAY<T>)
|
"""(T, NAT, ARRAY<T>) -> (ARRAY<T>)
|
||||||
Consume a value T, storing it at an index in the given array.
|
Consume a value T, storing it at an index in the given array.
|
||||||
Produces the updated array as the top of stack.
|
Produces the updated array as the top of stack.
|
||||||
|
@ -205,7 +206,7 @@ class Opcode:
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class Module(NamedTuple):
|
class Module(t.NamedTuple):
|
||||||
opcodes: list = []
|
opcodes: list = []
|
||||||
functions: dict = {}
|
functions: dict = {}
|
||||||
types: dict = {}
|
types: dict = {}
|
||||||
|
|
|
@ -75,3 +75,8 @@ class FunctionSignature(t.NamedTuple):
|
||||||
cls.parse_list(args),
|
cls.parse_list(args),
|
||||||
cls.parse_list(ret)
|
cls.parse_list(ret)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Closure(t.NamedTuple):
|
||||||
|
funref: FunctionRef
|
||||||
|
frag: t.List[t.Any]
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
from .fixtures import * # noqa
|
from .fixtures import * # noqa
|
||||||
|
|
||||||
from ichor import *
|
from ichor import *
|
||||||
from ichor.isa import Opcode
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,6 +43,7 @@ def test_and(vm, stack, ret):
|
||||||
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.CALLS(XOR2), Opcode.RETURN(1)], 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]],
|
||||||
|
@ -69,3 +69,18 @@ def test_funref(vm, stack, ret):
|
||||||
])
|
])
|
||||||
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.FUNREF(NOT1), 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.FUNREF(XOR2),
|
||||||
|
Opcode.CLOSUREF(1),
|
||||||
|
Opcode.CALLC(1),
|
||||||
|
Opcode.RETURN(1),
|
||||||
|
], stack = stack) == ret
|
||||||
|
|
Loading…
Reference in a new issue