Happy hacking; setting up for structs
This commit is contained in:
parent
ef3f3f5c1a
commit
4396ec6496
8 changed files with 200 additions and 137 deletions
|
@ -3,5 +3,100 @@
|
|||
from .keyword import Keyword
|
||||
from .symbol import Symbol
|
||||
|
||||
from abc import ABC
|
||||
from typing import NamedTuple, List, Mapping
|
||||
|
||||
__all__ = ["Keyword", "Symbol"]
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class TypeExpr(ABC):
|
||||
"""A type expression"""
|
||||
|
||||
bindings: []
|
||||
|
||||
|
||||
class TypeVariable(TypeExpr):
|
||||
name: str
|
||||
id: UUID = uuid4()
|
||||
|
||||
|
||||
class PrimitiveExpr(object):
|
||||
class Nat(TypeExpr): pass
|
||||
class N8(Nat): pass
|
||||
class N16(Nat): pass
|
||||
class N32(Nat): pass
|
||||
class N64(Nat): pass
|
||||
class N128(Nat): pass
|
||||
class N256(Nat): pass
|
||||
class N512(Nat): pass
|
||||
|
||||
class Unsigned(TypeExpr): pass
|
||||
class U8(Unsigned): pass
|
||||
class U16(Unsigned): pass
|
||||
class U32(Unsigned): pass
|
||||
class U64(Unsigned): pass
|
||||
class U128(Unsigned): pass
|
||||
class U256(Unsigned): pass
|
||||
class U512(): pass
|
||||
|
||||
class Integer(TypeExpr): pass
|
||||
class I8(Integer): pass
|
||||
class I16(Integer): pass
|
||||
class I32(Integer): pass
|
||||
class I64(Integer): pass
|
||||
class I128(Integer): pass
|
||||
class I256(Integer): pass
|
||||
class I512(Integer): pass
|
||||
|
||||
class Floating(TypeExpr): pass
|
||||
class F8(Floating): pass
|
||||
class F16(Floating): pass
|
||||
class F32(Floating): pass
|
||||
class F64(Floating): pass
|
||||
class F128(Floating): pass
|
||||
class F256(Floating): pass
|
||||
class F512(Floating): pass
|
||||
|
||||
|
||||
|
||||
class ArrayExpr(TypeExpr):
|
||||
child: TypeExpr
|
||||
|
||||
|
||||
class SumExpr(TypeExpr):
|
||||
children: List[TypeExpr]
|
||||
|
||||
|
||||
class ProductExpr(TypeExpr):
|
||||
children: Mapping[str, TypeExpr]
|
||||
|
||||
|
||||
####################################################################################################
|
||||
####################################################################################################
|
||||
####################################################################################################
|
||||
|
||||
class Function(NamedTuple):
|
||||
"""The type of a function; a subset of its signature."""
|
||||
|
||||
|
||||
class FunctionSignature(NamedTuple):
|
||||
raw: str
|
||||
type_params: list
|
||||
name: str
|
||||
args: list
|
||||
ret: list
|
||||
|
||||
@staticmethod
|
||||
def parse_list(l):
|
||||
return [e for e in l.split(",") if e]
|
||||
|
||||
@classmethod
|
||||
def parse(cls, raw: str):
|
||||
vars, name, args, ret = raw.split(";")
|
||||
return cls(
|
||||
raw,
|
||||
cls.parse_list(vars),
|
||||
name,
|
||||
cls.parse_list(args),
|
||||
cls.parse_list(ret)
|
||||
)
|
||||
|
|
30
projects/shogoth/src/python/shogoth/types/function.py
Normal file
30
projects/shogoth/src/python/shogoth/types/function.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class FunctionSignature(NamedTuple):
|
||||
raw: str
|
||||
type_params: list
|
||||
name: str
|
||||
args: list
|
||||
ret: list
|
||||
|
||||
@staticmethod
|
||||
def parse_list(l):
|
||||
return [e for e in l.split(",") if e]
|
||||
|
||||
@classmethod
|
||||
def parse(cls, raw: str):
|
||||
vars, name, args, ret = raw.split(";")
|
||||
return cls(
|
||||
raw,
|
||||
cls.parse_list(vars),
|
||||
name,
|
||||
cls.parse_list(args),
|
||||
cls.parse_list(ret)
|
||||
)
|
||||
|
||||
|
||||
class Function(NamedTuple):
|
||||
"""The type of a function; a subset of its signature."""
|
|
@ -1,2 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
# noqa
|
||||
|
||||
from .isa import * # noqa
|
||||
from .bootstrap import * # noqa
|
||||
from .impl import * # noqa
|
||||
|
|
|
@ -83,10 +83,10 @@ class Interpreter(object):
|
|||
def __init__(self, bootstrap_module):
|
||||
self.bootstrap = bootstrap_module
|
||||
|
||||
def run(self, opcodes):
|
||||
def run(self, opcodes, stack=[]):
|
||||
"""Directly interpret some opcodes in the configured environment."""
|
||||
|
||||
stack = Stackframe()
|
||||
stack = Stackframe(stack=stack)
|
||||
mod = self.bootstrap.copy()
|
||||
mod.define_function(";<entry>;;", opcodes)
|
||||
stack.ip = mod.functions[";<entry>;;"]
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
from typing import NamedTuple
|
||||
|
||||
from shogoth.types import Function, FunctionSignature
|
||||
|
||||
|
||||
class Opcode:
|
||||
class TRUE(NamedTuple):
|
||||
|
@ -51,6 +53,8 @@ class Opcode:
|
|||
Branch to `target` pushing the current point onto the call stack.
|
||||
The callee will see a stack containg only the provided `nargs`.
|
||||
A subsequent RETURN will return execution to the next point.
|
||||
|
||||
Executing a `CALL` pushes the name and module path of the current function.
|
||||
"""
|
||||
|
||||
funref: str
|
||||
|
@ -60,6 +64,9 @@ class Opcode:
|
|||
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.
|
||||
|
||||
If the call stack is empty, `RETURN` will exit the interpreter.
|
||||
"""
|
||||
|
||||
|
@ -68,6 +75,8 @@ class Opcode:
|
|||
class GOTO(NamedTuple):
|
||||
"""() -> ()
|
||||
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
|
||||
|
@ -75,11 +84,13 @@ class Opcode:
|
|||
|
||||
class STRUCT(NamedTuple):
|
||||
"""(*) -> (T)
|
||||
Consume the top N items of the stack, producing a struct.
|
||||
Consume the top N items of the stack, producing a struct of the type `structref`.
|
||||
|
||||
The name and module path of the current function MUST match the name and module path of `structref`.
|
||||
"""
|
||||
|
||||
nargs: int
|
||||
structref: str
|
||||
nargs: int
|
||||
|
||||
class FIELD(NamedTuple):
|
||||
"""(A) -> (B)
|
||||
|
@ -89,29 +100,6 @@ class Opcode:
|
|||
fieldref: str
|
||||
|
||||
|
||||
class FunctionSignature(NamedTuple):
|
||||
raw: str
|
||||
type_params: list
|
||||
name: str
|
||||
args: list
|
||||
ret: list
|
||||
|
||||
@staticmethod
|
||||
def parse_list(l):
|
||||
return [e for e in l.split(",") if e]
|
||||
|
||||
@classmethod
|
||||
def parse(cls, raw: str):
|
||||
vars, name, args, ret = raw.split(";")
|
||||
return cls(
|
||||
raw,
|
||||
cls.parse_list(vars),
|
||||
name,
|
||||
cls.parse_list(args),
|
||||
cls.parse_list(ret)
|
||||
)
|
||||
|
||||
|
||||
class Module(NamedTuple):
|
||||
opcodes: list = []
|
||||
functions: dict = {}
|
||||
|
|
10
projects/shogoth/test/python/shogoth/vm/fixtures.py
Normal file
10
projects/shogoth/test/python/shogoth/vm/fixtures.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import pytest
|
||||
from shogoth.vm import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def vm():
|
||||
return Interpreter(BOOTSTRAP)
|
44
projects/shogoth/test/python/shogoth/vm/test_bootstrap.py
Normal file
44
projects/shogoth/test/python/shogoth/vm/test_bootstrap.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import pytest
|
||||
from shogoth.vm import *
|
||||
|
||||
from .fixtures import * # noqa
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stack,ret', [
|
||||
[[True], [False]],
|
||||
[[True], [False]],
|
||||
])
|
||||
def test_not(vm, stack, ret):
|
||||
assert vm.run([Opcode.CALL(NOT)], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stack,ret', [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [True]],
|
||||
[[False, True], [True]],
|
||||
[[True, True], [True]],
|
||||
])
|
||||
def test_or(vm, stack, ret):
|
||||
assert vm.run([Opcode.CALL(OR)], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stack,ret', [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [False]],
|
||||
[[False, True], [False]],
|
||||
[[True, True], [True]],
|
||||
])
|
||||
def test_and(vm, stack, ret):
|
||||
assert vm.run([Opcode.CALL(AND)], stack = stack) == ret
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stack,ret', [
|
||||
[[False, False], [False]],
|
||||
[[True, False], [True]],
|
||||
[[False, True], [True]],
|
||||
[[True, True], [False]],
|
||||
])
|
||||
def test_xor(vm, stack, ret):
|
||||
assert vm.run([Opcode.CALL(XOR)], stack = stack) == ret
|
|
@ -5,10 +5,7 @@ Tests coverign the VM interpreter
|
|||
import pytest
|
||||
from shogoth.vm import *
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vm():
|
||||
return Interpreter(BOOTSTRAP)
|
||||
from .fixtures import * # noqa
|
||||
|
||||
|
||||
def test_true(vm):
|
||||
|
@ -67,110 +64,6 @@ def test_drop(vm):
|
|||
]) == [True]
|
||||
|
||||
|
||||
def test_not(vm):
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(NOT),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(NOT),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
|
||||
def test_or(vm):
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(OR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(OR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(OR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(OR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
|
||||
def test_and(vm):
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(AND),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(AND),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(AND),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(AND),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
|
||||
def test_xor(vm):
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(XOR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.FALSE(),
|
||||
Opcode.CALL(XOR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.FALSE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(XOR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [True]
|
||||
|
||||
assert vm.run([
|
||||
Opcode.TRUE(),
|
||||
Opcode.TRUE(),
|
||||
Opcode.CALL(XOR),
|
||||
Opcode.RETURN(1)
|
||||
]) == [False]
|
||||
|
||||
|
||||
def test_dup_too_many(vm):
|
||||
with pytest.raises(InterpreterError):
|
||||
vm.run([Opcode.DUP(1)])
|
||||
|
|
Loading…
Reference in a new issue