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