2th/AuthorImplementation.c

From Esolang
Jump to navigation Jump to search
Back to 2th
// Easy implementation of a 2th interpreter.

#include <stdio.h>
#include <stdlib.h>

#define NUM_CELLS 10000

int isDigit(char x) {
    return '0' <= x && x <= '9';
}

// Function for finding bracket matches.
// I know this is a slow approach to use a while loop to find the matching bracket...
// (It is preferred to prepare a match table before hand)
int matchJump(int i, const char *program, size_t programLength, int dir) {
	dir = (dir > 0)? 1 : -1; // direction to search in
	int level = -dir;
	int i0 = i; // saving original value of i for error message
	while (level != 0 && 0 < i && i < programLength) {
		i += dir;
		switch (program[i]) {
		case '[': level--; break;
		case ']': level++; break;
		}
	}
	if (level == 0) {
		return i;
	}
	else {
		printf("SYNTAX ERROR: unmatched opening bracket '[' at index %d", i0);
		return -1;
	}
}

// Interprets your 2th program string.
void interpret2thProgram(const char *program, size_t programLength) {
    char tape[NUM_CELLS] = { 0 }; // data tape
	char r = 0;                   // the register
    size_t t = 0;   // data pointer index
	size_t i = 0;   // instruction pointer index
	size_t m = 0;   // mode flag for if we are in register mode
	size_t n = 1;   // multiplier for run-length encoding
	size_t resetN;  // extra flag for whether to reset n to 1 (removes redundancy from the switch cases)
    while (i < programLength) {
		resetN = 1;
        switch(program[i]) {
		case '+': // Increment
			if (m) { r += n; } else { tape[t] += n; }
			i++;
			break;
		case '-': // Decrement
			if (m) { r -= n; } else { tape[t] -= n; }
			i++;
			break;
		case '<': // Move left
			t -= n;
			if (t < 0) { t = 0; }
			i++;
			break;
		case '>': // Move right
			t += n;
			if (t >= NUM_CELLS) { t = NUM_CELLS - 1; }
			i++;
			break;
		case '%': // enter cell mode
			m = 1;
			i++;
			break;
		case '^': // enter register mode
			m = 0;
			i++;
			break;
		case '[': // Jump past matching bracket if current cell/R is 0 (depending on mode)
			if ((m && r == 0) || (!m && tape[t] == 0)) {
				i = matchJump(i, program,  programLength, 1);
				if (i < 0) exit(1);
			}
			i++;
			break;
		case ']': // Jump back to the matching bracket if current cell/R is not 0 (depending on mode)
			if ((m && r != 0) || (!m && tape[t] != 0)) {
				i = matchJump(i, program,  programLength, -1);
				if (i < 0) exit(1);
			}
			i++;
			break;
		case '?':
			// Get a character of input
			if (m) { r = getchar(); } else { tape[t] = getchar(); }
			i++;
			break;
		case '.':
			// Set a character of output
			if (m) { putchar(r); } else { putchar(tape[t]); }
			i++;
			break;
		default:
			if (isDigit(program[i])) {
				// Read a number for the (run length encoding) multiplier, n
				n = 0;
				while (isDigit(program[i])) {
					n = (n * 10) + (program[i] - '0');
					i++;
				}
				// This combines making sure that (n != 0) and that we do not reset n if it is valid:
				resetN = (n == 0);
			} else {
				// Ignore other characters
				i++;
			}
			break;
        }
		// this happens unless a number (digits) was read:
		if (resetN) {
			n = 1;
		}
    }
}

int main(int argc, char **argv) {
	if (argc != 2) {
		printf("%s: error: expected exactly 1 argument, the program string\n", argv[0]);
		exit(1);
	}
	interpret2thProgram(argv[1], strlen(argv[1]));
}