#include <stdio.h>
#include <stdlib.h>

#include <jsapi.h>
#include <jsparse.h>
#include <jsscan.h>

/* from jsscan.h */
const char * TOKENS[81] = {
  "EOF", "EOL", "SEMI", "COMMA", "ASSIGN", "HOOK", "COLON", "OR", "AND",
  "BITOR", "BITXOR", "BITAND", "EQOP", "RELOP", "SHOP", "PLUS", "MINUS", "STAR",
  "DIVOP", "UNARYOP", "INC", "DEC", "DOT", "LB", "RB", "LC", "RC", "LP", "RP",
  "NAME", "NUMBER", "STRING", "OBJECT", "PRIMARY", "FUNCTION", "EXPORT",
  "IMPORT", "IF", "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
  "BREAK", "CONTINUE", "IN", "VAR", "WITH", "RETURN", "NEW", "DELETE",
  "DEFSHARP", "USESHARP", "TRY", "CATCH", "FINALLY", "THROW", "INSTANCEOF",
  "DEBUGGER", "XMLSTAGO", "XMLETAGO", "XMLPTAGC", "XMLTAGC", "XMLNAME",
  "XMLATTR", "XMLSPACE", "XMLTEXT", "XMLCOMMENT", "XMLCDATA", "XMLPI", "AT",
  "DBLCOLON", "ANYNAME", "DBLDOT", "FILTER", "XMLELEM", "XMLLIST", "RESERVED",
  "LIMIT",
};

const int NUM_TOKENS = sizeof(TOKENS) / sizeof(TOKENS[0]);

void print_tree(JSParseNode * root, int indent) {
  if (root == NULL) {
    return;
  }
  printf("%*s", indent, "");
  if (root->pn_type >= NUM_TOKENS) {
    printf("UNKNOWN");
  }
  else {
    printf("%s: starts at line %d, column %d, ends at line %d, column %d",
           TOKENS[root->pn_type],
           root->pn_pos.begin.lineno, root->pn_pos.begin.index,
           root->pn_pos.end.lineno, root->pn_pos.end.index);
  }
  printf("\n");
  switch (root->pn_arity) {
  case PN_UNARY:
    print_tree(root->pn_kid, indent + 2);
    break;
  case PN_BINARY:
    print_tree(root->pn_left, indent + 2);
    print_tree(root->pn_right, indent + 2);
    break;
  case PN_TERNARY:
    print_tree(root->pn_kid1, indent + 2);
    print_tree(root->pn_kid2, indent + 2);
    print_tree(root->pn_kid3, indent + 2);
    break;
  case PN_LIST:
    {
      JSParseNode * p;
      for (p = root->pn_head; p != NULL; p = p->pn_next) {
        print_tree(p, indent + 2);
      }
    }
    break;
  case PN_FUNC:
  case PN_NAME:
  case PN_NULLARY:
    break;
  default:
    fprintf(stderr, "Unknown node type\n");
    exit(EXIT_FAILURE);
    break;
  }
}

int main(void) {
  JSRuntime * runtime;
  JSContext * context;
  JSObject * global;
  JSTokenStream * token_stream;
  JSParseNode * node;

  runtime = JS_NewRuntime(8L * 1024L * 1024L);
  if (runtime == NULL) {
    fprintf(stderr, "cannot create runtime");
    exit(EXIT_FAILURE);
  }

  context = JS_NewContext(runtime, 8192);
  if (context == NULL) {
    fprintf(stderr, "cannot create context");
    exit(EXIT_FAILURE);
  }

  global = JS_NewObject(context, NULL, NULL, NULL);
  if (global == NULL) {
    fprintf(stderr, "cannot create global object");
    exit(EXIT_FAILURE);
  }

  if (! JS_InitStandardClasses(context, global)) {
    fprintf(stderr, "cannot initialize standard classes");
    exit(EXIT_FAILURE);
  }

  token_stream = js_NewFileTokenStream(context, NULL, stdin);
  if (token_stream == NULL) {
    fprintf(stderr, "cannot create token stream from file\n");
    exit(EXIT_FAILURE);
  }

  node = js_ParseTokenStream(context, global, token_stream);
  if (node == NULL) {
    fprintf(stderr, "parse error in file\n");
    exit(EXIT_FAILURE);
  }

  print_tree(node, 0);

  JS_DestroyContext(context);
  JS_DestroyRuntime(runtime);
}
