/*
 * bf.c
 *
 * gile's BrainFuck compiler.  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 FILE * bf_outf;

static int bf_chloop (char ch, char * one, char * many, char * tch, int start, int tlen)
{
	int len = start;
	register int i;
	if (tch[len + 1] != ch) {
		fprintf(bf_outf, "%s;\n", one);
	} else {
		for (i = 0; len < tlen; len++) {
			if ((tch[len] != ch) && ((tch[len] == '<') || (tch[len] == '>') ||
						(tch[len] == '+') || (tch[len] == '-') ||
						(tch[len] == '[') || (tch[len] == ']') ||
						(tch[len] == ',') || (tch[len] == '.')))
				break;
			else if (tch[len] != ch)
				continue;
			i++;
		}
		fprintf(bf_outf, "%s= %d;\n", many, i);
		len--;
	}
	return len;
}

int bf_interpret (char * tch, int tlen)
{
	char ch;
	int len;
	for (len = 0; len < tlen; len++) {
		switch (tch[len]) {
			case '<': len = bf_chloop('<', "posi--", "posi -", tch, len, tlen); break;
			case '>': len = bf_chloop('>', "posi++", "posi +", tch, len, tlen); break;
			case '+': len = bf_chloop('+', "stack[posi]++", "stack[posi] +", tch, len, tlen); break;
			case '-': len = bf_chloop('-', "stack[posi]--", "stack[posi] -", tch, len, tlen); break;
			case '[': fprintf(bf_outf, "while (stack[posi] != 0) {\n"); break;
			case ']': fprintf(bf_outf, "}\n"); break;
			case '.':
				fprintf(bf_outf, "putc(stack[posi], stdout);\nfflush(stdout);\n");
				break;
			case ',':
				fprintf(bf_outf, "i = getc(stdin);\nif (i != EOF)\n\tstack[posi] = i;\n");
				break;
		}
	}
	return 0;
}

int main (int argc, char ** argv)
{
	int i;
	FILE * fd;
	struct stat sb;
	char tempf[20] = "bfcc-temp.XXXXXX";
	char * bf_file;
	int bf_flen;
	if (argc < 2) {
		fprintf(stderr, "Usage: %s file binary\n", argv[0]);
		exit(1);
	}
	fd = fopen(argv[1], "r");
	if (!fd) {
		fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
		exit(1);
	}
	close(mkstemp(tempf));
	unlink(tempf);
	strcat(tempf, ".c");
	bf_outf = fopen(tempf, "w");
	if (!bf_outf) {
		fprintf(stderr, "bfcc: Could not create temporary file.\n");
		exit(1);
	}
	stat(argv[1], &sb);
	bf_flen = sb.st_size;
	bf_file = malloc(bf_flen + 1);
	if (!bf_file) {
		fprintf(stderr, "bfcc: Out of memory loading file.\n");
		exit(1);
	}
	read(fileno(fd), bf_file, bf_flen);
	fclose(fd);
	fprintf(bf_outf, "#include <stdio.h>\n\nint main (int argc, char ** argv)\n{\n");
	fprintf(bf_outf, "int stack[%d];\nint posi = 0;\nregister int i = 0;\n", BF_LIMIT);
	fprintf(bf_outf, "for (i = 0; i < %d; i++)\nstack[i] = 0;\n", BF_LIMIT);
	bf_interpret(bf_file, bf_flen);
	free(bf_file);
	fprintf(bf_outf, "exit(0);\n}\n\n");
	fclose(bf_outf);
	switch (fork()) {
		case -1: fprintf(stderr, "bfcc: Could not compile: %s\n", strerror(errno)); break;
		case 0:
			execlp("cc", "cc", tempf, "-o", argv[2], NULL);
			fprintf(stderr, "bfcc: Could not compile: %s\n", strerror(errno));
			_exit(127);
		default: wait(&i);
	}
	unlink(tempf);
	exit(0);
}

