// 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); }