An evening of hacking

This commit is contained in:
Reid D. 'arrdem' McKenzie 2022-05-31 23:08:29 -06:00
parent 9b965dba05
commit b6e1a61a85
9 changed files with 151 additions and 38 deletions

View file

@ -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",
],
)

View file

@ -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.

View 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()

View file

@ -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([]),

View file

@ -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,14 +38,15 @@ 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):
@ -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,14 +155,15 @@ 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:
return stack[:n]
sig = FunctionRef.parse(stack.name) sig = FunctionRef.parse(stack.name)
if (len(sig.ret) != n): if (len(sig.ret) != n):
_error("Signature violation") _error("Signature violation")
stack = stack.ret(n) stack = stack.ret(n)
else: continue
return stack[:n]
case Opcode.GOTO(n, _): case Opcode.GOTO(n, _):
if (n < 0): if (n < 0):

View file

@ -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)

View file

@ -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."""

View file

@ -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

View file

@ -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