Get CLOSUREF/CALLC working

This commit is contained in:
Reid D. 'arrdem' McKenzie 2022-05-31 23:54:11 -06:00
parent d1fab97c85
commit 3debddf0b2
5 changed files with 94 additions and 31 deletions

View file

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

View file

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

View file

@ -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 = {}

View file

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

View file

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