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 .keyword import Keyword
|
||||||
from .symbol import Symbol
|
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):
|
def __init__(self, bootstrap_module):
|
||||||
self.bootstrap = bootstrap_module
|
self.bootstrap = bootstrap_module
|
||||||
|
|
||||||
def run(self, opcodes):
|
def run(self, opcodes, stack=[]):
|
||||||
"""Directly interpret some opcodes in the configured environment."""
|
"""Directly interpret some opcodes in the configured environment."""
|
||||||
|
|
||||||
stack = Stackframe()
|
stack = Stackframe(stack=stack)
|
||||||
mod = self.bootstrap.copy()
|
mod = self.bootstrap.copy()
|
||||||
mod.define_function(";<entry>;;", opcodes)
|
mod.define_function(";<entry>;;", opcodes)
|
||||||
stack.ip = mod.functions[";<entry>;;"]
|
stack.ip = mod.functions[";<entry>;;"]
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
from shogoth.types import Function, FunctionSignature
|
||||||
|
|
||||||
|
|
||||||
class Opcode:
|
class Opcode:
|
||||||
class TRUE(NamedTuple):
|
class TRUE(NamedTuple):
|
||||||
|
@ -51,6 +53,8 @@ class Opcode:
|
||||||
Branch to `target` pushing the current point onto the call stack.
|
Branch to `target` pushing the current point onto the call stack.
|
||||||
The callee will see a stack containg only the provided `nargs`.
|
The callee will see a stack containg only the provided `nargs`.
|
||||||
A subsequent RETURN will return execution to the next point.
|
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
|
funref: str
|
||||||
|
@ -60,6 +64,9 @@ class Opcode:
|
||||||
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.
|
||||||
All other values on the stack will be discarded.
|
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.
|
If the call stack is empty, `RETURN` will exit the interpreter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -68,6 +75,8 @@ class Opcode:
|
||||||
class GOTO(NamedTuple):
|
class GOTO(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.
|
||||||
|
Branching does NOT update the name or module of the current function.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
target: int
|
target: int
|
||||||
|
@ -75,11 +84,13 @@ class Opcode:
|
||||||
|
|
||||||
class STRUCT(NamedTuple):
|
class STRUCT(NamedTuple):
|
||||||
"""(*) -> (T)
|
"""(*) -> (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
|
structref: str
|
||||||
|
nargs: int
|
||||||
|
|
||||||
class FIELD(NamedTuple):
|
class FIELD(NamedTuple):
|
||||||
"""(A) -> (B)
|
"""(A) -> (B)
|
||||||
|
@ -89,29 +100,6 @@ class Opcode:
|
||||||
fieldref: str
|
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):
|
class Module(NamedTuple):
|
||||||
opcodes: list = []
|
opcodes: list = []
|
||||||
functions: dict = {}
|
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
|
import pytest
|
||||||
from shogoth.vm import *
|
from shogoth.vm import *
|
||||||
|
|
||||||
|
from .fixtures import * # noqa
|
||||||
@pytest.fixture
|
|
||||||
def vm():
|
|
||||||
return Interpreter(BOOTSTRAP)
|
|
||||||
|
|
||||||
|
|
||||||
def test_true(vm):
|
def test_true(vm):
|
||||||
|
@ -67,110 +64,6 @@ def test_drop(vm):
|
||||||
]) == [True]
|
]) == [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):
|
def test_dup_too_many(vm):
|
||||||
with pytest.raises(InterpreterError):
|
with pytest.raises(InterpreterError):
|
||||||
vm.run([Opcode.DUP(1)])
|
vm.run([Opcode.DUP(1)])
|
||||||
|
|
Loading…
Reference in a new issue