Radixal!!!! is an esoteric language created collaboratively by the #esoteric IRC channel on 7 December 2012. The details of the spec were mostly worked out by User:ais523. The language revolves around having a numerical representation that can't represent certain numbers, and commands that are specifically designed to be awkward to use, while still quite powerful. Therefore, it can be considered to be in the same family as Malbolge and Dis, although probably rather easier, and without exactly the same purpose.
To avoid confusion, all numbers in this specification will be written in Roman numerals. (Apart from zero, which can't be written in Roman numerals without butchery.)
A Radixal!!!! program consists of a list of digits and spaces (collectively characters). The accepted digits are
9; they have their usual meanings as digits if written by themselves (zero, I, II, III, IV, V, VI, VII, VIII, IX). Spaces are expressed in source code via the use of any positive consecutive amount of whitespace. (Although multiple consecutive spaces can potentially appear during execution of the program, there is no way to express them in the representation of a program used on input.)
A string of digits can usually be interpreted as a Radixal!!!! integer. The rules for interpreting such strings of digits as an integer are as follows:
- Any string of
0s is zero.
- Any string that contains nothing but
1s, and at least I
1, is considered an error and crashes the interpreter. Although any sort of crash is acceptable and complies with the spec, we recommend segfaults, or on Windows, Blue Screens of Death.
- For other strings of digits, it's interpreted as a number in base n+I, where n is the largest digit in the string. For instance,
4has the value IV (being interpreted as a base V number),
15is interpreted as a base VI number and thus has the value XI, and
1999is interpreted as a base X number and as such has the value MCMXCIX.
Radixal!!!! integers can be converted back to strings of digits by choosing, out of the strings of digits whose value is that integer, the string which has the lowest total sum of digits and no leading zeroes (tiebroken by taking the shortest, if there's a tie, and the lower base, if there's a tie in that too). Each Radixal!!!! integer also has a base; zero has base zero, the base of a larger integer is the base that would be used when converting it to a string of digits and back again.
Note that not all integers are Radixal!!!!. Such integers cannot be represented in Radixal!!!!, nor can any of the operations in Radixal!!!! produce them.
The only state in an executing Radixal!!!! program is the instruction pointer (which starts at the second character of the program, which is sometimes but not always equivalent to starting at the first character of the program); one accumulator, which can hold arbitrary Radixal!!!! integers (initially II); and the program itself, which can be modified during execution, and is considered to have an unbounded length, by adding an unbounded number of spaces after the program.
Each step of execution consists of first moving the instruction pointer forwards to the next digit it finds, if it's on a space. Then the (space-bounded) string of digits that is pointed to by the pointer is interpreted as a Radixal!!!! integer, as is the string immediately after that, and the instruction pointer is moved to the space immediately after the second string. (If there are not two such strings, the interpreter should enter an infinite loop. Please save energy via optimising this infinite loop into an infinite sleep.) The first of these strings is interpreted as an argument to a command, and the second string as the command itself (the digitwise operations are taken from TriINTERCAL, and have the same meaning as used there):
|zero||No operation, argument is ignored|
|II|| Sets the accumulator to the base n BUT of the argument and the accumulator (where n is the base of the argument or the accumulator, whichever is larger). BUT is a digitwise operation, where you consider both numbers as being written in base n using the same number of digits, then for corresponding digits, choose the "buttier" digit between the two digits; |
|III|| Sets the accumulator to the base n what of the argument and the accumulator (where n is the base of the argument or the accumulator, whichever is larger). What is also a digitwise operation, similar to BUT, except that instead of taking the buttier digit, you subtract the digit corresponding to the accumulator from the digit corresponding to the argument, adding n if the result is negative. As a special case, if all digits in the resulting calculation are |
|IV|| Sets the accumulator to the base n sharkfin of the argument and the accumulator (where n is the base of the argument or the accumulator, whichever is larger). Sharkfin is like what, except that you add rather than subtracting, and if the result is greater than n, you subtract n. As a special case, if all digits in the resulting calculation are |
|V||If n is the value of the argument, writes the value of the accumulator, as a string of digits followed by a space, starting at the position n characters after the instruction pointer (if n is even) or n characters before the instruction pointer (if n is odd). This overwrites existing characters in those positions.|
|VI||If n is the value of the argument, reads the string of digits that contains the position n characters before the instruction pointer (if n is odd) or n characters after the instruction pointer (if n is even). If that position contains a space, rather than a digit, the instruction pointer is moved forwards (with that position moving forwards accordingly, as it's defined relative to the instruction pointer) until that position contains a digit. (This may send the interpreter into an infinite loop, if no digits are to be found past that point.)|
|VII||Moves the instruction pointer n characters backwards (if n is divisible by III) or n characters forwards (if n is not divisible by III), where n is the value of the argument.|
|VIII||Inputs one Unicode codepoint from the user; adds it to the argument, and then sets the accumulator to the lowest Radixal!!!! integer strictly larger than that value.|
|IX||This has the same effect as VIII, except that instead of inputting a codepoint from the user, it reuses the most recently input codepoint.|
|Other values||The argument and accumulator are multiplied with each other, interpreted as a Unicode codepoint, and output to the user. (Yes, this means that although arbitrary codepoints can be input, it's not possible to output arbitrary codepoints; programmers are encouraged to find similar-looking codepoints that can be output and use them instead.)|
Attempts to read or write before the start of the program cause it to exit; writes with a "success" exit code, reads with a "failure" exit code.
Radixal!!!! is not obviously either Turing-complete, or not Turing-complete; it clearly has unlimited memory, but it's not completely obvious what sort of control flow patterns are possible (with the only way to get a conditional jump being to conditionally write a VII instruction, or to overwrite the target of a VII instruction), making it a little unclear how to produce arbitrary effects at arbitrary points.