From 99f456e0ee1c967fb439f0ff90b3f67282e29cfc Mon Sep 17 00:00:00 2001 From: Reid 'arrdem' McKenzie Date: Tue, 9 Aug 2022 09:39:33 -0600 Subject: [PATCH] Renaming variant operations --- .../shoggoth/src/python/ichor/__init__.py | 12 --- .../shoggoth/src/python/ichor/assembler.py | 17 ++++- .../shoggoth/src/python/ichor/bootstrap.py | 14 ++-- .../python/ichor/{impl.py => interpreter.py} | 17 +++-- projects/shoggoth/src/python/ichor/isa.py | 74 ++++++++++--------- projects/shoggoth/src/python/ichor/state.py | 6 +- .../shoggoth/src/python/shoggoth/analyzer.py | 2 +- .../shoggoth/test/python/ichor/fixtures.py | 3 +- .../test/python/ichor/test_assembler.py | 3 +- .../test/python/ichor/test_bootstrap.py | 5 +- .../test/python/ichor/test_interpreter.py | 26 ++++--- .../shoggoth/test/python/ichor/test_state.py | 2 +- 12 files changed, 98 insertions(+), 83 deletions(-) rename projects/shoggoth/src/python/ichor/{impl.py => interpreter.py} (95%) diff --git a/projects/shoggoth/src/python/ichor/__init__.py b/projects/shoggoth/src/python/ichor/__init__.py index 375fa7c..e69de29 100644 --- a/projects/shoggoth/src/python/ichor/__init__.py +++ b/projects/shoggoth/src/python/ichor/__init__.py @@ -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 diff --git a/projects/shoggoth/src/python/ichor/assembler.py b/projects/shoggoth/src/python/ichor/assembler.py index 0088003..688a931 100644 --- a/projects/shoggoth/src/python/ichor/assembler.py +++ b/projects/shoggoth/src/python/ichor/assembler.py @@ -61,8 +61,8 @@ class FuncBuilder(object): case isa.GOTO(isa.Label(_) as l): assembled.append(isa.GOTO(labels[l])) - case isa.VTEST(isa.Label(_) as l): - assembled.append(isa.VTEST(labels[l])) + case isa.ATEST(isa.Label(_) as l): + assembled.append(isa.ATEST(labels[l])) case o: assembled.append(o) @@ -84,7 +84,18 @@ class LocalBuilder(FuncBuilder): self._labels = {} 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): pass diff --git a/projects/shoggoth/src/python/ichor/bootstrap.py b/projects/shoggoth/src/python/ichor/bootstrap.py index 494eaff..eaf5b47 100644 --- a/projects/shoggoth/src/python/ichor/bootstrap.py +++ b/projects/shoggoth/src/python/ichor/bootstrap.py @@ -25,20 +25,20 @@ NOT1 = BOOTSTRAP.define_function( isa.TYPEREF(), # a isa.DUP(), isa.IDENTIFIERC("true"), - isa.VARIANTREF(), # a + isa.ARMREF(), # a isa.DUP(), isa.SLOT(0), isa.ROT(2), - isa.VTEST(11), + isa.ATEST(11), - isa.VARIANT(0), - isa.RETURN(1), + isa.ARM(0), + isa.RETURN(), isa.DROP(1), isa.IDENTIFIERC("false"), - isa.VARIANTREF(), - isa.VARIANT(0), - isa.RETURN(1), + isa.ARMREF(), + isa.ARM(0), + isa.RETURN(), ], ) diff --git a/projects/shoggoth/src/python/ichor/impl.py b/projects/shoggoth/src/python/ichor/interpreter.py similarity index 95% rename from projects/shoggoth/src/python/ichor/impl.py rename to projects/shoggoth/src/python/ichor/interpreter.py index 0b4f930..ed372bb 100644 --- a/projects/shoggoth/src/python/ichor/impl.py +++ b/projects/shoggoth/src/python/ichor/interpreter.py @@ -1,6 +1,6 @@ #!/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 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): - """A shit simple instruction pointer based interpreter.""" + """A shit .interpretere instruction pointer based interpreter.""" def __init__(self, bootstrap_module: Module): self.bootstrap = bootstrap_module @@ -89,7 +89,7 @@ class Interpreter(object): stackframe.push(TypeRef(id.name)) - case isa.VARIANTREF(): + case isa.ARMREF(): id: Identifier = stackframe.pop() if not isinstance(id, Identifier): _error("VARIANTREF consumes an identifier and a typeref") @@ -104,7 +104,7 @@ class Interpreter(object): stackframe.push(VariantRef(t, id.name)) - case isa.VARIANT(n): + case isa.ARM(n): armref: VariantRef = stackframe.pop() if not isinstance(armref, VariantRef): _error("VARIANT must be given a valid constructor reference") @@ -122,7 +122,7 @@ class Interpreter(object): stackframe.drop(n) stackframe.push(v) - case isa.VTEST(n): + case isa.ATEST(n): armref: VariantRef = stackframe.pop() if not isinstance(armref, VariantRef): _error("VTEST must be given a variant reference") @@ -194,7 +194,8 @@ class Interpreter(object): stackframe = stackframe.call(fun, ip) continue - case isa.RETURN(n): + case isa.RETURN(): + n = 1 # FIXME: clean this up if (n > len(stackframe)): _error("Stack size violation") @@ -265,6 +266,10 @@ class Interpreter(object): stackframe = stackframe.call(fun, ip) continue + case isa.BREAK(): + # FIXME: let users override this / set custom handlers + return stackframe._stack + case _: raise Exception(f"Unhandled interpreter state {op}") diff --git a/projects/shoggoth/src/python/ichor/isa.py b/projects/shoggoth/src/python/ichor/isa.py index f030741..3667ffc 100644 --- a/projects/shoggoth/src/python/ichor/isa.py +++ b/projects/shoggoth/src/python/ichor/isa.py @@ -20,16 +20,17 @@ class Opcode(ABC): class GOTO(Opcode): """() -> () - 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. + 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 -#################################################################################################### +################################################################################ # https://wiki.laptop.org/go/Forth_stack_operators # 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 @@ -70,16 +71,17 @@ class DROP(Opcode): class SLOT(Opcode): """(..., A) -> (A, ..., A) - 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. + 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. """ target: int -#################################################################################################### +################################################################################ # Functional abstraction -#################################################################################################### +################################################################################ @dataclass class IDENTIFIERC(Opcode): @@ -123,23 +125,24 @@ class CALLF(Opcode): class RETURN(Opcode): """(... 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. + 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. + 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 - @dataclass class CLOSUREF(Opcode): """(`FUNREF`, 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): """(`CLOSURE`, 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] - Make a dynamic call to the closure at the top of stack. - The callee will see a stack containg only the provided `nargs` and closed-overs. - A subsequent RETURN will return execution to the next point. + Make a dynamic call to the closure at the top of stack. The callee will see + a stack containg only the provided `nargs` and closed-overs. A subsequent + RETURN will return execution to the next point. Executing a `CALL` pushes the name and module path of the current function. @@ -172,9 +176,9 @@ class CALLC(Opcode): nargs: int = 0 -#################################################################################################### +################################################################################ # Structures -#################################################################################################### +################################################################################ # FIXME: This lacks any sort of way to do dynamic type/field references @@ -188,8 +192,8 @@ class TYPEREF(Opcode): @dataclass -class VARIANTREF(Opcode): - """(IDENTIFIER, TYPEREF) -> (VARIANTREF) +class ARMREF(Opcode): + """(TYPEREF, IDENTIFIER) -> (VARIANTREF) Produce a VARIANTREF to an 'arm' of the given variant type. @@ -197,23 +201,25 @@ class VARIANTREF(Opcode): @dataclass -class VARIANT(Opcode): - """(VARIANTREF, ...) -> (B) +class ARM(Opcode): + """(ARMTREF, ...) -> (B) 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 name and module path of the current function MUST match the name and module path of `VARIANTREF`. - The arity of this opcode MUST match the arity of the arm. - The signature of the arm MUST match the signature fo the top N of the stack. + The name and module path of the current function MUST match the name and + module path of `VARIANTREF`. The arity of this opcode MUST match the arity + of the arm. The signature of the arm MUST match the signature fo the top N + of the stack. + """ nargs: int = 0 @dataclass -class VTEST(Opcode): - """(VARIANTREF, B) -> () +class ATEST(Opcode): + """(ARMREF, B) -> () Test whether B is a given arm of a variant A . If it is, branch to the given target. @@ -225,12 +231,12 @@ class VTEST(Opcode): @dataclass -class VLOAD(Opcode): - """(VARIANTREF, B) -> (A) +class ALOAD(Opcode): + """(ARMREF, B) -> (A) - Load the value of the variant arm. - VLOAD errors (undefined) if B is not within the variant. - VLOAD errors (undefined) if the value in B is not an A - use VTEST as needed. + Load the value of the variant arm. VLOAD errors (undefined) if B is not + within the variant. VLOAD errors (undefined) if the value in B is not an A - + use VTEST as needed. """ diff --git a/projects/shoggoth/src/python/ichor/state.py b/projects/shoggoth/src/python/ichor/state.py index 54985bd..142f72d 100644 --- a/projects/shoggoth/src/python/ichor/state.py +++ b/projects/shoggoth/src/python/ichor/state.py @@ -158,7 +158,7 @@ class Variant(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 - # 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 frag: t.List[t.Any] @@ -189,10 +189,10 @@ class Module(t.NamedTuple): def translate(start: int, end: int, i: isa.Opcode): # FIXME: Consolidate bounds checks somehow match i: - case isa.VTEST(t): + case isa.ATEST(t): d = t + start assert start <= d < end - return isa.VTEST(d) + return isa.ATEST(d) case isa.GOTO(t): d = t + start diff --git a/projects/shoggoth/src/python/shoggoth/analyzer.py b/projects/shoggoth/src/python/shoggoth/analyzer.py index c611ee3..11e660e 100644 --- a/projects/shoggoth/src/python/shoggoth/analyzer.py +++ b/projects/shoggoth/src/python/shoggoth/analyzer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -"""The implementation of Shogoth's lexical analyzer.""" +"""The.interpreterementation of Shogoth's lexical analyzer.""" from abc import ABC from dataclasses import dataclass diff --git a/projects/shoggoth/test/python/ichor/fixtures.py b/projects/shoggoth/test/python/ichor/fixtures.py index c768183..84f9119 100644 --- a/projects/shoggoth/test/python/ichor/fixtures.py +++ b/projects/shoggoth/test/python/ichor/fixtures.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 -from ichor import BOOTSTRAP, Interpreter +from ichor.bootstrap import BOOTSTRAP +from ichor.interpreter import Interpreter import pytest diff --git a/projects/shoggoth/test/python/ichor/test_assembler.py b/projects/shoggoth/test/python/ichor/test_assembler.py index 63a7eb0..f68ef76 100644 --- a/projects/shoggoth/test/python/ichor/test_assembler.py +++ b/projects/shoggoth/test/python/ichor/test_assembler.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 -from ichor import FuncBuilder, isa +from ichor import isa +from ichor.assembler import FuncBuilder import pytest diff --git a/projects/shoggoth/test/python/ichor/test_bootstrap.py b/projects/shoggoth/test/python/ichor/test_bootstrap.py index a805e03..f42e7b7 100644 --- a/projects/shoggoth/test/python/ichor/test_bootstrap.py +++ b/projects/shoggoth/test/python/ichor/test_bootstrap.py @@ -2,7 +2,8 @@ from .fixtures import * # noqa -from ichor import FALSE, isa, NOT1, TRUE +from ichor import isa +from ichor.bootstrap import FALSE, NOT1, TRUE import pytest @@ -15,7 +16,7 @@ def test_not(vm, stack, ret): isa.IDENTIFIERC(NOT1), isa.FUNREF(), isa.CALLF(1), - isa.RETURN(1) + isa.RETURN() ], stack = stack) == ret diff --git a/projects/shoggoth/test/python/ichor/test_interpreter.py b/projects/shoggoth/test/python/ichor/test_interpreter.py index f5edffd..60460e9 100644 --- a/projects/shoggoth/test/python/ichor/test_interpreter.py +++ b/projects/shoggoth/test/python/ichor/test_interpreter.py @@ -4,47 +4,49 @@ Tests coverign the VM interpreter from .fixtures import * # noqa -from ichor import ( +from ichor.bootstrap import ( FALSE, - InterpreterError, - isa, TRUE, ) +from ichor import isa +from ichor.interpreter import InterpreterError import pytest def test_return(vm): - assert vm.run([isa.RETURN(0)], stack=[TRUE, FALSE]) == [] - assert vm.run([isa.RETURN(1)], stack=[TRUE, FALSE]) == [TRUE] - assert vm.run([isa.RETURN(2)], stack=[TRUE, FALSE]) == [TRUE, FALSE] + with pytest.raises(InterpreterError): + vm.run([isa.RETURN()], stack=[]) + + 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): - assert vm.run([isa.DUP(1), isa.RETURN(3)], 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(1), isa.BREAK()], stack=[FALSE, TRUE]) == [FALSE, TRUE, TRUE] + assert vm.run([isa.DUP(2), isa.BREAK()], stack=[FALSE, TRUE]) == [FALSE, TRUE, FALSE, TRUE] def test_rot(vm): assert vm.run([ isa.ROT(2), - isa.RETURN(2) + isa.BREAK() ], stack=[FALSE, TRUE]) == [TRUE, FALSE] assert vm.run([ isa.ROT(2), - isa.RETURN(5) + isa.BREAK() ], stack=[TRUE, TRUE, TRUE, FALSE, TRUE]) == [TRUE, TRUE, TRUE, TRUE, FALSE] assert vm.run([ isa.ROT(3), - isa.RETURN(3) + isa.BREAK() ], stack=[FALSE, TRUE, FALSE]) == [FALSE, FALSE, TRUE] def test_drop(vm): assert vm.run([ isa.DROP(1), - isa.RETURN(1) + isa.BREAK() ], stack=[TRUE, FALSE]) == [TRUE] diff --git a/projects/shoggoth/test/python/ichor/test_state.py b/projects/shoggoth/test/python/ichor/test_state.py index 7ea65f9..884457c 100644 --- a/projects/shoggoth/test/python/ichor/test_state.py +++ b/projects/shoggoth/test/python/ichor/test_state.py @@ -2,7 +2,7 @@ """Tests covering the Ichor ISA and state model.""" -from ichor.impl import Stackframe +from ichor.interpreter import Stackframe import pytest