Fastlane
Introduction
Fastlane is an esoteric language created by User:TuxCrafting that has a peculiar way of doing flow control by controlling the "speed" of the instruction pointer.
Semantics
There are 26 arbitrary precision counters, a
to z
, of which only one can be selected at any given time.
By default, a
is selected.
The instruction pointer also has a "speed", which is the number by which it is incremented at the end of each executed instruction. The speed defaults to 1 and can be negative.
The instruction pointer wraps around; execution must be explicitely halted to stop.
Instructions
Instruction | Description |
---|---|
a - z |
Select a counter. |
> |
Increment the instruction pointer speed. |
< |
Decrement the instruction pointer speed. |
+ |
Increment the currently selected counter. |
- |
Decrement the currently selected counter. |
* |
Skip the next instruction. |
% |
Skip the next instruction if the currently selected counter is zero. |
@ |
Skip the next instruction if the currently selected counter is nonzero. |
! |
Output the numeric value of the currently selected counter. |
? |
Input the numeric value of the currently selected counter. Undefined behavior if the input cannot be parsed. |
. |
Output the character value of the currently selected counter. |
, |
Input the character value of the currently selected counter. EOF is 0. |
$ |
Halt execution. |
Every other character (including whitespace and newlines) is a no-op (It is customary to use #
).
Examples
(Unoptimized) Hello world
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++++++++++++.+++++++..+++.-------------------------------------------------------------------.------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++.++++++++++++++++++++++++.+++.------.--------.-------------------------------------------------------------------.-----------------------.$
Truth machine
?!@$*>!<
Add two numbers
#?#b#?#*<a@>*b*!*$-b+a%>b#!#$
Computational class
Fastlane is Turing complete by translating a 25-counter Minsky machine into it.
The Minsky machine has three instructions:
INC [counter] [next]
, increments a counter, where[counter]
is the counter number (1-25) and[next]
the address of the next instruction.DEC [counter] [nextZ] [nextNZ]
, decrements a counter, where[counter]
is the counter number (1-25),[nextZ]
the address of the next instruction if the counter was zero and[nextNZ]
the address of the next instruction if the counter was nonzero.HLT
, halts.
The uses of the Fastlane counters are:
a
: Current address.b-z
: Minsky machine counters.
Each instruction is encoded as:
INC [counter] [next]
is[counter letter]+a[next times +]
DEC [counter] [nextZ] [nextNZ]
is[counter letter]@>>
… Well, kind of. There also needs to be two other jump instructions at a special place in the interleaved body. See a bit below.
HLT
is$
DEC
instructions are then prepended by two dummy instructions which will be replaced later. If the DEC
instruction was at the start of the program, a dummy jump is added at the start.
The instructions are then all translated to their Fastlane representation. The order is reversed, and if there are more than 2 instructions, the first instruction in the list is placed at the last position.
Then, for each DEC
instruction:
- The instruction right after becomes
####<-a[nextNZ times +]
- The instruction two after becomes
####<a[nextZ times +]
They are then all padded with NOPs and interleaved like abcdabcdabcd
…
Then, a way to examine the state and execute the corresponding snippet, an “accelerator”, is required.
The accelerator is formed by:
- Start with
a
. - If there is more than 1 instruction:
- Append
@>-#>
- If there are exactly 2 instructions:
- Append
#
- Append
- If there are more than 2 instructions:
- Set
i
to be from 1 to the number of instructions minus 2:- Append
[i times #]@[i times #][i+2 times >][i+1 times #]-
- Append
- Set
- Append
And at the end, a way of decelerating back to 1 to wrap back is required.
The decelerator is formed by:
- Start with an empty string.
- If there is more than 1 instruction:
- Set
i
to be from the number of instructions minus 1 to 1:- Append
[i times <]#*[i-1 times #]<
- Append
- Set
The final code is the concatenation of the accelerator, the interleaved body, and the decelerator.
Implementation
Simple implementation in C, requires GNU MP.
// Fastlane interpreter in C. // Build with: cc -o fastlane fastlane.c -lgmp #include <gmp.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <file>\n", argv[0]); return 1; } FILE *f = strcmp(argv[1], "-") == 0 ? stdin : fopen(argv[1], "r"); if (f == NULL) { fprintf(stderr, "Can't open file.\n"); return 1; } char *code = NULL; size_t code_len = 0; size_t code_i = 0; while (!ferror(f) && !feof(f)) { if (code_i >= code_len) { code = realloc(code, code_len += 512); } char c; if ((c = fgetc(f)) != EOF) { code[code_i++] = c; } } if (f != stdin) { fclose(f); } ptrdiff_t ip = 0; ptrdiff_t ip_speed = 1; mpz_t counters[26]; size_t counter = 0; char running = 1; for (size_t i = 0; i < 26; i++) { mpz_init(counters[i]); } #define NEXT ip = (ip + ip_speed) % code_i while (running) { char c = code[ip]; if (c >= 'a' && c <= 'z') { counter = c - 'a'; } switch (c) { case '>': ip_speed += 1; break; case '<': ip_speed -= 1; break; case '+': mpz_add_ui(counters[counter], counters[counter], 1); break; case '-': mpz_sub_ui(counters[counter], counters[counter], 1); break; case '*': ip += ip_speed; break; case '%': if (mpz_cmp_ui(counters[counter], 0) == 0) NEXT; break; case '@': if (mpz_cmp_ui(counters[counter], 0) != 0) NEXT; break; case '!': mpz_out_str(stdout, 10, counters[counter]); putchar('\n'); break; case '?': mpz_inp_str(counters[counter], stdin, 10); break; case '.': putchar(mpz_get_ui(counters[counter]) & 255); break; case ',': c = getchar(); mpz_set_ui(counters[counter], c == EOF ? 0 : c & 255); break; case '$': running = 0; } NEXT; } for (size_t i = 0; i < 26; i++) { mpz_clear(counters[i]); } return 0; }