Hollow is a Turing-complete esoteric programming language from 2012. (The language was invented in 2011, but certain details were not polished until 2012.) A Hollow program consist of any number (including zero) of lines. A line in this context means a data string of proper structure; something that can be executed. There may be any number and order of new-lines and spaces before and after any one line, and there must be at least one new-line in between two lines to separate them. All strings that contain data must be valid and executable. The memory model is an unbound stack of strings.

Initially, execution begins in the very first line. Where the execution continues after an executed line is determined by the success or failure of that line. Success is achieved when and only if every part of the line's execution proceeds without problems.

A line begins with a data part written inside {} brackets. The data part of a line is followed by an instruction, which is one character long (or in one case, there is no character). The instruction is followed by the success and failure indicators, which are strings consisting of '<' and '>' characters. There may be any number of those characters, including zero, but in one indicator they all must be of the same character. (A mixture such as <<> would be forbidden.) The success and failure are separated by a '|' character. If the failure part moves zero lines, then '|' is not required (and it is by no means an error to have it).

The possible instructions after the data part are these:

(nothing)Pushes the data part into stack. Always successful if the data part is fine.
.Does not push the data part into stack. Always successful if the data part is fine.
+Pushes two instances of the data part into stack. Always successful if the data part is fine.
\Searches stack (from bottom to top) for a string that contains the data part. If such a string is found, it is removed from its place in the stack and placed on the very top. If no such string is found, failure issues. If the data part is an empty string, nothing is searched and failure issues. If the stack is empty, nothing will naturally be found, and failure issues. The data part is not stored after use.
=Splits the top-most string of the stack with the data part. If the data part is a zero string or the string in stack does not contain the data part (that is not an empty string), the top-most stack value remains as it is, and failure issues. If the string does contain the data part, then it is split into two parts. If the string in stack is, for example, "ab-:-cdef" and data part is "-:-", the stack will end up having "ab" as its top-most item and "cdef" as its second-top-most item. The original top-most item "ab-:-cdef" will be gone, and the data part is not stored after use.
:Outputs the data part. Always successful if the data part is fine. Does not place the data part in stack.

The data part is a string that is created by the data inside {} brackets. There are six special characters, of which two may affect the success/failure state of the given line. All special characters replace themselves with the data they bring. A data part may not contain characters '{', '}', and '.' nor new-lines. Those are represented with special characters. The special characters are marked by a '.' following a single-digit number:

.0Takes a character as input. If no more input / EOF, .0 returns an empty string and a failure ensues. Otherwise returns the input character.
.1Takes the top-most stack item (removing it from the stack) and replaces itself with it. If .1 is used twice, the second .1 will pop the stack just like the first did. If there is no data in the stack when .1 is used, it returns an empty string and makes the line a failure.
.6Returns ".". Always successful.
.7Returns "{". Always successful.
.8Returns "}". Always successful.
.9Returns a new-line. Always successful.

If the data part were, say, ".60", it would create ".0" as data. If that were to be saved in the stack, and later used by another line, for example with the data part of "a.1b", that line would (assuming that the ".0" were top-most in stack at the time) create the data "a.0b". All data that is taken from input by .0 or stack by .1 is static data, in which special characters do not have any effect. The use of numbers 2, 3, 4, and 5 as a part of a special character is an error.

After the line has created its data and executed its instruction, it is time to go to the next instruction. If the line were a success and its success part were, for example, ">>", the control flow would move to the line after the next line. Empty lines or lines consisting only of spaces (meaning actual rows of text here, the shortest possible Hollow line being "{}") are meaningless to control flow, they are removed when the program is read in. However, if the control flow / line pointer moves to a line that is not in existence, that is, the line(s) before the first line, or the line(s) after the last line, the interpreter shall stay in an infinite loop, since there is no line in the point it is trying to access. Ending the program execution happens by placing a single '?' instead of a sequence of '<'s / '>'s. Encountering a '?' in either the success or failure part, the program terminates successfully.

Below is an example program, a truth-machine. It requests input, if the input is 0 it will print 0 and terminate, if the input is 1 it will keep printing 1 and never terminates.

{ a truth-machine in Hollow }.>

It works like this. The execution starts on the 1st line. Its data part creates the string " a truth-machine in Hollow " but the "." instruction discards it (the line is essentially a comment). Everything is successful and ">" moves the execution to the 2nd line. There, the data part creates the string "1" and pushes it to stack. Execution moves on to line 3. Here the data part is ".0" which, when the string is being created, will replace itself with a character taken as input. The "\" instruction searches the stack from bottom to top. The stack, at the moment, contains only one item, "1". If the input character the user gave is "1", the "\" instruction will find such an instance in the stack, move it to the top (where it already is) and follow the success part, ">", of line 3. However, if the input was "0", the "\" will not find any string containing that data from the stack, and thus, it is a failure, ">>". In case of failure, line pointer moves to line 5, where the data part creates "0", which is output, always successfully. "?" as the success part terminates the program. If line 3 were a success (input was "1"), line pointer would move to line 4 instead, and there data part would make "1" which would be successfully output. Since the success and failure parts on line 4 are both empty, it means that the line pointer moves 0 units in neither direction. The line pointer thus moves to line 4 again, where it already is, in an infinite loop that keeps printing the "1" string it every time creates.


An interpreter written in Python can be found here. Usage: hollow.py prog.hlw

A couple of Hollow programs I have written:

1.hlwMy traditional 'pyramid made of 1s' program.
un.hlwAn Underload interpreter! I wrote this to prove the Turing-completeness of this language. Give your Underload program as input. Be sure that your Underload program does not have []<> characters (it is very unlikely that any Underload program has them). (This program is a bit wasteful of memory, it tends to leave instances of empty strings lying about, but since the memory is infinite (in specs anyway), no harm done. I did not fix this because I, now half a year later, cannot bear to figure out how my program works.)
ct.hlwA Cyclic Tag interpreter, for a good measure. The program and memory strings are given in the program, so alter them there. I did not bother making this one to collect input.
quine.hlwA quine.
etre.hlwAn Etre interpreter. Give the Etre program as input. The interpreter outputs the memory array/string whenever it is changed or the memory pointer moves.

-- Keymaker, 22.2.2012