// Brainfuck prettyprinter.
//
// Mostly to show how to parse Brainfuck with error detection. Not
// much of a prettyprinter because it strips comments, without which
// Brainfuck isn't really readable, but it will at least show the
// control flow structure.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct node {
// The jmp is always positive and shows how far away the matching [
// or ] is.
int tok, val, jmp;
};
// The stack contains an entry for every enclosing [.
struct stack_entry {
int line, col; // Position of the [. Used for error reporting.
int open_abs; // Absolute index of [.
};
struct node* parse(FILE *f, int *size) {
int line = 1;
int col = 0;
int c;
int ast_capacity = 10;
int ast_next = 0;
struct node* ast = calloc(ast_capacity, sizeof(struct node));
int stack_capacity = 10;
int stack_top = 0;
struct stack_entry* stack = calloc(stack_capacity, sizeof(struct stack_entry));
while ((c = fgetc(f)) != EOF) {
if (ast_next == ast_capacity) {
ast_capacity *= 2;
ast = realloc(ast, ast_capacity * sizeof(struct node));
}
switch (c) {
case '[':
if (stack_top == stack_capacity) {
stack_capacity *= 2;
stack = realloc(stack, stack_capacity * sizeof(struct stack_entry));
}
ast[ast_next].tok = c;
stack[stack_top].open_abs = ast_next;
stack[stack_top].line = line;
stack[stack_top].col = col;
stack_top++;
ast_next++;
col++;
break;
case ']':
if (stack_top == 0) {
fprintf(stderr, "Unmatched right bracket at line %d column %d\n",
line, col);
free(ast);
ast = NULL;
goto end;
}
ast[ast_next].tok = c;
ast[ast_next].jmp = ast_next - stack[stack_top].open_abs;
ast[stack[stack_top].open_abs].jmp = ast_next - stack[stack_top].open_abs;
stack_top--;
col++;
ast_next++;
break;
case '+':
case '>':
ast[ast_next].tok = c;
ast[ast_next].val = 1;
col++;
ast_next++;
break;
case '-':
case '<':
ast[ast_next].tok = c;
ast[ast_next].val = -1;
col++;
ast_next++;
break;
case '\n':
col = 0;
line++;
break;
default:
col++;
break;
}
}
if (stack_top != 0) {
fprintf(stderr, "Unmatched left bracket at line %d column %d\n",
stack[stack_top-1].line, stack[stack_top-1].col);
free(ast);
ast = NULL;
}
end:
*size = ast_next-1;
free(stack);
return ast;
}
void spaces(int n) {
for (int i = 0; i < n; i++) {
putchar(' ');
}
}
void print_ast(struct node* ast, int n) {
int depth = 0;
for (int i = 0; i < n; i++) {
switch (ast[i].tok) {
case '[':
putchar('\n');
spaces(depth);
puts("[");
depth += 2;
spaces(depth);
break;
case ']':
putchar('\n');
depth -= 2;
spaces(depth);
puts("]");
spaces(depth);
break;
default:
putchar(ast[i].tok);
}
}
putchar('\n');
}
int main() {
int size;
struct node* nodes = parse(stdin, &size);
if (nodes) {
print_ast(nodes, size);
}
free(nodes);
}
Response:
text/plain