Arepo is an esoteric programming language invented by User:DoggyDogWhirl on October 15, 2021. It is a two-dimensional programming language inspired by Befunge, and by the concept of "time inversion" from Christopher Nolan's movie Tenet.
The name comes from the Sator Square, an ancient Latin palindrome which inspired parts of Tenet. In fact, "Arepo" is the name of an unseen character in the film.
Arepo has not been finished, as the language is being developed alongside an interpreter for it.
Arepo uses a single accumulator instead of a stack like Befunge. To perform a binary operation, the pointer and a past version of itself must both land on the operator at the same time. This is possible through time inversion.
Time inversion is a concept heavily featured in Tenet. An inverted object or person experiences time backwards, so they appear to a normal person as if they are being played in reverse.
For example, in Tenet there is a machine called a "turnstile". When an object enters one chamber, they exit the other chamber having been inverted. A person exiting a turnstile would be able to see themselves walking in, as they are now traveling back in time. This also means that they could have seen themselves exiting (in reverse) as they entered the turnstile. Their timeline might look like this:
<----------\ P ----------------/ t -------------------------->
The person and their "future" inverted self appear to walk into a turnstile and both disappear. The person does not exist past that time unless they invert again:
/--------------------> \----------\ P ----------------/ t -------------------------->
Three versions of the person exist at once, one of them inverted. Two of them seem to magically appear from the turnstile.
Arepo uses inversion to have multiple instruction pointers moving through a program at a given time. Each pointer only holds one integer value. These pointers can interact with each other if they both land on the same binary command.
To greatly simplify programming, there is no possible way to change events that have already been determined. This allows the simulation to follow the pointer forward and backward through time. The first time the pointer crosses a binary command, it is not affected. Any subsequent time, the pointer's value is changed to the result of it and the first pointer.
Here is a demonstration:
3 | v | + | + | v | 3
On its first pass, the pointer with value 3 enters and exits unscathed.
(This is not code, and the arrows are not commands. They're just a visual aid here.)
Suppose later, it has a value of 2 and crosses the plus sign.
3 | v | 2 > + | + > 5 | v | 3
As expected, it exits with value 5. Here's what happens if the second pointer is inverted:
3 | v | 2 < + | + < 5 | v | 3
It appears to be the same, but from its perspective:
| 3 | v + < 5 | 2 < + v | 3 |
The 3 is subtracted from the 5 instead of added. When inverted, the inverse function is applied, so the function always applies in forward time. Thus, it is not necessary to have any commands that are an inverse of another.
I am unsure of the Turing-completeness of Arepo. It is difficult to loop through a set of numbers, especially since commands always use the first instance of the pointer as an argument. Future versions may change its status, also.
In forward time, the following functions are the same as they are in Befunge, except of course that they now act on 1 or 2 pointers:
||UP: Sets the pointer's direction to up||unchanged|
||DOWN: Sets the pointer's direction to down|
||LEFT: Sets the pointer's direction to left|
||RIGHT: Sets the pointer's direction to right|
||RANDOM: Sets the pointer in a random direction||unchanged|
||PLUS: Adds the two values||MINUS: Subtracts from the first value|
||TIMES: Multiplies the two values||DIVIDE: Divides by the first value|
||MODULO: Finds the remainder after division||ERROR: If the second value is greater, exit with an error|
||HALT: Ends the program||unchanged|
||IF: Sets the pointer's direction to right if its value is 0, otherwise left||IF: Functions as the opposite IF|
||IF: Sets the pointer's direction to down if its value is 0, otherwise up|
||GREATER: Sets value to 1 if the first value is greater, otherwise 0||IN/DECREMENT: First value+1 if second value is 0, else value-1|
||NOT: Sets value to 1 if 0, otherwise 0||unchanged|
||BRIDGE: Jumps over the next command|
||LITERAL: sets the pointer to its hex value|
||OUTPUT: Outputs as an integer||INPUT: Gets an integer as input|
||OUTPUT: Outputs as a character||INPUT: Gets a character as input|
||GET: Gets the ASCII value of (first, second)||FIND: Index of the second value in the first value's column|
Inverse functions in italics break the "functions always work in normal time" rule, and otherwise should return errors for certain conditions. They do not to make programming in inverted time more enjoyable.
These commands have been changed:
||CONSTANT: Returns the value of the first pointer||ERROR: Exits if the values are not equal|
||CHARACTER: Gets the next character's ASCII value||CHARACTER: Gets the previous character's ASCII value|
PUT has not been implemented as it may change the past. While Tenet has a way to fix this problem, I do not.
This command is new:
||TURNSTILE: Moves the pointer to an adjacent |
P -----O | P -----OO--> <--O |
>O O,O O| @
I have more ideas for Arepo that I may add after implementing this version. For now, I just want to get a finished version out there, no matter how simple or trimmed down.
- Allow pointers to hold multiple items
- Pair up pointers on commands rather than always using the first one
- Remove italicized inverse commands (just to make things harder)
- Create a command that is pre-inverted: designed to work in inverted time
- Change previously simulated events
- Pointers wait on binary commands until another arrives
- SWAP: switches the values of two pointers
- PUT: "p" in Befunge, which would have to be a 3-argument operation or one that only puts symbols on the first row
- And possibly more!