Fastlane

From Esolang
Jump to navigation Jump to search

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 #
    • 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 #]-

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 #]<

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;
}