/*
 * bf.c
 *
 * gile's BrainFuck interpreter.  fun fun...  Useless too, huh?
 *
 * (C) 2002 Kyle Donaldson <gile@gilex.net>
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

/*
 * 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.
*/

#define BF_LIMIT	65536

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

int bf_interpret (char);

static int bf_loop (void)
{
	int end;
	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, "bf: Unmatched '[' character (byte %d).\n", bf_fpos);
		return -1;
	}
	if (bf_ptr[bf_pos] == 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[bf_pos] != 0);
	bf_fpos = end;
	return 0;
}

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

int main (int argc, char ** argv)
{
	register int i;
	FILE * fd;
	struct stat sb;
	bf_pos = 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, "bf: 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);
}

