/*
 * bb.c
 *
 * gile's Brainbomb interpreter.  Brainfuck - just more painful.
 *
 * (C) 2002 Kyle Donaldson <gile@gilex.net>
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

/*
 * Brainbomb - or, "Ooh, let's make it harder!"
 *
 * (Brainbomb is a derivative of Brainfuck)
 *
 * Brainfuck is a simple - yet highly obsfucated - Turing-complete
 * language.  It consists of eight operations:
 *
 *	+	Increment the current pointer.
 *	-	Decrement the current pointer.
 *	>	Increment the pointer index.
 *	<	Decrement the pointer index.
 *	[	Jump to matching ] if pointer is zero.
 *		Executes instructions contained inside elsewise.
 *	]	Jump to matching [ if pointer is not zero.
 *		Else exit loop.
 *	.	Print ASCII value of pointer to stdout.
 *	,	Retrive ASCII value from stdin and store in pointer.
 *
 * This implementation of Brainfuck has a 'stack' of 65536 pointers.
 * It is also 32-bit, as opposed to the standard 8-bit that BF was
 * originally written with.
 *
 * --------------------------
 *
 * Brainbomb arranges the memory stack in a square matrix.  There are 256
 * rows of data cells, each containing 256 cells of data.  Or, 256 column
 * cells containing 256 rows of data, if you prefer.
 *
 * This means expanding the Brainfuck instruction set:
 *
 *	^	Move up a row
 *	v	Move down a row
 *	'	Display the integer value of current cell
 *
 * If a program attempts to access a row or column beyond the limits, the
 * language automatically wraps around.  It just makes it more interesting.
 *
 * The 'print integer' operation arose out of the fact that this BF/BB
 * implementation is 32-bit.  That makes it just too painful to try to
 * convert and print in ASCII in code.  Try to represent 2147483648 in
 * ASCII when it is in ONE CELL ;)
 *
 * I didn't have a purpose for creating a two-dimensional Brainfuck, it just
 * seemed like fun at the time.  Enjoy.  And carefully plan your code.
*/

#define BB_ROW_LIMIT	256
#define BB_COL_LIMIT	256
#define BF_LIMIT	(BB_ROW_LIMIT * BB_COL_LIMIT)
#define BB_POSI		((bf_pos_r * BB_ROW_LIMIT) + bf_pos_c)

static int bf_ptr[BF_LIMIT];
static int bf_pos_r;
static int bf_pos_c;
static char * bf_file;
static int bf_fpos;
static int bf_flen;

int bf_interpret (char);

static int bf_loop (void)
{
	register int end;
	register int bc = 0;
	for (end = bf_fpos; end < bf_flen; end++) {
		if (bf_file[end] == '[')
			bc++;
		else if (bf_file[end] == ']')
			bc--;
		if (bc == 0)
			break;
	}
	bf_fpos++;
	if (bc != 0) {
		fprintf(stderr, "bb: Unmatched '[' character (byte %d).\n", bf_fpos);
		return -1;
	}
	if (bf_ptr[BB_POSI] == 0) {
		bf_fpos = end;
		return 0;
	}
	do {
		bc = bf_fpos;
		while (bf_fpos < end) {
			if (bf_interpret(bf_file[bf_fpos]) != 0)
				break;
			bf_fpos++;
		}
		bf_fpos = bc;
	} while (bf_ptr[BB_POSI] != 0);
	bf_fpos = end;
	return 0;
}

int bf_interpret (char tch)
{
	char ch;
	register int i;
	register int len;
	switch (tch) {
		case '<':
			bf_pos_c--;
			if (bf_pos_c < 0)
				bf_pos_c = (BB_COL_LIMIT - 1);
			break;
		case '>':
			bf_pos_c++;
			if (bf_pos_c >= BB_COL_LIMIT)
				bf_pos_c = 0;
			break;
		case '^':
			bf_pos_r--;
			if (bf_pos_r < 0)
				bf_pos_r = (BB_ROW_LIMIT - 1);
			break;
		case 'v':
			bf_pos_r++;
			if (bf_pos_r >= BB_ROW_LIMIT)
				bf_pos_r = 0;
			break;
		case '+': bf_ptr[BB_POSI]++; break;
		case '-': bf_ptr[BB_POSI]--; break;
		case '[': return bf_loop(); break;
		case ']': fprintf(stderr, "bb: Unmatched ']' character (byte %d).\n", bf_fpos);
		case '.': putc(bf_ptr[BB_POSI], stdout); fflush(stdout); break;
		case '\'': printf("%d", bf_ptr[BB_POSI] ); fflush(stdout); break;
		case ',':
			ch = getchar();
			if (ch == EOF)
				bf_ptr[BB_POSI] = 0;
			bf_ptr[BB_POSI] = ch;
			break;
	}
	return 0;
}

int main (int argc, char ** argv)
{
	register int i;
	FILE * fd;
	struct stat sb;
	bf_pos_c = 0;
	bf_pos_r = 0;
	bf_fpos = 0;
	for (i = 0; i < BF_LIMIT; i++)
		bf_ptr[i] = 0;
	if (argc < 2) {
		fprintf(stderr, "Usage: %s file\n", argv[0]);
		exit(1);
	}
	fd = fopen(argv[1], "r");
	if (!fd) {
		fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
		exit(1);
	}
	stat(argv[1], &sb);
	bf_flen = sb.st_size;
	bf_file = malloc(bf_flen + 1);
	if (!bf_file) {
		fprintf(stderr, "bb: Out of memory loading file.\n");
		exit(1);
	}
	read(fileno(fd), bf_file, bf_flen);
	for (bf_fpos = 0; bf_fpos < bf_flen; bf_fpos++)
		bf_interpret(bf_file[bf_fpos]);
	fclose(fd);
	exit(0);
}

