MoreMathRPN
Paradigm(s) | imperative, functional |
---|---|
Designed by | User:Calculus is fun |
Appeared in | 2023 |
Computational class | Turing-Complete |
Major implementations | JavaScript |
Influenced by | FALSE, TI-BASIC |

- Unless otherwise specified, "or" is exclusive or.
MoreMathRPN (abbreviated MMRPN) is a stack based imperative language created in 2023, created by Calculus is fun (talk).
Originally for simple linear algebra, it has since grown to be turing complete. It is inspired by FALSE and its relatives, whilst also being more human friendly.
Using relative offsets over absolute positions is the primary philosophy of MMRPN, no labels, nor line numbers.
Commands
Each line must have only one command, breakpoint, or comment.
While not part of the standard, MMRPN reserves "!!!" to be a breakpoint, if you write an interpreter or compiler, your code should at least behave like a breakpoint and its line doesn't exist
Literals
This language has rational and string literals, there are 4 ways to input a Rational:
- Integers e.g. 10
- Ratio of 2 integers e.g. 3/5
- Terminating decimal e.g. 3.894
- Recurring decimal e.g. 1.1[32] -or- 0.[3]
You can prepend a hyphen to negate the value, and periods can be substituted with commas.
String literals can only be in an instruction's parameters,
they are double quoted strings that allow spaces; most literal UTF-8 characters; along with \\, \", and \n escape characters.
Instructions
There are 2 types of input a command has, Parameters and Arguments. Parameters are integers placed after the command and separated by spaces. Arguments are from the stack and can be rational (R) or matrices (M), if needed; variables will be denoted [top] a, b, c, ... [bottom]
Standard operations
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
" | Comment (see examples below) | None | None |
+ | Addition | None | 2 R or 2 M |
- | Subtraction (b - a) | None | 2 R |
* | Multiplication | None | 2 R, 2 M, or 1 R and 1 M |
/ | Division (b / a) | None | 2 R |
% | Remainder (b % a) | None | 2 R |
int | Integer part | None | 1 R |
floor | Round down | None | 1 R |
ceil | Round up | None | 1 R |
den | Denominator | None | 1 R |
gcd | Greatest common divisor | None | 2 R |
lcm | Least common multiple | None | 2 R |
Checks and comparisons
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
# | Stack height | None | None |
step | Greater than zero (a > 0 → 1, a <= 0 → 0) | None | 1 R |
hyperStep | Divides by infinity, by cheating (a == -∞ → -1, a == ∞ → 1, otherwise → 0) |
None | 1 R |
compare | Comparison (a > b → -1, a == b → 0, a < b → 1) | None | 2 R |
query | looks at an element in the stack whose depth is given by the parameter, if it's rational then outputs 0, if it's a matrix, outputs 1 | Depth | None |
Stack manipulation
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
>> | Copy | Depth | None |
-> | Pull | Depth | None |
<- | Push | Depth | None |
del | Delete | Depth | None |
Matrices
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
m | Matrix | Columns, rows | columns * row R |
unzip | Inverse of m command |
None | 1 M |
addRow | Row addition | Row from, row to | 1 R and 1 M |
scaleRow | Row scaling | Row | 1 R and 1 M |
swapRows | Row swapping | Row from, row to | 1 M |
T | Transpose | None | 1 M |
inv | Inverse | None | 1 R or 1 M |
ref | Row echelon form | None | 1 M |
ind | Value at index | Column, row | 1 M |
rip | Rip last column | None | 1 M |
flip | Flip across rows | None | 1 M |
dim | Dimensions | None | 1 M |
dot | Dot product | None | 2 M |
hadamard | Hadamard product | None | 2 M |
kronecker | Kronecker product | None | 2 M |
outer | Outer product | None | 2 M |
Loops
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
jmp | Jump to | Offset | None |
repeat | Repeat loop | End Count | None |
next | Next iteration | None | None |
>>> | Iteration number | Loop Depth | None |
break | Break out of current loop | None | None |
leap | Break out of current loop, then jump | Offset | None |
Variables
Variable names can not have spaces nor start with ".
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
hold | Store top of stack as variable | Name | 1 R or 1 M |
place | Place copy of variable to stack | Name | None |
lose | Remove a variable | Name | None |
exists | Exists (does variable exist? yes → 1, no → 0) | Name | None |
IO & strings
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
chars | Converts a string's UTF-8 code points in reverse order (end is at the bottom), then tops it with the string's length. | String | None |
inputR | Prompts the user to input a number in a valid format as described above, if invalid, the program will ask again. The prompt message is an optional parameter, and not giving it should default to a generic message. |
Prompt (optional) | None |
inputS | Prompts the user to input a string, converts the response's UTF-8 code points in reverse order (end is at the bottom), then tops it with the string's length. The prompt message is an optional parameter, and not giving it should default to a generic message. |
Prompt (optional) | None |
inputSE | Prompts the user to input a string potentially containing escaped characters (especially \n & \\), converts the response's UTF-8 code points in reverse order (end is at the bottom), then tops it with the string's length. The prompt message is an optional parameter, and not giving it should default to a generic message. |
Prompt (optional) | None |
outputC | Outputs a single character whose code point is the parameter | Code point | None |
outputS | Outputs a string | String | None |
outputV | Pops and outputs a rational or matrix, format depends on implementation | None | 1 R or 1 M |
error | Outputs a string as an error message, and stops execution | String | None |
Lambda functions
Operation symbol/name | Description / Formula | Parameters | Arguments |
---|---|---|---|
run | interprets a row matrix as a string, then interprets the string | None | 1 M (single row) |
end | Stops the current program, useful with run commands |
None | None |
halt | Halt and catch fire | none | none |
Tip: I recommend you write any code normally, place chars "
before the first instruction then use a macro that presses the End, Delete, \, n keys in that order exactly once. AutoHotKey is great for this.
Notes on recursion
While recursion is allowed with run
, please avoid deep recursion, instead use a system akin to the first #Ackermann function example, with a return path using jmp
.
The run
command should only be used if:
- You're calling a sequence of commands multiple times, in different contexts (i.e. both inside and outside a
repeat...next
block). - You need to jump over a code block that can be changed by another person.
- A function is a parameter of another function, like Array.reduce().
Dynamic parameters
You might be thinking how if-else statements are going to happen in this language; fortunately, parameters can be controlled by the program, these get rounded towards 0 if the rational is not an integer.
- ]n grabs the nth item from the top of the stack
- [n grabs the nth item from the bottom of the stack
- $name uses a variable
Behavior
Certain commands have specific actions given interesting conditions, here's a list of those conditions.
Infinity
In normal mathematics, dividing by zero (0) is disallowed, due to inevitable paradoxes that occur, but guess what? This is a programming language, where having a value of greater magnitude is useful! so infinity is here:
"Positive infinity 1/0 "Negative infinity "-1/0
While Infinity does work in other situations, it gets real weird. If you subtract infinity from infinity, multiply infinity by zero, or other zany operations, you'll run into a special "Indeterminate form" error, when MMRPN can't give a good answer.
Repeat without next
If a repeat
is missing a corresponding next
, It is inferred to be at the end of the program, but the loop won't repeat. Either break
or leap
will end the program if run in an incomplete repeat
statement.
repeat 10 3 5 break 5 1 "next statement inferred here!
Nested loops
repeat ... next
blocks can be nested, helpful for simplifying advanced patterns
repeat 10 repeat 10 "outer >>> 1 "inner >>> 0 + 2 - next next m 10 10
Out of the loop
calling break
or leap
outside the active loop results in an error.
It is recommend to keep looping code in the repeat...next
block.
repeat 10 jmp 2 next "Error! break
Leaping back
leap
with a negative parameter behaves like break
, this is to dissuade the act of jumping into the repeat you just left.
"You would expect this to be an infinite loop, but it isn't. repeat 1 "I'm now a break! leap -2 next
Zero jmp
When jmp
is given a parameter of 0, jmp
does nothing, this is so you don't have to write 1 +
every time you use it. See examples below.
Examples
More examples can be found here
Creating conditions
Conditions are often created via the step
or compare
instructions, then by evaluating an polynomial function and feeding that into a jmp
, complex branching behavior can appear.
"change me to 1, 2 or 3 1 "1 → "1", 2 → "21", 3 -> "31" >> 0 >> 0 * -2 / -> 1 2 / 4 + + jmp ]0 outputS "3" jmp 2 outputS "2" outputS "1"
You can also use repeat
with a parameter of 1 or 0, to run or skip a block of lines.
"0 or 1 0 repeat ]0 1 2 3 4 "dummy 1 next del 0
Fibonnaci
This program repeatedly adds the previous 2 numbers forever
0 1 >> 1 >> 1 + jmp -3
We can use the repeat instruction to limit the repetitions
0 1 "repeat 50 times... repeat 50 "indentation of code is optional, but preferred >> 1 >> 1 + next
Factorial
Calculating the factorial is also easy
"argument 5 1 repeat ]1 >>> 0 * next del 1 "120 should be alone
Coefficients of a cubic
Using the matrix commands, one can solve a linear system quickly.
"degree + 1 repeat 4 1 >>> 0 1 - "degree repeat 3 >> 1 >> 1 * -> 1 next del 0 m 1 4 next "degree repeat 3 aug next T flip inv "f(0) 0 "f(1) 1 "f(2) 4 "f(3) -13 m 1 4 * "x^3, x^2, x^1, x^0
Cat
Takes the input and returns it; Unless you send the empty string, then it terminates.
inputS hold len step 3 * jmp ]0 del 0 jmp 7 del 0 repeat $len outputC ]0 del 0 next jmp -13
99 bottles of beer
Use the IO commands and get very hammered.
99 repeat 97 >> 0 >> 0 1 - >> 0 <- 3 <- 3 outputV outputS " bottles of beer on the wall,\n" outputV outputS " bottles of beer.\nTake one down pass it around,\n" outputV outputS " bottles of beer on the wall.\n" next del 0 "Deal with singular bottle. 1 2 2 outputV outputS " bottles of beer on the wall,\n" outputV outputS " bottles of beer.\nTake one down pass it around,\n" outputV outputS " bottle of beer on the wall.\n" 1 1 outputV outputS " bottle of beer on the wall,\n" outputV outputS " bottle of beer.\nTake one down pass it around,\nOops, we ran out of beer!"
Using run
With the assistance of other commands, you can create functions with a similar syntax to FALSE.
chars "outputS \"Hello, world!\"" hold len del 0 m $len 1 flip hold hello del 0 "... place hello run
You can also use aug
in various ways to create dynamic functions, such as creating unique variable names
Fractran interpreter
It's fitting that a language based on rationals can run Fractran. Here's John Conway's prime program.
"program 17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1 # hold len del 0 m $len 1 "input 2 "loop -> 1 >> 1 repeat $len "read fraction >>> 0 1 - -> 2 ind ]1 0 -> 1 <- 3 del 1 "check validity >> 1 * >> 0 1 % step 2 * jmp ]0 "success leap 2 "failure del 0 del 0 next "exit jmp 4 "update input del 0 del 1 "iterate again jmp -27
Because we can run Fractran directly, this proves MMRPN is Turing-complete
Brainfuck interpreter
It is quite monstrous, at 356 lines, I put it in it's own section: go view it →
Quine
I have 2 quines available for you, again they are big, so here's another section: go view it →
Implicit plotter
Want to use the worst plotter know to man? Yes? Ok then, take a look →
Ackermann function
This function requires recursion; with a little planning however, you can use the stack as a call stack too!
"m 3 "n 3 "---- 43 jmp 3 "return path -> 1 jmp ]0 "begin A(m,n) form m, n, return location <- 2 "m==0 >> 1 step 6 * jmp ]0 "true del 0 "n + 1 del 1 1 + "return jmp -12 "false "n==0 del 0 >> 0 step 10 * jmp ]0 "true del 0 del 0 1 - 1 25 "A(m-1,1) jmp -23 del 0 "return jmp -27 "false del 0 >> 1 -> 1 1 - 34 "A(m, n-1) jmp -32 del 0 -> 1 1 - -> 1 41 "A(m-1,A(m,n-1)) jmp -39 del 0 "return jmp -43 "end A(m,n) del 0 outputV
If you want to use run
:
3 3 outputS "A(" >> 1 outputV outputS ", " >> 0 outputV outputS ")" chars ">> 1\nstep\n6\n*\njmp ]0\ndel 0\ndel 1\n1\n+\nend\ndel 0\n>> 0\nstep\n9\n*\njmp ]0\ndel 0\ndel 0\n1\n-\n1\nplace Ackermann\nrun\nend\ndel 0\n1\n-\n>> 1\n1\n-\n<- 2\nrepeat 2\nplace Ackermann\nrun\nnext\nend" hold Ackermann del 0 m $Ackermann 1 flip hold Ackermann run outputS " = " outputV
Examples elsewhere on this site
- A+B problem
- FizzBuzz
- Half hearted
- Kolakoski sequence
- Looping counter
- Never gonna give you up
- Pi alpha function
- Shape machine
- User:XKCD_Random_Number#MoreMathRPN