An evening of hacking
This commit is contained in:
parent
9b965dba05
commit
b6e1a61a85
9 changed files with 151 additions and 38 deletions
|
@ -10,3 +10,12 @@ py_project(
|
||||||
py_requirement("flask"),
|
py_requirement("flask"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zapp_binary(
|
||||||
|
name="ichor",
|
||||||
|
main = "src/python/ichor/__main__.py",
|
||||||
|
shebang="/usr/bin/env python3",
|
||||||
|
deps = [
|
||||||
|
":lib",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
>
|
>
|
||||||
> ~ Charlie Stross, "A Colder War"
|
> ~ Charlie Stross, "A Colder War"
|
||||||
|
|
||||||
|
Shoggoth is a language designed to provide highly durable, portable agents.
|
||||||
|
|
||||||
|
Shoggoth runs atop a custom platform named Ichor, which aims to trivialize providing these properties.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
While the source for this project happens to be published no grant of rights is made.
|
While the source for this project happens to be published no grant of rights is made.
|
||||||
|
|
23
projects/shoggoth/src/python/ichor/__main__.py
Normal file
23
projects/shoggoth/src/python/ichor/__main__.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
ichor entrypoint
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import *
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
vm = Interpreter(BOOTSTRAP)
|
||||||
|
ret = vm.run(
|
||||||
|
[
|
||||||
|
Opcode.CALLS(XOR3),
|
||||||
|
Opcode.RETURN(1)
|
||||||
|
],
|
||||||
|
stack = [True, False, False]
|
||||||
|
)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -11,7 +11,7 @@ from .typing import ProductExpr, SumExpr
|
||||||
|
|
||||||
BOOTSTRAP = Module()
|
BOOTSTRAP = Module()
|
||||||
|
|
||||||
NOT = BOOTSTRAP.define_function(
|
NOT1 = BOOTSTRAP.define_function(
|
||||||
";/lang/shoggoth/v0/bootstrap/not;bool;bool",
|
";/lang/shoggoth/v0/bootstrap/not;bool;bool",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
Opcode.IF(target=3),
|
||||||
|
@ -22,7 +22,7 @@ NOT = BOOTSTRAP.define_function(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
OR = BOOTSTRAP.define_function(
|
OR2 = BOOTSTRAP.define_function(
|
||||||
";/lang/shoggoth/v0/bootstrap/or;bool,bool;bool",
|
";/lang/shoggoth/v0/bootstrap/or;bool,bool;bool",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
Opcode.IF(target=3),
|
||||||
|
@ -36,7 +36,17 @@ OR = BOOTSTRAP.define_function(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
AND = BOOTSTRAP.define_function(
|
OR3 = BOOTSTRAP.define_function(
|
||||||
|
";/lang/shoggoth/v0/bootstrap/or;bool,bool,bool;bool",
|
||||||
|
[
|
||||||
|
# A B C
|
||||||
|
Opcode.CALLS(OR2), # A|B C
|
||||||
|
Opcode.CALLS(OR2), # A|B|C
|
||||||
|
Opcode.RETURN(1),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
AND2 = BOOTSTRAP.define_function(
|
||||||
";/lang/shoggoth/v0/bootstrap/and;bool,bool;bool",
|
";/lang/shoggoth/v0/bootstrap/and;bool,bool;bool",
|
||||||
[
|
[
|
||||||
Opcode.IF(target=3),
|
Opcode.IF(target=3),
|
||||||
|
@ -49,20 +59,30 @@ AND = BOOTSTRAP.define_function(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
XOR = BOOTSTRAP.define_function(
|
AND3 = BOOTSTRAP.define_function(
|
||||||
|
";/lang/shoggoth/v0/bootstrap/and;bool,bool,bool;bool",
|
||||||
|
[
|
||||||
|
# A B C
|
||||||
|
Opcode.CALLS(AND2), # A&B C
|
||||||
|
Opcode.CALLS(AND2), # A&B&C
|
||||||
|
Opcode.RETURN(1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
XOR2 = BOOTSTRAP.define_function(
|
||||||
";/lang/shoggoth/v0/bootstrap/xor;bool,bool;bool",
|
";/lang/shoggoth/v0/bootstrap/xor;bool,bool;bool",
|
||||||
[
|
[
|
||||||
Opcode.DUP(nargs=2),
|
Opcode.DUP(nargs=2),
|
||||||
# !A && B
|
# !A && B
|
||||||
Opcode.CALLS(NOT),
|
Opcode.CALLS(NOT1),
|
||||||
Opcode.CALLS(AND),
|
Opcode.CALLS(AND2),
|
||||||
Opcode.IF(target=6),
|
Opcode.IF(target=6),
|
||||||
Opcode.TRUE(),
|
Opcode.TRUE(),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
# !B && A
|
# !B && A
|
||||||
Opcode.ROT(2),
|
Opcode.ROT(2),
|
||||||
Opcode.CALLS(NOT),
|
Opcode.CALLS(NOT1),
|
||||||
Opcode.CALLS(AND),
|
Opcode.CALLS(AND2),
|
||||||
Opcode.IF(target=12),
|
Opcode.IF(target=12),
|
||||||
Opcode.TRUE(),
|
Opcode.TRUE(),
|
||||||
Opcode.RETURN(1),
|
Opcode.RETURN(1),
|
||||||
|
@ -72,6 +92,23 @@ XOR = BOOTSTRAP.define_function(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
XOR3 = BOOTSTRAP.define_function(
|
||||||
|
";/lang/shoggoth/v0/bootstrap/xor;bool,bool,bool;bool",
|
||||||
|
[
|
||||||
|
# A B C
|
||||||
|
Opcode.ROT(nargs=3), # C A B
|
||||||
|
Opcode.ROT(nargs=3), # B C A
|
||||||
|
Opcode.DUP(nargs=1), # B B C A
|
||||||
|
Opcode.ROT(nargs=4), # A B B C
|
||||||
|
Opcode.CALLS(XOR2), # A^B B C
|
||||||
|
Opcode.ROT(nargs=3), # C A^B B
|
||||||
|
Opcode.ROT(nargs=3), # B C A^B
|
||||||
|
Opcode.CALLS(XOR2), # B^C A^B
|
||||||
|
Opcode.CALLS(OR2), # A^B|B^C
|
||||||
|
Opcode.RETURN(1),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
TRUE = BOOTSTRAP.define_type(
|
TRUE = BOOTSTRAP.define_type(
|
||||||
"/lang/shoggoth/v0/true",
|
"/lang/shoggoth/v0/true",
|
||||||
ProductExpr([]),
|
ProductExpr([]),
|
||||||
|
|
|
@ -24,11 +24,12 @@ def rotate(l):
|
||||||
|
|
||||||
|
|
||||||
class Stackframe(object):
|
class Stackframe(object):
|
||||||
def __init__(self, stack=None, name=None, ip=None, parent=None):
|
def __init__(self, stack=None, name=None, ip=None, parent=None, depth=0):
|
||||||
self.stack = stack or []
|
self.stack = stack or []
|
||||||
self.name = name or ";unknown;;"
|
self.name = name or ";unknown;;"
|
||||||
self.ip = ip or 0
|
self.ip = ip or 0
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.depth = depth
|
||||||
|
|
||||||
def push(self, obj):
|
def push(self, obj):
|
||||||
self.stack.insert(0, obj)
|
self.stack.insert(0, obj)
|
||||||
|
@ -37,15 +38,16 @@ class Stackframe(object):
|
||||||
return self.stack.pop(0)
|
return self.stack.pop(0)
|
||||||
|
|
||||||
def call(self, signature: FunctionRef, ip):
|
def call(self, signature: FunctionRef, ip):
|
||||||
print(signature)
|
self.ip += 1
|
||||||
nargs = len(signature.args)
|
nargs = len(signature.args)
|
||||||
args, self.stack = self.stack[:nargs], self.stack[nargs:]
|
args, self.stack = self.stack[:nargs], self.stack[nargs:]
|
||||||
return Stackframe(
|
return Stackframe(
|
||||||
stack=args,
|
stack=args,
|
||||||
name=signature.raw,
|
name=signature.raw,
|
||||||
ip=ip,
|
ip=ip,
|
||||||
parent=self
|
parent=self,
|
||||||
)
|
depth=self.depth+1
|
||||||
|
)
|
||||||
|
|
||||||
def ret(self, nargs):
|
def ret(self, nargs):
|
||||||
self.parent.stack = self.stack[:nargs] + self.parent.stack
|
self.parent.stack = self.stack[:nargs] + self.parent.stack
|
||||||
|
@ -96,6 +98,8 @@ class Interpreter(object):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
op = mod.opcodes[stack.ip]
|
op = mod.opcodes[stack.ip]
|
||||||
|
# print("{0}{1: <50} {2}: {3}".format(" " * stack.depth, str(stack.stack), stack.ip, op))
|
||||||
|
|
||||||
match op:
|
match op:
|
||||||
case Opcode.TRUE():
|
case Opcode.TRUE():
|
||||||
stack.push(True)
|
stack.push(True)
|
||||||
|
@ -151,15 +155,16 @@ class Interpreter(object):
|
||||||
if (n > len(stack)):
|
if (n > len(stack)):
|
||||||
_error("Stack size violation")
|
_error("Stack size violation")
|
||||||
|
|
||||||
if stack.parent:
|
if stack.depth == 0:
|
||||||
sig = FunctionRef.parse(stack.name)
|
|
||||||
if (len(sig.ret) != n):
|
|
||||||
_error("Signature violation")
|
|
||||||
|
|
||||||
stack = stack.ret(n)
|
|
||||||
else:
|
|
||||||
return stack[:n]
|
return stack[:n]
|
||||||
|
|
||||||
|
sig = FunctionRef.parse(stack.name)
|
||||||
|
if (len(sig.ret) != n):
|
||||||
|
_error("Signature violation")
|
||||||
|
|
||||||
|
stack = stack.ret(n)
|
||||||
|
continue
|
||||||
|
|
||||||
case Opcode.GOTO(n, _):
|
case Opcode.GOTO(n, _):
|
||||||
if (n < 0):
|
if (n < 0):
|
||||||
_error("Illegal branch target")
|
_error("Illegal branch target")
|
||||||
|
|
|
@ -7,6 +7,9 @@ from .typing import FunctionRef
|
||||||
|
|
||||||
|
|
||||||
class Opcode:
|
class Opcode:
|
||||||
|
####################################################################################################
|
||||||
|
# Logic
|
||||||
|
####################################################################################################
|
||||||
class TRUE(NamedTuple):
|
class TRUE(NamedTuple):
|
||||||
"""() -> (bool)
|
"""() -> (bool)
|
||||||
Push the constant TRUE onto the stack.
|
Push the constant TRUE onto the stack.
|
||||||
|
@ -189,6 +192,18 @@ class Opcode:
|
||||||
Produces the updated array as the top of stack.
|
Produces the updated array as the top of stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# Naturals
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# Integers
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# Ratios
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
|
||||||
class Module(NamedTuple):
|
class Module(NamedTuple):
|
||||||
opcodes: list = []
|
opcodes: list = []
|
||||||
|
@ -241,3 +256,12 @@ class Module(NamedTuple):
|
||||||
def define_type(self, name, signature):
|
def define_type(self, name, signature):
|
||||||
# FIXME: What in TARNATION is this going to do
|
# FIXME: What in TARNATION is this going to do
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
b = []
|
||||||
|
marks = {v: k for k, v in self.functions.items()}
|
||||||
|
for i, o in zip(range(1<<64), self.opcodes):
|
||||||
|
if(i in marks):
|
||||||
|
b.append(f"{marks[i]}:")
|
||||||
|
b.append(f"{i: >10}: {o}")
|
||||||
|
return "\n".join(b)
|
||||||
|
|
|
@ -26,10 +26,6 @@ class ProductExpr(t.NamedTuple):
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
class Function(t.NamedTuple):
|
|
||||||
"""The type of a function; a subset of its signature."""
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionRef(t.NamedTuple):
|
class FunctionRef(t.NamedTuple):
|
||||||
raw: str
|
raw: str
|
||||||
type_params: list
|
type_params: list
|
||||||
|
@ -79,7 +75,3 @@ class FunctionSignature(t.NamedTuple):
|
||||||
cls.parse_list(args),
|
cls.parse_list(args),
|
||||||
cls.parse_list(ret)
|
cls.parse_list(ret)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Function(t.NamedTuple):
|
|
||||||
"""The type of a function; a subset of its signature."""
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from ichor import *
|
||||||
[[True], [False]],
|
[[True], [False]],
|
||||||
])
|
])
|
||||||
def test_not(vm, stack, ret):
|
def test_not(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(NOT)], stack = stack) == ret
|
assert vm.run([Opcode.CALLS(NOT1)], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -21,7 +21,7 @@ def test_not(vm, stack, ret):
|
||||||
[[True, True], [True]],
|
[[True, True], [True]],
|
||||||
])
|
])
|
||||||
def test_or(vm, stack, ret):
|
def test_or(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(OR)], stack = stack) == ret
|
assert vm.run([Opcode.CALLS(OR2)], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -31,7 +31,7 @@ def test_or(vm, stack, ret):
|
||||||
[[True, True], [True]],
|
[[True, True], [True]],
|
||||||
])
|
])
|
||||||
def test_and(vm, stack, ret):
|
def test_and(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(AND)], stack = stack) == ret
|
assert vm.run([Opcode.CALLS(AND2)], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
@ -40,19 +40,31 @@ def test_and(vm, stack, ret):
|
||||||
[[False, True], [True]],
|
[[False, True], [True]],
|
||||||
[[True, True], [False]],
|
[[True, True], [False]],
|
||||||
])
|
])
|
||||||
def test_xor(vm, stack, ret):
|
def test_xor2(vm, stack, ret):
|
||||||
assert vm.run([Opcode.CALLS(XOR)], stack = stack) == ret
|
assert vm.run([Opcode.CALLS(XOR2)], stack = stack) == ret
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
|
[[False, False, False], [False]],
|
||||||
|
[[True, False, False], [True]],
|
||||||
|
[[False, True, False], [True]],
|
||||||
|
[[True, True, False], [True]],
|
||||||
|
[[True, True, True], [False]],
|
||||||
|
[[False, True, True], [True]],
|
||||||
|
[[False, False, True], [True]],
|
||||||
|
])
|
||||||
|
def test_xor3(vm, stack, ret):
|
||||||
|
assert vm.run([Opcode.CALLS(XOR3)], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
[[], [FunctionRef.parse(NOT)]]
|
[[], [FunctionRef.parse(NOT1)]]
|
||||||
])
|
])
|
||||||
def test_funref(vm, stack, ret):
|
def test_funref(vm, stack, ret):
|
||||||
assert vm.run([Opcode.FUNREF(NOT), Opcode.RETURN(1)], stack = stack) == ret
|
assert vm.run([Opcode.FUNREF(NOT1), Opcode.RETURN(1)], stack = stack) == ret
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("stack,ret", [
|
@pytest.mark.parametrize("stack,ret", [
|
||||||
[[], [True]]
|
[[], [True]]
|
||||||
])
|
])
|
||||||
def test_callf(vm, stack, ret):
|
def test_callf(vm, stack, ret):
|
||||||
assert vm.run([Opcode.FALSE(), Opcode.FUNREF(NOT), Opcode.CALLF(1)], stack = stack) == ret
|
assert vm.run([Opcode.FALSE(), Opcode.FUNREF(NOT1), Opcode.CALLF(1)], stack = stack) == ret
|
||||||
|
|
|
@ -86,3 +86,10 @@ def test_drop_too_many(vm):
|
||||||
|
|
||||||
with pytest.raises(InterpreterError):
|
with pytest.raises(InterpreterError):
|
||||||
vm.run([Opcode.TRUE(), Opcode.DROP(2)])
|
vm.run([Opcode.TRUE(), Opcode.DROP(2)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_frames():
|
||||||
|
assert len(list(Stackframe().frames())) == 1
|
||||||
|
assert len(list(Stackframe(parent=Stackframe()).frames())) == 2
|
||||||
|
assert len(list(Stackframe(parent=Stackframe(parent=Stackframe())).frames())) == 3
|
||||||
|
assert len(list(Stackframe(parent=Stackframe(parent=Stackframe(parent=Stackframe()))).frames())) == 4
|
||||||
|
|
Loading…
Reference in a new issue