I THINK ∴ I'M DANGEROUS

Table of Contents

UBI

The Ultimate Brainfuck Interpreter or the Universal Brainfuck Interpreter.

ubi4

ubi4 (not pictured) eschews the ubi3 feature-set in favor of speed.

Todo

  • Add option parsing
  • Add optional timeout via alarm(2)

Code

ubi3.c
/*
	ubi3 - Ultimate Brainfuck Interpreter 3 
 
			- or -
 
	ubi3 - Universal Brainfuck Interpreter 3
 
	This is a bf interpreter based on ubi and Erik Bosman's bf 
	interpreter. The features and polish come from ubi. The linked 
	list and recursive loading (ideas), come from Erik Bosman. All
	code is original in origin.
 
	ubi3 has two priorities:
 
	1) Be featureful and complete.
	2) Be fast.
 
	ubi3 was written with those two points in mind, and they are 
	listed in order of importance. Erik Bosman's 8-bit bf 
	interpreter is slightly faster for some bf programs and his 16 
	bit version is decidely faster than ubi3.
 
	It's not called "ultimate/universal" for nothing. The purpose 
	behind ubi3 is to run any bf program out there. Granted, there 
	are a lot of bf-like languages out there, and really, ubi3 can 
	only run a small subset of those. If a language or script is 
	100% bf compatible, ubi3 should be able to run it. ubi3 handles 
	quirks such as cell and array wrapping, multiple EOF styles, and 
	8, 16, or 32 bit cell sizes.
 
 
	****************************
 
	If you lost my lovely make file, ubi3 can be compiled via:
 
	gcc -std=c99 -pedantic -Wall -O3 -ffast-math ubi3.c -o ubi3
 
	To my knowledge, ubi3 is completely c99 compliant and should 
	compile	with any c99 compliant compiler.
 
	****************************
 
	Exit Codes:
	0 = No Errors
	1 = Command line error
	2 = File error
 
	<><><><><><><><><><><><><><><><>
	TODO 
 
 
*/
 
#define UBI_VERSION "3.0b"
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> 
 
/*
#include <sys/types.h>
#include <sys/stat.h> */
 
#define CHKOPT(i,v) ((i&v)==v) /* check to see if an option is set. as macro for clarity */
 
typedef struct {
	char op;		/* operator */
	unsigned int amt;	/* ammount */
	void *loop;		/* enter loop */
	void *next;		/* next command */
} command;
 
struct {
	char cell_size;
	short options;
	char cell_bytes; /* how many bytes in the cell? */
	void *sheet_end; /* pointer to end of the sheet. */
	unsigned int sheet_size; /* how man cells in a sheet */
} config;
 
 
char	*sheet_8;
short 	*sheet_16;
int 	*sheet_32;
 
 
char *loadfile(const char *filename)
{
	/*
		loadfile() takes a filename as a paramter (char pointer)
		and loads it into memory, stripping out non-command characters
	*/
 
	FILE *fp;
	char *file_data;
	char *fd_start;
	char ch;
 
	if (!(fp=fopen(filename,"r")))
	{
		perror("Could not open file");
		exit(2);
	}
 
	fseek(fp, 0, SEEK_END);
	file_data = calloc(ftell(fp)+1, sizeof(char));
	fd_start = file_data;
	fseek(fp, 0, SEEK_SET);
 
	while(!feof(fp))
	{
		ch=fgetc(fp);
		switch(ch) /* switch case is easier and clearer than a bunch of logical ORs */
		{
			case '.': case '>': case '+': case ']':
			case ',': case '<': case '-': case '[':
				*(file_data++) = ch;
		}
	}
 
	/* free possibly unused memory */
	fd_start= realloc(fd_start, strlen(fd_start));
 
	return(fd_start);
}
 
char *code_optimize(char *cmd_string)
{
	/*
		optimizes a cmd_string. returns the same pointer passed to it.
	*/
 
	char *found;
	char ch, match;
 
 
	/* the below do/while loop removes any zero sum ops */
	do
	{
		ch=0; 
		for ( match=0; match<=12; match+=3 ) 
		{
			if ( (found = strstr(cmd_string, "+-\0-+\0><\0<>\0[]"+match )) )  /* what can I say? I started */
			{							 /* as a perl hacker. */
				*found=0;		/* this is my clever way */
				strcat(cmd_string, found+2);  /* of snipping out the offending code */
				ch=1;
			}
		}
	}while( ch );
 
 
	return(cmd_string);
}
 
command *preprocess(char **cmd_string)
{
	/* preprocess() recursively loads a cmd_string into command linked list	*/
	command *cmd = calloc(1, sizeof(command));
	command *first = cmd;
	do
	{
		cmd->op = **cmd_string;
 
		switch(cmd->op)
		{
			case '<': case '>': case '+': case '-':
				while( cmd->op == *((*cmd_string)++) ) cmd->amt++;
				(*cmd_string) -= 2;
			break;
 
			case '[':
				(*cmd_string)++;
				cmd->loop = preprocess(cmd_string);
			break;
 
			case ']':
				return(first);
			break;
 
			case '.': case ',':
			break;
		}
		cmd->next = calloc(1, sizeof(command));
		cmd = cmd->next;
	} while( *((*cmd_string)++) );
 
 
	return(first);
}
 
 
void bf_run_8(command *cmd)
{
	while(cmd->op)
	{
		switch(cmd->op)
		{
			case '<':
				sheet_8 -= cmd->amt;
			break;
 
			case '>':
				sheet_8 += cmd->amt;
			break;
 
			case '-':
				*sheet_8 -= cmd->amt;
			break;
 
			case '+':
				*sheet_8 += cmd->amt;
			break;
 
			case '[':
				while(*sheet_8) bf_run_8(cmd->loop);
			break;
 
			case '.':
				putchar(*sheet_8);
			break;
 
			case ',':
				*sheet_8=getchar();
			break;
 
			case ']':
				return;
			break;
		}
		cmd = cmd->next;
	}
}
 
void bf_run_16(command *cmd)
{
	while(cmd->op)
	{
		switch(cmd->op)
		{
			case '<':
				sheet_16 -= cmd->amt;
			break;
 
			case '>':
				sheet_16 += cmd->amt;
			break;
 
			case '-':
				*sheet_16 -= cmd->amt;
			break;
 
			case '+':
				*sheet_16 += cmd->amt;
			break;
 
			case '[':
				while(*sheet_16) bf_run_16(cmd->loop);
			break;
 
			case '.':
				putchar(*sheet_16);
			break;
 
			case ',':
				*sheet_16=getchar();
			break;
 
			case ']':
				return;
			break;
		}
		cmd = cmd->next;
	}
}
 
void bf_run_32(command *cmd)
{
	while(cmd->op)
	{
		switch(cmd->op)
		{
			case '<':
				sheet_32 -= cmd->amt;
			break;
 
			case '>':
				sheet_32 += cmd->amt;
			break;
 
			case '-':
				*sheet_32 -= cmd->amt;
			break;
 
			case '+':
				*sheet_32 += cmd->amt;
			break;
 
			case '[':
				while(*sheet_32) bf_run_32(cmd->loop);
			break;
 
			case '.':
				putchar(*sheet_32);
			break;
 
			case ',':
				*sheet_32=getchar();
			break;
 
			case ']':
				return;
			break;
		}
		cmd = cmd->next;
	}
}
 
void show_help(void)
{
	fprintf(stderr,"HI\n");
}
 
int main(int argc, char *argv[])
{
	char ch;
 
	/* set defaults */
	config.options = 0;
	config.cell_size = 0;
 
	while( (ch = getopt(argc, argv, "Oc:h?V")) != -1 )
	{
		switch(ch)
		{
			case 'O': /* toggle optimization */
				config.options ^= 1;
			break;
 
			case 'c': /* set cell bit size*/
				if (!
				     ( 
					(sscanf(optarg, "%d", (int*)&(config.cell_size)) == 1) &&
				   	( config.cell_size < 3 ) && 
				   	( config.cell_size > -1)
				     ) 
				   )
				{
					fprintf(stderr, "Bad parameter passed to -c. Valid parameters are 0 = 8 bits (Default); 1 = 16 bits; 2 = 32 bits\n");
					exit(1);
				}				
			break;
 
			case 's': /* set sheet size */
				if ( (sscanf(optarg, "%d", (int*)&(config.sheet_size)) == 1) )
				{
 
				}
 
			break;
			case 'h': case '?': /* show help, hur hur */
 
			show_help();
 
			/* no break here on purpose! */
			case 'V': /* show version */
				fprintf(stderr, "ubi version %s.\n\nCopyright (c) 2008 Matthew 'Zashi' Hiles.\n", UBI_VERSION);
				exit(0);
			break;
		}
	}
 
	if (argv[optind] == NULL)
	{
		/* no file to run specified? show usage? */
		fprintf(stderr, "Error: No source file specified. -h for help.\n");
		exit(1);
	}
 
	char *code = loadfile(argv[optind]);
 
	if (! CHKOPT(config.options,1)) code_optimize(code);
 
	command *prog = preprocess(&code);
 
	switch(config.cell_size)
	{
		case 0:
			sheet_8 = calloc( 30000, sizeof(char) );
			config.sheet_end = sheet_8 + ( 30000 * sizeof(char) );
			bf_run_8(prog);
		break;
 
		case 1:
			sheet_16 = calloc( 30000, sizeof(short) );
			config.sheet_end = sheet_16 + ( 30000 * sizeof(short) );
			bf_run_16(prog);
		break;
 
		case 2:
			sheet_32 = calloc( 30000, sizeof(int) );
			config.sheet_end = sheet_32 + ( 30000 * sizeof(int) );
			bf_run_32(prog);
		break;
	}
 
	return(0);
}