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"),
|
||||
],
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
NOT = BOOTSTRAP.define_function(
|
||||
NOT1 = BOOTSTRAP.define_function(
|
||||
";/lang/shoggoth/v0/bootstrap/not;bool;bool",
|
||||
[
|
||||
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",
|
||||
[
|
||||
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",
|
||||
[
|
||||
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",
|
||||
[
|
||||
Opcode.DUP(nargs=2),
|
||||
# !A && B
|
||||
Opcode.CALLS(NOT),
|
||||
Opcode.CALLS(AND),
|
||||
Opcode.CALLS(NOT1),
|
||||
Opcode.CALLS(AND2),
|
||||
Opcode.IF(target=6),
|
||||
Opcode.TRUE(),
|
||||
Opcode.RETURN(1),
|
||||
# !B && A
|
||||
Opcode.ROT(2),
|
||||
Opcode.CALLS(NOT),
|
||||
Opcode.CALLS(AND),
|
||||
Opcode.CALLS(NOT1),
|
||||
Opcode.CALLS(AND2),
|
||||
Opcode.IF(target=12),
|
||||
Opcode.TRUE(),
|
||||
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(
|
||||
"/lang/shoggoth/v0/true",
|
||||
ProductExpr([]),
|
||||
|
|
|
@ -24,11 +24,12 @@ def rotate(l):
|
|||
|
||||
|
||||
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.name = name or ";unknown;;"
|
||||
self.ip = ip or 0
|
||||
self.parent = parent
|
||||
self.depth = depth
|
||||
|
||||
def push(self, obj):
|
||||
self.stack.insert(0, obj)
|
||||
|
@ -37,15 +38,16 @@ class Stackframe(object):
|
|||
return self.stack.pop(0)
|
||||
|
||||
def call(self, signature: FunctionRef, ip):
|
||||
print(signature)
|
||||
self.ip += 1
|
||||
nargs = len(signature.args)
|
||||
args, self.stack = self.stack[:nargs], self.stack[nargs:]
|
||||
return Stackframe(
|
||||
stack=args,
|
||||
name=signature.raw,
|
||||
ip=ip,
|
||||
parent=self
|
||||
)
|
||||
stack=args,
|
||||
name=signature.raw,
|
||||
ip=ip,
|
||||
parent=self,
|
||||
depth=self.depth+1
|
||||
)
|
||||
|
||||
def ret(self, nargs):
|
||||
self.parent.stack = self.stack[:nargs] + self.parent.stack
|
||||
|
@ -96,6 +98,8 @@ class Interpreter(object):
|
|||
|
||||
while True:
|
||||
op = mod.opcodes[stack.ip]
|
||||
# print("{0}{1: <50} {2}: {3}".format(" " * stack.depth, str(stack.stack), stack.ip, op))
|
||||
|
||||
match op:
|
||||
case Opcode.TRUE():
|
||||
stack.push(True)
|
||||
|
@ -151,15 +155,16 @@ class Interpreter(object):
|
|||
if (n > len(stack)):
|
||||
_error("Stack size violation")
|
||||
|
||||
if stack.parent:
|
||||
sig = FunctionRef.parse(stack.name)
|
||||
if (len(sig.ret) != n):
|
||||
_error("Signature violation")
|
||||
|
||||
stack = stack.ret(n)
|
||||
else:
|
||||
if stack.depth == 0:
|
||||
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, _):
|
||||
if (n < 0):
|
||||
_error("Illegal branch target")
|
||||
|
|
|
@ -7,6 +7,9 @@ from .typing import FunctionRef
|
|||
|
||||
|
||||
class Opcode:
|
||||
####################################################################################################
|
||||
# Logic
|
||||
####################################################################################################
|
||||
class TRUE(NamedTuple):
|
||||
"""() -> (bool)
|
||||
Push the constant TRUE onto the stack.
|
||||
|
@ -189,6 +192,18 @@ class Opcode:
|
|||
Produces the updated array as the top of stack.
|
||||
"""
|
||||
|
||||
####################################################################################################
|
||||
# Naturals
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# Integers
|
||||
####################################################################################################
|
||||
|
||||
####################################################################################################
|
||||
# Ratios
|
||||
####################################################################################################
|
||||
|
||||
|
||||
class Module(NamedTuple):
|
||||
opcodes: list = []
|
||||
|
@ -241,3 +256,12 @@ class Module(NamedTuple):
|
|||
def define_type(self, name, signature):
|
||||
# FIXME: What in TARNATION is this going to do
|
||||
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):
|
||||
raw: str
|
||||
type_params: list
|
||||
|
@ -79,7 +75,3 @@ class FunctionSignature(t.NamedTuple):
|
|||
cls.parse_list(args),
|
||||
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]],
|
||||
])
|
||||
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", [
|
||||
|
@ -21,7 +21,7 @@ def test_not(vm, stack, ret):
|
|||
[[True, True], [True]],
|
||||
])
|
||||
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", [
|
||||
|
@ -31,7 +31,7 @@ def test_or(vm, stack, ret):
|
|||
[[True, True], [True]],
|
||||
])
|
||||
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", [
|
||||
|
@ -40,19 +40,31 @@ def test_and(vm, stack, ret):
|
|||
[[False, True], [True]],
|
||||
[[True, True], [False]],
|
||||
])
|
||||
def test_xor(vm, stack, ret):
|
||||
assert vm.run([Opcode.CALLS(XOR)], stack = stack) == ret
|
||||
def test_xor2(vm, 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", [
|
||||
[[], [FunctionRef.parse(NOT)]]
|
||||
[[], [FunctionRef.parse(NOT1)]]
|
||||
])
|
||||
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", [
|
||||
[[], [True]]
|
||||
])
|
||||
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):
|
||||
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