I wrote an interpreter in C for this language. I've named the interpreter "buttf*ck" since this is a f*cked-up version of brainf*ck. The interpreter is pretty minimal. I made no attempt to write "good" code, I used unsafe C functions like strcpy(), and I made no attempt to free memory (but it gets freed after the program exits anyway). The parser also doesn't really report errors correctly, unless you compile a debug version. Oh, and in some cases it'll break on really large programs/strings. But here it is anyway, all 413 lines of sexy interpreter code:
CODE
/*
* buttf*ck.c - Interpreter for the Buttf*ck programming language.
* <http://www.createblog.com/forums/index.php?showtopic=240400>
* Compile:
* $ gcc -o buttf*ck buttf*ck.c
*/
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TKN_BUF_SIZE 256
#define MAX_PATH 256
#define FIRST_LINE 1
#define FIRST_COL 1
typedef enum {ERR_BAD_ARGS = 1, ERR_OUT_OF_MEMORY, ERR_BAD_FILE} bf_memory_error;
typedef enum {TKN_STRING = 0, TKN_LOCATION, TKN_EOF, TKN_ERROR} bf_token_type;
typedef enum {
S_START, S_DONE, S_EOF, S_STRING,
S_LOCATION, S_TILDE, S_DATA, S_ERROR
} bf_token_state;
const char *TOKEN_NAMES[] = {"STRING", "LOCATION", "EOF", "ERROR"};
#define PLUS_MASK 0xffffffff
#define MINUS_MASK 0x0
typedef struct {
bf_token_type type;
unsigned int line;
unsigned int col;
char *val;
} bf_token;
typedef struct {
const char *path;
FILE *stream;
unsigned int line;
unsigned int col;
} bf_tokenizer;
typedef struct {
bf_tokenizer *stream;
char *path;
char *str;
} bf_parser;
static char *expand_tildes(char *origpath, char *newpath)
{
char *home = NULL;
char *xpath = NULL;
home = getenv("HOME");
xpath = strchr(origpath, '~');
xpath++;
sprintf(newpath, "%s%s", home, xpath);
return newpath;
}
static bf_token *bf_create_token(bf_token_type type, unsigned int line, unsigned int col)
{
bf_token *tkn = NULL;
tkn = (bf_token *) malloc(sizeof(bf_token));
if (!tkn) {
perror("bf_create_token");
exit(ERR_OUT_OF_MEMORY);
}
tkn->type = type;
tkn->line = FIRST_LINE;
tkn->col = FIRST_COL;
tkn->val = NULL;
return tkn;
}
static void bf_free_token(bf_token *t)
{
free(t->val);
free(t);
}
static char *bf_token_format(bf_token *t)
{
char *s;
s = (char *) malloc(64);
if (!s) {
perror("bf_token_format");
exit(ERR_OUT_OF_MEMORY);
}
s = strcpy(s, TOKEN_NAMES[t->type]);
if (t->val) {
sprintf(s, "%s(%s)", s, t->val);
}
sprintf(s, "%s [%u]", s, t->line);
return s;
}
static bf_tokenizer *bf_create_tokenizer(const char *path)
{
bf_tokenizer *t = NULL;
FILE *fh = NULL;
t = (bf_tokenizer *) malloc(sizeof(bf_tokenizer));
if (!t) {
perror("bf_create_tokenizer");
exit(ERR_OUT_OF_MEMORY);
}
t->path = path;
fh = fopen(path, "r");
if (!fh) {
perror("bf_create_tokenizer");
exit(ERR_BAD_FILE);
}
t->stream = fh;
t->line = 0;
t->col = 0;
return t;
}
static void bf_free_tokenizer(bf_tokenizer *t)
{
fclose(t->stream);
t->stream = NULL;
free(t);
}
static unsigned char bf_getc(bf_tokenizer *t)
{
int c;
c = getc(t->stream);
t->col++;
switch (c) {
case '\t':
c = ' ';
t->col += 7;
break;
case '\n':
c = ' ';
t->line++;
break;
case EOF:
c = '\';
break;
}
return (unsigned char) c;
}
static unsigned char bf_ungetc(bf_tokenizer *t, unsigned char ch)
{
int c;
c = ungetc(ch, t->stream);
return c != EOF ? (unsigned char) c : '\';
}
static bf_token *bf_next_token(bf_tokenizer *t)
{
bf_token_type type;
bf_token *tkn = NULL;
bf_token_state state = S_START;
char *buf = NULL;
unsigned int bufpos = 0;
unsigned int bitpos = 0;
unsigned int masked = 0;
unsigned int bitmask = 0;
buf = (char *) malloc(TKN_BUF_SIZE);
if (!buf) {
perror("bf_next_token");
exit(ERR_OUT_OF_MEMORY);
}
while (state != S_DONE) {
unsigned char ch = bf_getc(t);
switch (state) {
case S_START:
switch (ch) {
case '\':
state = S_DONE;
tkn = bf_create_token(TKN_EOF, t->line, t->col);
break;
case '.':
state = S_START;
break;
case '[':
state = S_STRING;
break;
case '~':
state = S_TILDE;
break;
default:
#ifdef DEBUG
fprintf(stderr, "Error (START), expected EOF, '.', '[', or '~', got %c\n", ch);
#endif
state = S_ERROR;
buf[bufpos++] = ch;
break;
}
break;
case S_DATA:
switch (ch) {
case '[':
state = S_DATA;
break;
case '>':
bitpos++;
if (bitpos > 7) {
unsigned char bc = (unsigned char) masked;
if (!isprint(bc)) {
fprintf(stderr, "Warning: non-printable character %#x, "
"line %u, col %u\n",
bc, t->line, t->col);
}
buf[bufpos++] = bc;
bitpos = 0;
masked = 0;
}
bitmask = (unsigned int) pow(2, (7 - bitpos));
break;
case '+':
state = S_DATA;
masked |= (PLUS_MASK & bitmask);
break;
case '-':
state = S_DATA;
masked |= (MINUS_MASK & bitmask);
break;
case ']':
state = S_DONE;
buf[bufpos++] = (unsigned char) masked;
tkn = bf_create_token(type, t->line, t->col);
break;
default:
#ifdef DEBUG
fprintf(stderr, "Error (%s), expected '[', '>', '+', '-', ']', got %c\n",
(type == TKN_STRING ? "STRING" : "LOCATION"), ch);
#endif
state = S_ERROR;
buf[bufpos++] = ch;
break;
}
break;
case S_TILDE:
switch (ch) {
case '[':
state = S_LOCATION;
break;
default:
#ifdef DEBUG
fprintf(stderr, "Error, expected '~', got %c\n", ch);
#endif
state = S_ERROR;
buf[bufpos++] = ch;
break;
}
break;
case S_STRING:
state = S_DATA;
type = TKN_STRING;
break;
case S_LOCATION:
state = S_DATA;
type = TKN_LOCATION;
break;
case S_ERROR:
if (ch == '\') {
state = S_DONE;
tkn = bf_create_token(TKN_ERROR, t->line, t->col);
} else {
buf[bufpos++] = ch;
}
break;
}
}
buf[bufpos] = '\'; /* Terminate that string, son! */
tkn->val = buf;
return tkn;
}
static bf_parser *bf_create_parser(bf_tokenizer *t)
{
bf_parser *p = NULL;
p = (bf_parser *) malloc(sizeof(bf_parser));
if (!p) {
perror("bf_create_parser");
exit(ERR_OUT_OF_MEMORY);
}
p->stream = t;
p->path = NULL;
p->str = NULL;
return p;
}
static void bf_free_parser(bf_parser *p)
{
bf_free_tokenizer(p->stream);
free(p->path);
free(p->str);
free(p);
}
static void bf_parse_string(bf_parser *p)
{
bf_token *t = NULL;
t = bf_next_token(p->stream);
switch (t->type) {
case TKN_STRING:
p->str = t->val;
break;
default:
fprintf(stderr, "Error: Line %u, Col %u\n", t->line, t->col);
break;
}
}
static void bf_parse_location(bf_parser *p)
{
bf_token *t = NULL;
t = bf_next_token(p->stream);
switch (t->type) {
case TKN_LOCATION:
p->path = t->val;
break;
case TKN_EOF:
break;
default:
fprintf(stderr, "Error: Line %u, Col %u\n", t->line, t->col);
break;
}
}
static int bf_parser_run(bf_parser *p)
{
FILE *fh = NULL;
char path[MAX_PATH];
size_t len = 0;
bf_parse_string(p);
bf_parse_location(p);
fprintf(stderr, "Path: %s\n", p->path);
fprintf(stderr, "String: %s\n", p->str);
expand_tildes(p->path, path);
fh = fopen(path, "w");
if (!fh) {
perror("bf_parser_run");
exit(ERR_BAD_FILE);
}
len = strlen(p->str);
if (fwrite(p->str, len, 1, fh) < 1) {
perror("bf_parser_run");
exit(ERR_BAD_FILE);
}
fclose(fh);
return 0;
}
int main(int argc, char **argv)
{
bf_tokenizer *tokenizer = NULL;
bf_parser *parser = NULL;
if (argc < 2) {
fprintf(stderr, "Usage: buttf*ck source.bf\n");
exit(ERR_BAD_ARGS);
}
tokenizer = bf_create_tokenizer(argv[1]);
parser = bf_create_parser(tokenizer);
return bf_parser_run(parser);
}
Source file:
buttf*ck.c (because CB's code box messes up the format a bit)
(That URL doesn't
really have a '*' in it; I assume you know how to change it.)
P.S. Your code is a bit off in some places -- "hello, world!" isn't printed. "hello, v????B" is printed instead, where '?' are unprintable characters. I think the problem starts with the 'v' -- looks like you may have skipped a bit.