nouse
programming language
nouse
is pronounced like noose. It is spelled
nouse
because it's not useful.
The inspiration for creating this language came while reading a description of Befunge, in which the flow of control can go left, right, up, or down in a torus. So I figured it would be even more perverse if the flow of control depended on something like the current size of the stack.
The nouse
virtual machine consists of a ring of bytes and
a stack of bytes. Execution continues as long as the instruction ring
is not empty, and begins at instruction 0 with an empty stack.
Each byte in the ring is an instruction. Each instruction is composed of two parts. The byte modulus 7 specifies the instruction. The byte divided by seven specifies the skip multiplier.
The skip of the instruction is the skip multiplier multiplied by size of the stack before the operation is executed.
For example, if the instruction isThe skip is used to determine the next instruction and, if necessary, the operand of the instruction. Thewrite 4
, wherewrite
is the operation, and4
is the skip multiplier, and if the stack contains 3 bytes, then the skip is 12.
read
,
write
, and swap
operations have no operand,
and, when the stack is empty, the add
and
test
operations become noops with no operand. When there
is no operand, the next instruction is determined by skipping skip
bytes after the current instruction. When there is an operand with
the add
, test
, add
operations,
the operand location is determined by skipping skip bytes after the
current instruction, and the next instruction is determined by
skipping skip bytes after the operand location. The
cut
, paste
, and swap
operations
change the instruction ring; the descriptions of these operations will
specify how the operand and next instruction are determined from the
skip.
The operations are
cut
: Push contents of the operand location onto
the stack, then remove the operand location from the ring. The next
instruction is determined by skipping skip bytes after the removed
location.
paste
: If the stack is not empty, the top of the
stack popped and inserted into the ring after skipping skip bytes, in
other words, it is inserted just before the normal operand location,
then, the next instruction is determined by skipping skip bytes after
the newly inserted byte. If the stack is empty, a copy of the
contents of the operand location is inserted.
read
: Read one byte from the input channel, and
push it on the top of the stack. If at the end of file, the stack is
not changed.
write
: If the stack is not empty, write the top of
the stack to the output channel. The stack is not changed.
add
: If the stack is not empty, replace the top of
the stack with the sum of the top of the stack and the contents of the
operand location.
test
: If the stack is not empty, compare the top
of the stack with the contents of the operand. If they are equal, pop
the top of the stack.
swap
: Swap the contents of the ring and the
stack. The current location, (containing this swap
instruction), becomes the bottom of the stack. The bottom of the
stack becomes the current location, and the next instruction is
determined by skipping skip bytes. Note that the skip is determined
before the swap occurs, from the skip multiplier of the original
instruction and the size of the original stack.
There are two syntaxes: the assembly syntax, and the line-noise syntax. The simple interpreter in OCaml and the interpreter in C support only the line-noise syntax. The fancier interpreter in OCaml supports both syntaxes, and can convert between the two.
#:<>+?^
0
to
9
.
a
to
z
.
_
, which is ignored
if the operation doesn't allow that multiplier
#
for cut
is inspired by an ancient tty
setting for delete
:
for paste
is inspired by the cons
operator
<
and >
for read
and
write
is inspired by sh redirects
+
for add
is obvious
?
for test
is obvious
^
for swap
is inspired its use as xor,
which can be used to swap bits
read 0, write 6, swap 0, test 2, add 1
<0>6^0?2+1
cut 0, 72, write 0, paste 0 cut 0, 101, write 0, paste 0 cut 0, 108, write 0, write 0, paste 0 cut 0, 111, write 0, paste 0 cut 0, 32, write 0, paste 0 cut 0, 119, write 0, paste 0 cut 0, 111, write 0, paste 0 cut 0, 114, write 0, paste 0 cut 0, 108, write 0, paste 0 cut 0, 100, write 0, paste 0 cut 0, 33, write 0, paste 0 cut 0, 13, write 0, paste 0 cut 0, 10, write 0, paste 0 swap 0
#0<a>0:0#0>e>0:0#0>f>0>0:0#0^f>0:0#0+4>0:0#0#h>0:0#0^f>0:0#0<g>0:0#0>f >0:0#0<e>0:0#0?4>0:0#0^1>0:0#0>1>0:0^0
#0^0#0>0#0+0#0:4#0>0#0+0#0#1#0>0#0>0#0+0#0>0#0>0#0+0#0<p#0>0#0+0#0>c#0 >0#0+0#0>z#0>0#0+0#0>0#0>0#0+0#0?z#0>0#0+0#0>z#0>0#0+0#0#r#0>0#0+0#0?x #0>0#0+0#0:_#0>0#0+0#0:z#0^0#2#0#0#2#e<a^0