Semqain (SElf-Modifying Queue brAINf***) is an esoteric programming language by User:BoundedBeans resembling BF, but with the code and the tape fused and queue-ified.
A single thread in Semqain possesses a queue of 4-bit numbers (0-15), a data pointer, a data pointer stack, an initial queue state, and a thread message queue. The command pointer is always at the front, but the data pointers can be anywhere in the queue. In the starting thread, the initial queue state is just a clone of what the queue was initialized to, but further threads use the queue of the thread they forked off of. The data pointer stack is initially empty. The message queue is empty when a thread is created. Messages consist of the length (0-15) followed by that many cells (the body).
Threads also remember the order which they were created, used by the 15 1 command.
Whenever something is dequeued from the queue, the data pointer moves, to compensate for the fact that all data is moved back. The data pointers on the stack also move . Accessing a cell over and over again does not require explicitly moving the data pointer, but the data pointer is implicitly moved to the right, allowing it to access the same data, even though it's technically not the same position. Moving to the left is simply like moving the pointer twice, but you only have to explicitly move it once.
If the data pointer ever gets out of range, the data pointer's position is popped off the data pointer stack. If the stack is empty, the thread halts.
The thread also halts if the queue is empty.
If all threads have halted, the program halts as a whole.
The syntax consists mainly of command characters, but there are two special forms:
- The equal sign (
=) initializes the data pointer to the cell following this character. This character must occur outside of a comment exactly once, and must not be the last non-comment character.
- The closing square bracket (
]) surrounds comments. The two brackets and anything inside that isn't also a closing square bracket will not be added into the queue, and equal signs will be ignored. All invalid characters (including whitespace) must be surrounded by this or it is an error without even executing the program.
Each instruction has a corresponding 4-bit number, but is represented with a character in the code. The initial tape contents are also in the code as well.
|>||1||Moves the data pointer right (towards the back of the queue)|
|<||2||Moves the data pointer left (towards the front of the queue)|
|+||3||Adds one to the cell at the data pointer, wrapping if necessary|
|-||4||Subtracts one from the cell at the data pointer, wrapping if necessary|
|.||5||Outputs the cell at the data pointer as a nybble.|
|,||6||Inputs a nybble into the cell at the data pointer|
|!||7||Dequeues a second number, dequeues and enqueues that many cells. If the pointer was in one of those cells, move it to the enqueued position.|
|?||8||Dequeues a second number, dequeues that many cells.|
|;||9||Dequeues a second number, if the cell at the data pointer is zero, dequeue that many cells|
|#||10||Halt the current thread.|
|/||11||Enqueue the contents of the initial state of the queue. This is the only single-threaded way to expand the queue.|
|*||12||Push the data pointer onto the stack, as a location to teleport to later|
|&||13||Pop the data pointer off the stack, teleport the data pointer to it|
|@||14||save all contents of the queue, data pointer, and data pointer stack to a location. (The message queue is not copied.) Start a new thread with the following changes:
|[||15||Look at and dequeue the next cell in memory. Depending on what it is, execute something different.
.>.>.>.>.>.<.>.<.>>.#]The previous actually gets executed. ]=-?,.,*The previous is just a nybble table to print from]