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.
HLTis$
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
ito 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
ito 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;
}