An evening of hacking

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

View file

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

View file

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

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

View file

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

View file

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

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):
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."""

View file

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

View file

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