99 (pronounced "ninety-nine") is a brand new Esoteric programming language (not to be confused with 99, note the italics). Your task in this challenge is to write an interpreter for 99 that is as short as possible. The submission with the fewest bytes wins. Tiebreaker goes to the submission posted first.
Since this question is a bit more in depth than usual, and I'm eager to see good answers, I'll be awarding a 250 rep bounty to my favorite answer (not necessarily the winner).
99 Spec
99 is an imperative language. Each line in a 99 program is a single statement, and during execution, the instruction pointer starts at the top line and goes through each of the subsequent lines in order, executing them along the way. The program ends when the last line has been executed. Goto statements may reroute the path of the instruction pointer.
Newline, space, and 9
are the only three characters that matter in a 99 program. All other characters are completely ignored. Additionally, trailing spaces on each line are ignored, and multiple spaces in a row are read as one space. ("Newline" refers to any common line break encoding. It doesn't matter which one your interpreter uses.)
So this program:
9 BLAH 99 9a9bb9c9
9 this line and the next have 6 trailing spaces 9
Is identical to this program:
9 99 9999
9 9
Variables
Variables in 99 all have names that are one or more 9
's strung together (9+
in regex). For example, 9
, 99
, and 9999999999
are all distinct variables. Naturally, there are infinitely many (barring memory limitations).
The value of each variable is a signed, arbitrary precision integer. By default, each variable is assigned to its own numeric representation. So unless it has been reassigned, the value of the variable 9
is the number 9, and the value of the variable 99
is the number 99, and so on. You could think of it as treating the variables as plain numbers until they are explicitly assigned.
I will use V
to refer to an arbitrary variable name below.
Each instance of V
could be replaced with 9
, 99
, 999
, 9999
, etc.
Statements
There are five different statement types in 99. Each line in a 99 program contains exactly one statement.
The syntax described here assumes all extraneous characters have been removed, all trailing spaces have been removed, and all sequences of multiple spaces have been replaced with single spaces.
1. No Operation
An empty line is a no-op. It does nothing (besides incrementing the instruction pointer).
2. Output
V
A single variable V
on a line prints that variable to stdout.
If V
has an odd number of 9
's (9
, 999
, etc.) then the integer value of V
divided by 9 will be printed (in decimal).
If V
has an even number of 9
's (99
, 9999
, etc.) then the ASCII character with code V
divided by 9, mod 128 will be printed. (That's (V / 9) % 128
, a value from 0 to 127.)
Example: The program
9
9999
would print 1W
. The first line prints 1
because 9/9 is 1. The second line prints W
because 9999/9 is 1111, and 1111 mod 128 is 87, and 87 is the character code for W
.
Note that line breaks are not printed between output tokens. \n
needs to be explicitly printed for a line break.
3. Input
V
A single variable V
on a line with a leading space takes input from stdin and stores it in that variable.
If V
has an odd number of 9
's then the user may type in any signed integer, and V
will be set to 9 times that value.
If V
has an even number of 9
's then the user may type in any ASCII character, and V
will be set to 9 times its character code.
Example: Given -57
and A
as input, this program
9
9
99
99
would output -57A
. Internally, the variable 9
would have the value -513, and 99
would have the value 585.
Your interpreter may assume that the inputs are always syntactically valid.
4. Assignment
This statement can be arbitrarily long. It is two or more variables on a line, separated by spaces:
V1 V2 V3 V4 V5 ...
This assigns V1
to the sum of all the V
's with even indices, minus the sum of the V
's with odd indices (excluding V1
). Assignments are by value, not by reference.
It could be translated in most languages as V1 = V2 - V3 + V4 - V5 + ...
.
So, if there are only two variables, it's normal assignment:
V1 V2
→ V1 = V2
If there are three, then it's subtraction:
V1 V2 V3
→ V1 = V2 - V3
And the +
/-
sign keeps switching back and forth with each additional variable:
V1 V2 V3 V4
→ V1 = V2 - V3 + V4
Example: This program would output 1110123
:
999 Prints triple-nine divided by nine (111).
999 9 9 Assigns triple-nine to zero (nine minus nine).
999 Prints triple-nine divided by nine (0)
9 999 9 Assigns single-nine to negative nine (zero minus nine).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (1).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (2).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (3).
5. Goto (jump if all zero)
This statement can also be arbitrarily long. It is two or more variables on a line, separated by spaces, with a leading space:
V1 V2 V3 V4 V5 ...
If some of the values besides V1
are non-zero, then this behaves just like a no-op. The instruction pointer is moved to the next line as usual.
If all of the values besides V1
are zero, then the instruction pointer is moved to line number V1
. The lines are zero-indexed, so if V1
is zero, then the pointer moves to the top line. The program terminates (normally, without error) if V1
is negative or is larger than the highest possible index (number of lines minus one).
Note that V1
was not divided by 9 here. And since it's impossible to have a variable be a value that's not a multiple of 9, only line numbers that are multiples of 9 can be jumped to.
Examples:
This program will print 1
's forever:
9 Prints single-nine divided by nine (always 1).
99 9 9 Assigns double-nine to zero.
99 99 Jumps to line zero (top line) if double-nine is zero.
This program
99999999 Print G.
999 99 Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999 Set 10-nine to zero.
99999999999 9999999999 Set 11-nine to zero.
999 Print triple-nine's value divided by nine. (This is the ninth line.)
99999999 Print G.
999 999 9 Subtract nine from triple-nine.
99999 999 Jump to line 5-nines if triple-nine is zero (ends program).
9 99999999999 9999999999 Jump to line nine if 10-nine and 11-nine are zero (always jumps).
will output the numbers 11 to 1, in decreasing order, surrounded by G
's:
G11G10G9G8G7G6G5G4G3G2G1G
Additional Details
The ideal interpreter will run from the command line with the 99 program file name as an argument. The I/O will also be done on the fly in the command line.
You may, however, just write an interpreter function that takes in the program as a string as well as a list of the input tokens (e.g. ["-57", "A"]
). The function should print or return the output string.
Slightly different ways of running the interpreter and handling I/O are fine if these options are impossible in your language.
Bonus: Write something cool in 99 and I'll gladly put it in this post as an example.
- Here's a Pastebin of a neat "99 Bottles of Beer" program from Mac's answer.
Hope you enjoyed my 99th challenge! :D