ZeroStack2D

From Esolang
Jump to navigation Jump to search

ZeroStack2D

ZeroStack2D is a stack-based, 2D grid esoteric programming language, designed to be Turing-complete. It draws inspiration from Befunge and Brainfuck, combining 2D instruction pointer movement with a minimal stack-based execution model.

Overview

ZeroStack2D operates on a 2D grid of characters, where an instruction pointer (IP) moves across the grid, executing commands. The language's core is a single stack, which is used for all data storage and manipulation.

Instructions

Command Description
> Set IP direction to right
< Set IP direction to left
^ Set IP direction to up
v Set IP direction to down
0/! Push 0 onto the stack
+ Pop a value, add 1, push the result
- Pop a value, subtract 1, push the result
: Duplicate the top stack value
\ Swap the top two stack values
$ Pop and discard the top stack value
? Read a character from input, push its ASCII value
Read an integer from input, push it
. Pop a value and print it as a number
, Pop a value and print it as a character
Pop a value; if non-zero, set direction up, else down
_ Pop a value; if non-zero, set direction right, else left
@ Terminate the program

Turing completeness

ZeroStack2D is Turing-complete, as it can simulate a Minsky counter machine (P), which is known to be Turing-complete. This is achieved by using the stack to represent counters and the conditional jump instructions to control the flow.

Implementations

This is Python reference implementation, which interprets ZeroStack2D source files from a 2D grid:

<code>
#!/usr/bin/python3
import os, sys

class Element():
    def __init__(self, data):
        self.data = data
        self.next = None

class Stack:
    def __init__(self):
        self.head = None

    def __bool__(self):
        return self.head is not None

    def push(self, data):
        new_node = Element(data)
        new_node.next = self.head
        self.head = new_node

    def pop(self):
        if not self:
            return 0
        data = self.head.data
        self.head = self.head.next
        return data

    def swap(self):
        if not self:
            self.push(0)
        a = self.pop()
        b = self.pop() if self else 0
        self.push(a)
        self.push(b)

    def add(self, b):
        a = self.pop()
        self.push(b + a)

with open(sys.argv[1]) as f:
    program = f.readlines()

stack = Stack()
x = 0
y = 0
d = 0

while True:
    cmd = program[y][x]

    if cmd == ">":
        d = 0
    elif cmd == "<":
        d = 2
    elif cmd == "^":
        d = 1
    elif cmd == "v":
        d = 3

    elif cmd in "!0":
        stack.push(0)
    elif cmd == "+":
        stack.add(1)
    elif cmd == "-":
        stack.add(-1)
    elif cmd == ":":
        k = stack.pop()
        stack.push(k)
        stack.push(k)
    elif cmd in "\\/":
        stack.swap()
    elif cmd == "?":
        stack.push(ord(input()))
    elif cmd == "~":
        stack.push(int(input()))
    elif cmd == "$":
        stack.pop()
    elif cmd == ".":
        print(stack.pop())
    elif cmd == ",":
        print(chr(stack.pop()))

    elif cmd == "|":
        if stack:
            a = stack.pop()
            if a:
                d = 1
            else:
                d = 3
            stack.push(a)
        else:
            d = 3

    elif cmd == "_":
        if stack:
            a = stack.pop()
            a = stack.pop()
            if a:
                d = 0
            else:
                d = 2
            stack.push(a)
        else:
            d = 2

    elif cmd == "@":
        break

    if d == 0:
        x += 1
    elif d == 1:
        y -= 1
    elif d == 2:
        x -= 1
    elif d == 3:
        y += 1
</code>