Get CLOSUREF/CALLC working

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

View file

@ -11,10 +11,12 @@ def main():
vm = Interpreter(BOOTSTRAP)
ret = vm.run(
[
Opcode.CALLS(XOR3),
Opcode.RETURN(1)
Opcode.FUNREF(XOR2),
Opcode.CLOSUREF(1),
Opcode.CALLC(1),
Opcode.RETURN(1),
],
stack = [True, False, False]
stack = [True, False]
)
print(ret)

View file

@ -11,12 +11,11 @@ context (a virtual machine) which DOES have an easily introspected and serialize
import sys
assert sys.version_info > (3, 10, 0), "`match` support is required"
from copy import deepcopy
from .isa import FunctionRef, Opcode
from .isa import FunctionRef, Opcode, Closure
def rotate(l):
@ -183,8 +182,8 @@ class Interpreter(object):
sig = stack.pop()
if not isinstance(sig, FunctionRef):
_error("CALLF requires a funref at top of stack")
if not n == len(sig.args):
_error("CALLF target violation; not enough arguments provided")
if n != len(sig.args):
_error("CALLF target violation; argument count missmatch")
if n > len(stack):
_error("Stack size violation")
@ -196,6 +195,47 @@ class Interpreter(object):
stack = stack.call(sig, ip)
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 _:
raise Exception(f"Unhandled interpreter state {op}")

View file

@ -1,26 +1,26 @@
"""The instruction set for Shogoth."""
from typing import NamedTuple
import typing as t
from .typing import FunctionRef
from .typing import *
class Opcode:
####################################################################################################
# Logic
####################################################################################################
class TRUE(NamedTuple):
class TRUE(t.NamedTuple):
"""() -> (bool)
Push the constant TRUE onto the stack.
"""
class FALSE(NamedTuple):
class FALSE(t.NamedTuple):
"""() -> (bool)
Push the constant FALSE onto the stack.
"""
class IF(NamedTuple):
class IF(t.NamedTuple):
"""(bool) -> ()
Branch to another point if the top item of the stack is TRUE.
Otherwise fall through.
@ -33,21 +33,21 @@ class Opcode:
####################################################################################################
# Stack manipulation
####################################################################################################
class DUP(NamedTuple):
class DUP(t.NamedTuple):
"""(A, B, ...) -> (A, B, ...)
Duplicate the top N items of the stack.
"""
nargs: int = 1
class ROT(NamedTuple):
class ROT(t.NamedTuple):
"""(A, B, ... Z) -> (Z, A, B, ...)
Rotate the top N elements of the stack.
"""
nargs: int = 2
class DROP(NamedTuple):
class DROP(t.NamedTuple):
"""(*) -> ()
Drop the top N items of the stack.
"""
@ -57,7 +57,7 @@ class Opcode:
####################################################################################################
# Functional abstraction
####################################################################################################
class CALLS(NamedTuple):
class CALLS(t.NamedTuple):
"""(... A) -> (... B)
Call [static]
@ -74,7 +74,7 @@ class Opcode:
funref: str
class RETURN(NamedTuple):
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.
@ -87,7 +87,7 @@ class Opcode:
nargs: int
class GOTO(NamedTuple):
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.
@ -97,14 +97,14 @@ class Opcode:
target: int
anywhere: bool = False
class FUNREF(NamedTuple):
class FUNREF(t.NamedTuple):
"""() -> (`FUNREF<... A to ... B>`)
Construct a reference to a static codepoint.
"""
funref: str
class CALLF(NamedTuple):
class CALLF(t.NamedTuple):
"""(`FUNREF<... A to ... B>`, ... A) -> (... B)
Call [funref]
@ -117,19 +117,20 @@ class Opcode:
nargs: int = 0
class CLOSUREF(NamedTuple):
class CLOSUREF(t.NamedTuple):
"""(`FUNREF<A, ... B to ... C>`, A) -> (`CLOSURE<... B to ... C>`)
Construct a closure over the function reference at the top of the stack.
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>`)
Further close over the closure at the top of the stack.
This may produce nullary closures.
"""
class CALLC(NamedTuple):
class CALLC(t.NamedTuple):
"""(`CLOSURE<... A to ... B>`, ... A) -> (... B)
Call [closure]
@ -145,7 +146,7 @@ class Opcode:
####################################################################################################
# Structures
####################################################################################################
class STRUCT(NamedTuple):
class STRUCT(t.NamedTuple):
"""(*) -> (T)
Consume the top N items of the stack, producing a struct of the type `structref`.
@ -155,14 +156,14 @@ class Opcode:
structref: str
nargs: int
class FLOAD(NamedTuple):
class FLOAD(t.NamedTuple):
"""(A) -> (B)
Consume the struct reference at the top of the stack, producing the value of the referenced field.
"""
fieldref: str
class FSTORE(NamedTuple):
class FSTORE(t.NamedTuple):
"""(A) -> (B)
Consume the struct reference at the top of the stack, producing the value of the referenced field.
"""
@ -172,7 +173,7 @@ class Opcode:
####################################################################################################
# Arrays
####################################################################################################
class ARRAY(NamedTuple):
class ARRAY(t.NamedTuple):
"""(*) -> (ARRAY<Y>)
Consume the top N items of the stack, producing an array of the type `typeref`.
"""
@ -180,13 +181,13 @@ class Opcode:
typeref: str
nargs: int
class ALOAD(NamedTuple):
class ALOAD(t.NamedTuple):
"""(NAT, ARRAY<T>) -> (T)
Consume a reference to an array and an index, producing the value at that index.
FIXME: Or a fault/signal.
"""
class ASTORE(NamedTuple):
class ASTORE(t.NamedTuple):
"""(T, NAT, ARRAY<T>) -> (ARRAY<T>)
Consume a value T, storing it at an index in the given array.
Produces the updated array as the top of stack.
@ -205,7 +206,7 @@ class Opcode:
####################################################################################################
class Module(NamedTuple):
class Module(t.NamedTuple):
opcodes: list = []
functions: dict = {}
types: dict = {}

View file

@ -75,3 +75,8 @@ class FunctionSignature(t.NamedTuple):
cls.parse_list(args),
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 ichor import *
from ichor.isa import Opcode
import pytest
@ -44,6 +43,7 @@ def test_and(vm, stack, ret):
def test_xor2(vm, stack, ret):
assert vm.run([Opcode.CALLS(XOR2), Opcode.RETURN(1)], stack = stack) == ret
@pytest.mark.parametrize("stack,ret", [
[[False, False, False], [False]],
[[True, False, False], [True]],
@ -69,3 +69,18 @@ def test_funref(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
@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