Renaming variant operations

This commit is contained in:
Reid 'arrdem' McKenzie 2022-08-09 09:39:33 -06:00
parent a7b236fa5f
commit 2557ff1e4f
12 changed files with 98 additions and 83 deletions

View file

@ -1,12 +0,0 @@
#!/usr/bin/env python
"""
Import everything out of the Ichor modules.
FIXME: A real public API here would be great, but somewhat un-pythonic.
"""
from ichor.bootstrap import * # noqa
from ichor.impl import * # noqa
from ichor.isa import * # noqa
from ichor.typing import * # noqa

View file

@ -61,8 +61,8 @@ class FuncBuilder(object):
case isa.GOTO(isa.Label(_) as l): case isa.GOTO(isa.Label(_) as l):
assembled.append(isa.GOTO(labels[l])) assembled.append(isa.GOTO(labels[l]))
case isa.VTEST(isa.Label(_) as l): case isa.ATEST(isa.Label(_) as l):
assembled.append(isa.VTEST(labels[l])) assembled.append(isa.ATEST(labels[l]))
case o: case o:
assembled.append(o) assembled.append(o)
@ -84,7 +84,18 @@ class LocalBuilder(FuncBuilder):
self._labels = {} self._labels = {}
def _write(self, op): def _write(self, op):
pass super()._write(op)
match op:
case isa.DROP(n):
self._stack -= n
case isa.DUP(n):
self._stack += n
case isa.ROT(_):
pass
case _:
self._stack -= getattr(op, 'nargs', 0)
self._stack += 1
def write_local(self, label: isa.Label): def write_local(self, label: isa.Label):
pass pass

View file

@ -25,20 +25,20 @@ NOT1 = BOOTSTRAP.define_function(
isa.TYPEREF(), # <typeref bool> a isa.TYPEREF(), # <typeref bool> a
isa.DUP(), isa.DUP(),
isa.IDENTIFIERC("true"), isa.IDENTIFIERC("true"),
isa.VARIANTREF(), # <variantref true:bool> <typeref bool> a isa.ARMREF(), # <variantref true:bool> <typeref bool> a
isa.DUP(), isa.DUP(),
isa.SLOT(0), isa.SLOT(0),
isa.ROT(2), isa.ROT(2),
isa.VTEST(11), isa.ATEST(11),
isa.VARIANT(0), isa.ARM(0),
isa.RETURN(1), isa.RETURN(),
isa.DROP(1), isa.DROP(1),
isa.IDENTIFIERC("false"), isa.IDENTIFIERC("false"),
isa.VARIANTREF(), isa.ARMREF(),
isa.VARIANT(0), isa.ARM(0),
isa.RETURN(1), isa.RETURN(),
], ],
) )

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""The Ichor VM implementation. """The Ichor VM.interpreterementation.
The whole point of Shoggoth is that program executions are checkpointable and restartable. This requires that rather than The whole point of Shoggoth is that program executions are checkpointable and restartable. This requires that rather than
using a traditional recursive interpreter which is difficult to snapshot, interpretation in shoggoth occur within a using a traditional recursive interpreter which is difficult to snapshot, interpretation in shoggoth occur within a
@ -35,7 +35,7 @@ class InterpreterError(Exception):
class Interpreter(object): class Interpreter(object):
"""A shit simple instruction pointer based interpreter.""" """A shit .interpretere instruction pointer based interpreter."""
def __init__(self, bootstrap_module: Module): def __init__(self, bootstrap_module: Module):
self.bootstrap = bootstrap_module self.bootstrap = bootstrap_module
@ -89,7 +89,7 @@ class Interpreter(object):
stackframe.push(TypeRef(id.name)) stackframe.push(TypeRef(id.name))
case isa.VARIANTREF(): case isa.ARMREF():
id: Identifier = stackframe.pop() id: Identifier = stackframe.pop()
if not isinstance(id, Identifier): if not isinstance(id, Identifier):
_error("VARIANTREF consumes an identifier and a typeref") _error("VARIANTREF consumes an identifier and a typeref")
@ -104,7 +104,7 @@ class Interpreter(object):
stackframe.push(VariantRef(t, id.name)) stackframe.push(VariantRef(t, id.name))
case isa.VARIANT(n): case isa.ARM(n):
armref: VariantRef = stackframe.pop() armref: VariantRef = stackframe.pop()
if not isinstance(armref, VariantRef): if not isinstance(armref, VariantRef):
_error("VARIANT must be given a valid constructor reference") _error("VARIANT must be given a valid constructor reference")
@ -122,7 +122,7 @@ class Interpreter(object):
stackframe.drop(n) stackframe.drop(n)
stackframe.push(v) stackframe.push(v)
case isa.VTEST(n): case isa.ATEST(n):
armref: VariantRef = stackframe.pop() armref: VariantRef = stackframe.pop()
if not isinstance(armref, VariantRef): if not isinstance(armref, VariantRef):
_error("VTEST must be given a variant reference") _error("VTEST must be given a variant reference")
@ -194,7 +194,8 @@ class Interpreter(object):
stackframe = stackframe.call(fun, ip) stackframe = stackframe.call(fun, ip)
continue continue
case isa.RETURN(n): case isa.RETURN():
n = 1 # FIXME: clean this up
if (n > len(stackframe)): if (n > len(stackframe)):
_error("Stack size violation") _error("Stack size violation")
@ -265,6 +266,10 @@ class Interpreter(object):
stackframe = stackframe.call(fun, ip) stackframe = stackframe.call(fun, ip)
continue continue
case isa.BREAK():
# FIXME: let users override this / set custom handlers
return stackframe._stack
case _: case _:
raise Exception(f"Unhandled interpreter state {op}") raise Exception(f"Unhandled interpreter state {op}")

View file

@ -20,16 +20,17 @@ class Opcode(ABC):
class GOTO(Opcode): class GOTO(Opcode):
"""() -> () """() -> ()
Branch to another point within the same bytecode segment. The target MUST be within the same module range as the Branch to another point within the same bytecode segment. The target MUST be
current function. Branching does NOT update the name or module of the current function. 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
#################################################################################################### ################################################################################
# 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
@ -70,16 +71,17 @@ class DROP(Opcode):
class SLOT(Opcode): class SLOT(Opcode):
"""(..., A) -> (A, ..., A) """(..., A) -> (A, ..., A)
Copy the Nth (counting up from 0 at the bottom of the stack) item to the top of the stack. Copy the Nth (counting up from 0 at the bottom of the stack) item to the top
Intended to allow users to emulate (immutable) frame local slots for reused values. of the stack. Intended to allow users to emulate (immutable) frame local
slots for reused values.
""" """
target: int target: int
#################################################################################################### ################################################################################
# Functional abstraction # Functional abstraction
#################################################################################################### ################################################################################
@dataclass @dataclass
class IDENTIFIERC(Opcode): class IDENTIFIERC(Opcode):
@ -123,23 +125,24 @@ class CALLF(Opcode):
class RETURN(Opcode): class RETURN(Opcode):
"""(... A) -> () """(... A) -> ()
Return to the source of the last `CALL`. The returnee will see the top `nargs` values of the present stack Return to the source of the last `CALL`. The returnee will see the top
appended to theirs. All other values on the stack will be discarded. `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. 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. If the call stack is empty, `RETURN` will exit the interpreter.
""" """
nargs: int
@dataclass @dataclass
class CLOSUREF(Opcode): class CLOSUREF(Opcode):
"""(`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. This may produce nullary closures. Construct a closure over the function reference at the top of the stack.
This may produce nullary closures.
""" """
@ -150,7 +153,8 @@ class CLOSUREF(Opcode):
class CLOSUREC(Opcode): class CLOSUREC(Opcode):
"""(`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. This may produce nullary closures. Further close over the closure at the top of the stack. This may produce
nullary closures.
""" """
@ -162,9 +166,9 @@ class CALLC(Opcode):
Call [closure] Call [closure]
Make a dynamic call to the closure at the top of stack. Make a dynamic call to the closure at the top of stack. The callee will see
The callee will see a stack containg only the provided `nargs` and closed-overs. a stack containg only the provided `nargs` and closed-overs. A subsequent
A subsequent RETURN will return execution to the next point. RETURN will return execution to the next point.
Executing a `CALL` pushes the name and module path of the current function. Executing a `CALL` pushes the name and module path of the current function.
@ -172,9 +176,9 @@ class CALLC(Opcode):
nargs: int = 0 nargs: int = 0
#################################################################################################### ################################################################################
# Structures # Structures
#################################################################################################### ################################################################################
# FIXME: This lacks any sort of way to do dynamic type/field references # FIXME: This lacks any sort of way to do dynamic type/field references
@ -188,8 +192,8 @@ class TYPEREF(Opcode):
@dataclass @dataclass
class VARIANTREF(Opcode): class ARMREF(Opcode):
"""(IDENTIFIER, TYPEREF) -> (VARIANTREF) """(TYPEREF, IDENTIFIER) -> (VARIANTREF)
Produce a VARIANTREF to an 'arm' of the given variant type. Produce a VARIANTREF to an 'arm' of the given variant type.
@ -197,23 +201,25 @@ class VARIANTREF(Opcode):
@dataclass @dataclass
class VARIANT(Opcode): class ARM(Opcode):
"""(VARIANTREF<a ⊢ A ⊂ B>, ...) -> (B) """(ARMTREF<a ⊢ A ⊂ B>, ...) -> (B)
Construct an instance of an 'arm' of a variant. Construct an instance of an 'arm' of a variant.
The type of the 'arm' is considered to be the type of the whole variant. The type of the 'arm' is considered to be the type of the whole variant.
The name and module path of the current function MUST match the name and module path of `VARIANTREF`. The name and module path of the current function MUST match the name and
The arity of this opcode MUST match the arity of the arm. module path of `VARIANTREF`. The arity of this opcode MUST match the arity
The signature of the arm MUST match the signature fo the top N of the stack. of the arm. The signature of the arm MUST match the signature fo the top N
of the stack.
""" """
nargs: int = 0 nargs: int = 0
@dataclass @dataclass
class VTEST(Opcode): class ATEST(Opcode):
"""(VARIANTREF<a ⊢ A ⊂ B>, B) -> () """(ARMREF<a ⊢ A ⊂ B>, B) -> ()
Test whether B is a given arm of a variant A . Test whether B is a given arm of a variant A .
If it is, branch to the given target. If it is, branch to the given target.
@ -225,12 +231,12 @@ class VTEST(Opcode):
@dataclass @dataclass
class VLOAD(Opcode): class ALOAD(Opcode):
"""(VARIANTREF<a ⊢ A ⊂ B>, B) -> (A) """(ARMREF<a ⊢ A ⊂ B>, B) -> (A)
Load the value of the variant arm. Load the value of the variant arm. VLOAD errors (undefined) if B is not
VLOAD errors (undefined) if B is not within the variant. within the variant. VLOAD errors (undefined) if the value in B is not an A -
VLOAD errors (undefined) if the value in B is not an A - use VTEST as needed. use VTEST as needed.
""" """

View file

@ -158,7 +158,7 @@ class Variant(t.NamedTuple):
class Closure(t.NamedTuple): class Closure(t.NamedTuple):
# Note that a closure over a closure is equivalent to a single closure which extends the captured stack fragment, so # Note that a closure over a closure is equivalent to a single closure which extends the captured stack fragment, so
# there's no need for a union here as we can simply convert nested closures. # there's no need for a union here as we can .interpretery convert nested closures.
funref: FunctionRef funref: FunctionRef
frag: t.List[t.Any] frag: t.List[t.Any]
@ -189,10 +189,10 @@ class Module(t.NamedTuple):
def translate(start: int, end: int, i: isa.Opcode): def translate(start: int, end: int, i: isa.Opcode):
# FIXME: Consolidate bounds checks somehow # FIXME: Consolidate bounds checks somehow
match i: match i:
case isa.VTEST(t): case isa.ATEST(t):
d = t + start d = t + start
assert start <= d < end assert start <= d < end
return isa.VTEST(d) return isa.ATEST(d)
case isa.GOTO(t): case isa.GOTO(t):
d = t + start d = t + start

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""The implementation of Shogoth's lexical analyzer.""" """The.interpreterementation of Shogoth's lexical analyzer."""
from abc import ABC from abc import ABC
from dataclasses import dataclass from dataclasses import dataclass

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from ichor import BOOTSTRAP, Interpreter from ichor.bootstrap import BOOTSTRAP
from ichor.interpreter import Interpreter
import pytest import pytest

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from ichor import FuncBuilder, isa from ichor import isa
from ichor.assembler import FuncBuilder
import pytest import pytest

View file

@ -2,7 +2,8 @@
from .fixtures import * # noqa from .fixtures import * # noqa
from ichor import FALSE, isa, NOT1, TRUE from ichor import isa
from ichor.bootstrap import FALSE, NOT1, TRUE
import pytest import pytest
@ -15,7 +16,7 @@ def test_not(vm, stack, ret):
isa.IDENTIFIERC(NOT1), isa.IDENTIFIERC(NOT1),
isa.FUNREF(), isa.FUNREF(),
isa.CALLF(1), isa.CALLF(1),
isa.RETURN(1) isa.RETURN()
], stack = stack) == ret ], stack = stack) == ret

View file

@ -4,47 +4,49 @@ Tests coverign the VM interpreter
from .fixtures import * # noqa from .fixtures import * # noqa
from ichor import ( from ichor.bootstrap import (
FALSE, FALSE,
InterpreterError,
isa,
TRUE, TRUE,
) )
from ichor import isa
from ichor.interpreter import InterpreterError
import pytest import pytest
def test_return(vm): def test_return(vm):
assert vm.run([isa.RETURN(0)], stack=[TRUE, FALSE]) == [] with pytest.raises(InterpreterError):
assert vm.run([isa.RETURN(1)], stack=[TRUE, FALSE]) == [TRUE] vm.run([isa.RETURN()], stack=[])
assert vm.run([isa.RETURN(2)], stack=[TRUE, FALSE]) == [TRUE, FALSE]
assert vm.run([isa.RETURN()], stack=[FALSE, TRUE]) == [FALSE]
assert vm.run([isa.DROP(1), isa.RETURN()], stack=[TRUE, FALSE]) == [TRUE]
def test_dup(vm): def test_dup(vm):
assert vm.run([isa.DUP(1), isa.RETURN(3)], stack=[FALSE, TRUE]) == [FALSE, TRUE, TRUE] assert vm.run([isa.DUP(1), isa.BREAK()], stack=[FALSE, TRUE]) == [FALSE, TRUE, TRUE]
assert vm.run([isa.DUP(2), isa.RETURN(4)], stack=[FALSE, TRUE]) == [FALSE, TRUE, FALSE, TRUE] assert vm.run([isa.DUP(2), isa.BREAK()], stack=[FALSE, TRUE]) == [FALSE, TRUE, FALSE, TRUE]
def test_rot(vm): def test_rot(vm):
assert vm.run([ assert vm.run([
isa.ROT(2), isa.ROT(2),
isa.RETURN(2) isa.BREAK()
], stack=[FALSE, TRUE]) == [TRUE, FALSE] ], stack=[FALSE, TRUE]) == [TRUE, FALSE]
assert vm.run([ assert vm.run([
isa.ROT(2), isa.ROT(2),
isa.RETURN(5) isa.BREAK()
], stack=[TRUE, TRUE, TRUE, FALSE, TRUE]) == [TRUE, TRUE, TRUE, TRUE, FALSE] ], stack=[TRUE, TRUE, TRUE, FALSE, TRUE]) == [TRUE, TRUE, TRUE, TRUE, FALSE]
assert vm.run([ assert vm.run([
isa.ROT(3), isa.ROT(3),
isa.RETURN(3) isa.BREAK()
], stack=[FALSE, TRUE, FALSE]) == [FALSE, FALSE, TRUE] ], stack=[FALSE, TRUE, FALSE]) == [FALSE, FALSE, TRUE]
def test_drop(vm): def test_drop(vm):
assert vm.run([ assert vm.run([
isa.DROP(1), isa.DROP(1),
isa.RETURN(1) isa.BREAK()
], stack=[TRUE, FALSE]) == [TRUE] ], stack=[TRUE, FALSE]) == [TRUE]

View file

@ -2,7 +2,7 @@
"""Tests covering the Ichor ISA and state model.""" """Tests covering the Ichor ISA and state model."""
from ichor.impl import Stackframe from ichor.interpreter import Stackframe
import pytest import pytest