NICE language It's generally a queue machine (like a stack machine) with one register and an unlimited number of queues. The basic commands themselves are arranged in lines, but the commands aren't the turns but the content of the lines.
There isn't infinite RAM, but it doesn't matter, for two reasons. The first is the multiple queues system, and the second is that a turing complete system doesn't need RAM, just the ability to emulate it (and I think that a machine with one unlimited queue of fix sized integers is enough).
The memory model is quite non-standard. There are a few infinite-sizes queues scattered in the program space. Whenever an IP passes over a queue, it becomes the IP's "current queue". That means that some threads can have the same "current" queue. All the commands that use a queue, use the current queue only. There is also one register for every IP, that is used mainly for passing values between the queues.
The IPs follow the code lines, and perform the operations along them. There are 15 valid instructions. The behaviour of the other non-whitespace characters is implementation-defined, hence unspecified. Don't count on tab expansion on the parser's side. the NICE machine is a queue-based machine, yet we will use stack-machine terms (such as "push" and "pop").
|Q||Holds a queue inside it. When passed on, the IP's current queue becomes this queue. See below for further explanation.|
|:||Split the current IP to all possible directions (including an IP that will move backwards). The new IPs will be placed "before" the current IP in the IP cycle.|
|i||Read a character from the standard input, and push it to the current queue|
|o||Pop a character from the current queue and send it to the standard output|
|l||Pop a byte from the current queue, and store it's in the current IP's register|
|s||Push the value of the current IP's register's into the current queue|
|+||Add the value of the register to a byte popped from the current queue, and push the result back into the current queue|
|-||Substract a byte popped from the current queue from the register, and push the result back into the queue.|
|*||Multiply a byte popped from the current queue with the register, and push the result back into the queue.|
|/||Divide the number in the current register by a value pooped from the current queue, and push the quotient into the queue.|
|&||Divide the number in the current register by a value pooped from the current queue, and push the remainder into the queue|
|!||Pop a number from the queue. If it is 0, push 1. Otherwise, push 0.|
|#||Don't execute the next instruction on the line if the value at the top of the stack is 0.|
|@||Wait 1 tick to run the next instruction.|
The program starts with one IP in the upper-left corner of the file. It doesn't have a current queue. If you want to do anything, you should have a Q instruction near the beginning of a program. Whenever the IP switches the current queue (i.e. it already had selected a queue before) it pushes the value of the register into the new current queue. I think that this model can be a quite novel solution to the Forums issue in Begunge-102, but I didn't want to publish it so I can use it in this language.
You might have wandered about conditional execution and jumps. There is no instruction that deals with conditions. Instead, whenever an IP arrives at a function, it pops a number from the current queue (if it's empty, the number is 0), divides it by the number of available paths, and selects a new direction according to the remainder, from left to right. Let me demonstrate it using the following example:
x x a x a Xbb d c d c
The execution starts on the x branch, and moves towards the X. When the IP reaches the X, it executes the instruction at the X, then pops a number from the current queue, divides it by 4, and looks at the remainder: if it's 0, the a path is chosen. If it is 1, the b path is chosen, and so on. Note that the x path will no be chosen under any circunstances. Also note that the interpreter won't look too carefully at the paths. Even if you have a path, and one character near it, the IP will mess around (because almost any character will be a function).
When the IP reaches a dead end, it is removed from the IP cycle. When the last IP dies, the program stops.