// Generated by CoffeeScript 1.9.3
var $, BOM, CJSX_ESC_COMMENT, CLOSING_TAG, COMMENT, HEREDOC, HEREGEX, JSTOKEN, OPENING_TAG, PRAGMA, ParseTreeBranchNode, ParseTreeLeafNode, Parser, REGEX, SIMPLESTR, TAG_ATTRIBUTES, TRAILING_SPACES, WHITESPACE, compact, count, invertLiterate, last, ref, repeat, starts, throwSyntaxError;

ref = require('./helpers'), count = ref.count, starts = ref.starts, compact = ref.compact, last = ref.last, repeat = ref.repeat, throwSyntaxError = ref.throwSyntaxError, invertLiterate = ref.invertLiterate;

$ = require('./symbols');

ParseTreeLeafNode = function(type, value) {
  if (value == null) {
    value = null;
  }
  return {
    type: type,
    value: value
  };
};

ParseTreeBranchNode = function(type, value, children) {
  if (value == null) {
    value = null;
  }
  if (children == null) {
    children = [];
  }
  return {
    type: type,
    value: value,
    children: children
  };
};

module.exports = Parser = (function() {
  function Parser() {}

  Parser.prototype.parse = function(code, opts) {
    var consumed, i, message, ref1, ref2;
    this.opts = opts != null ? opts : {};
    this.parseTree = ParseTreeBranchNode(this.opts.root || $.ROOT);
    this.activeStates = [this.parseTree];
    this.chunkLine = 0;
    this.chunkColumn = 0;
    this.cjsxPragmaChecked = false;
    code = this.clean(code);
    i = 0;
    while ((this.chunk = code.slice(i))) {
      if (this.activeStates.length === 0) {
        break;
      }
      consumed = ((ref1 = this.currentState()) !== $.CJSX_EL && ref1 !== $.CJSX_ATTRIBUTES ? this.csComment() || this.csHeredoc() || this.csString() || this.csRegex() || this.jsEscaped() : void 0) || this.cjsxStart() || this.cjsxAttribute() || this.cjsxComment() || this.cjsxEscape() || this.cjsxUnescape() || this.cjsxEnd() || this.cjsxText() || this.coffeescriptCode();
      ref2 = this.getLineAndColumnFromChunk(consumed), this.chunkLine = ref2[0], this.chunkColumn = ref2[1];
      i += consumed;
    }
    if ((this.activeBranchNode() != null) && this.activeBranchNode() !== this.parseTree) {
      message = "Unexpected end of input: unclosed " + (this.currentState());
      throwSyntaxError(message, {
        first_line: this.chunkLine,
        first_column: this.chunkColumn
      });
    }
    this.remainder = code.slice(i);
    if (!this.opts.recursive) {
      if (this.remainder.length) {
        throwSyntaxError("Unexpected return from root state", {
          first_line: this.chunkLine,
          first_column: this.chunkColumn
        });
      }
    }
    return this.parseTree;
  };

  Parser.prototype.csComment = function() {
    var comment, here, match, pragmaMatch, prefix;
    if (!(match = this.chunk.match(COMMENT))) {
      return 0;
    }
    comment = match[0], here = match[1];
    if (!this.cjsxPragmaChecked) {
      this.cjsxPragmaChecked = true;
      if (pragmaMatch = comment.match(PRAGMA)) {
        if (pragmaMatch && pragmaMatch[1] && pragmaMatch[1].length) {
          prefix = pragmaMatch[1];
        } else {
          prefix = 'React.DOM';
        }
        this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_PRAGMA, prefix));
        return comment.length;
      }
    }
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS_COMMENT, comment));
    return comment.length;
  };

  Parser.prototype.csHeredoc = function() {
    var heredoc, match;
    if (!(match = HEREDOC.exec(this.chunk))) {
      return 0;
    }
    heredoc = match[0];
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS_HEREDOC, heredoc));
    return heredoc.length;
  };

  Parser.prototype.csString = function() {
    var quote, string;
    switch (quote = this.chunk.charAt(0)) {
      case "'":
        string = SIMPLESTR.exec(this.chunk)[0];
        break;
      case '"':
        string = this.balancedString(this.chunk, '"');
    }
    if (!string) {
      return 0;
    }
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS_STRING, string));
    return string.length;
  };

  Parser.prototype.csRegex = function() {
    var flags, length, match, ref1, regex;
    if (this.chunk.charAt(0) !== '/') {
      return 0;
    }
    if (length = this.csHeregex()) {
      return length;
    }
    if (!(match = REGEX.exec(this.chunk))) {
      return 0;
    }
    ref1 = match, match = ref1[0], regex = ref1[1], flags = ref1[2];
    if (regex.indexOf("\n") > -1) {
      return 0;
    }
    if (regex === '//') {
      return 0;
    }
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS_REGEX, match));
    return match.length;
  };

  Parser.prototype.csHeregex = function() {
    var body, flags, heregex, match;
    if (!(match = HEREGEX.exec(this.chunk))) {
      return 0;
    }
    heregex = match[0], body = match[1], flags = match[2];
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS_HEREGEX, heregex));
    return heregex.length;
  };

  Parser.prototype.jsEscaped = function() {
    var match, script;
    if (!(this.chunk.charAt(0) === '`' && (match = JSTOKEN.exec(this.chunk)))) {
      return 0;
    }
    script = match[0];
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.JS_ESC, script));
    return script.length;
  };

  Parser.prototype.cjsxStart = function() {
    var attributesText, input, match, selfClosing, tagName;
    if (!(match = OPENING_TAG.exec(this.chunk))) {
      return 0;
    }
    input = match[0], tagName = match[1], attributesText = match[2], selfClosing = match[3];
    if (!(selfClosing || this.chunk.indexOf("</" + tagName + ">", input.length) > -1)) {
      return 0;
    }
    this.pushActiveBranchNode(ParseTreeBranchNode($.CJSX_EL, tagName));
    this.pushActiveBranchNode(ParseTreeBranchNode($.CJSX_ATTRIBUTES));
    return 1 + tagName.length;
  };

  Parser.prototype.cjsxAttribute = function() {
    var attrName, bareVal, cjsxEscVal, doubleQuotedVal, input, match, singleQuotedVal, spreadAttr, whitespace;
    if (this.currentState() !== $.CJSX_ATTRIBUTES) {
      return 0;
    }
    if (this.chunk.charAt(0) === '/') {
      if (this.chunk.charAt(1) === '>') {
        this.popActiveBranchNode();
        this.popActiveBranchNode();
        return 2;
      } else {
        throwSyntaxError("/ without immediately following > in CJSX tag " + (this.peekActiveState(2).value), {
          first_line: this.chunkLine,
          first_column: this.chunkColumn
        });
      }
    }
    if (this.chunk.charAt(0) === '>') {
      this.popActiveBranchNode();
      return 1;
    }
    if (!(match = TAG_ATTRIBUTES.exec(this.chunk))) {
      return 0;
    }
    input = match[0], attrName = match[1], doubleQuotedVal = match[2], singleQuotedVal = match[3], cjsxEscVal = match[4], bareVal = match[5], spreadAttr = match[6], whitespace = match[7];
    if (attrName) {
      if (doubleQuotedVal != null) {
        this.addLeafNodeToActiveBranch(ParseTreeBranchNode($.CJSX_ATTR_PAIR, null, [ParseTreeLeafNode($.CJSX_ATTR_KEY, "\"" + attrName + "\""), ParseTreeLeafNode($.CJSX_ATTR_VAL, "\"" + doubleQuotedVal + "\"")]));
        return input.length;
      } else if (singleQuotedVal != null) {
        this.addLeafNodeToActiveBranch(ParseTreeBranchNode($.CJSX_ATTR_PAIR, null, [ParseTreeLeafNode($.CJSX_ATTR_KEY, "\"" + attrName + "\""), ParseTreeLeafNode($.CJSX_ATTR_VAL, "'" + singleQuotedVal + "'")]));
        return input.length;
      } else if (cjsxEscVal) {
        this.pushActiveBranchNode(ParseTreeBranchNode($.CJSX_ATTR_PAIR));
        this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_ATTR_KEY, "\"" + attrName + "\""));
        return input.indexOf('{');
      } else if (bareVal) {
        this.addLeafNodeToActiveBranch(ParseTreeBranchNode($.CJSX_ATTR_PAIR, null, [ParseTreeLeafNode($.CJSX_ATTR_KEY, "\"" + attrName + "\""), ParseTreeLeafNode($.CJSX_ATTR_VAL, bareVal)]));
        return input.length;
      } else {
        this.addLeafNodeToActiveBranch(ParseTreeBranchNode($.CJSX_ATTR_PAIR, null, [ParseTreeLeafNode($.CJSX_ATTR_KEY, "\"" + attrName + "\""), ParseTreeLeafNode($.CJSX_ATTR_VAL, 'true')]));
        return input.length;
      }
    } else if (spreadAttr) {
      this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_ATTR_SPREAD, spreadAttr));
      return input.length;
    } else if (whitespace != null) {
      this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_WHITESPACE, whitespace));
      return input.length;
    } else {
      return throwSyntaxError("Invalid attribute " + input + " in CJSX tag " + (this.peekActiveState(2).value), {
        first_line: this.chunkLine,
        first_column: this.chunkColumn
      });
    }
  };

  Parser.prototype.cjsxComment = function() {
    var match;
    match = this.chunk.match(CJSX_ESC_COMMENT);
    if (!match) {
      return 0;
    }
    this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_COMMENT, match[1]));
    return match[0].length;
  };

  Parser.prototype.cjsxEscape = function() {
    var ref1;
    if (!(this.chunk.charAt(0) === '{' && ((ref1 = this.currentState()) === $.CJSX_EL || ref1 === $.CJSX_ATTR_PAIR))) {
      return 0;
    }
    this.pushActiveBranchNode(ParseTreeBranchNode($.CJSX_ESC));
    this.activeBranchNode().stack = 1;
    return 1;
  };

  Parser.prototype.cjsxUnescape = function() {
    var ref1;
    if (!(this.currentState() === $.CJSX_ESC && this.chunk.charAt(0) === '}')) {
      return 0;
    }
    if (this.activeBranchNode().stack === 0) {
      this.popActiveBranchNode();
      if ((ref1 = this.currentState()) === $.CJSX_ATTR_PAIR) {
        this.popActiveBranchNode();
      }
      return 1;
    } else {
      return 0;
    }
  };

  Parser.prototype.cjsxEnd = function() {
    var input, match, tagName;
    if (this.currentState() !== $.CJSX_EL) {
      return 0;
    }
    if (!(match = CLOSING_TAG.exec(this.chunk))) {
      return 0;
    }
    input = match[0], tagName = match[1];
    if (tagName !== this.activeBranchNode().value) {
      throwSyntaxError("opening CJSX tag " + (this.activeBranchNode().value) + " doesn't match closing CJSX tag " + tagName, {
        first_line: this.chunkLine,
        first_column: this.chunkColumn
      });
    }
    this.popActiveBranchNode();
    return input.length;
  };

  Parser.prototype.cjsxText = function() {
    if (this.currentState() !== $.CJSX_EL) {
      return 0;
    }
    if (this.newestNode().type !== $.CJSX_TEXT) {
      this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CJSX_TEXT, ''));
    }
    this.newestNode().value += this.chunk.charAt(0);
    return 1;
  };

  Parser.prototype.coffeescriptCode = function() {
    if (this.currentState() === $.CJSX_ESC) {
      if (this.chunk.charAt(0) === '{') {
        this.activeBranchNode().stack++;
      } else if (this.chunk.charAt(0) === '}') {
        this.activeBranchNode().stack--;
        if (this.activeBranchNode().stack === 0) {
          return 0;
        }
      }
    }
    if (this.newestNode().type !== $.CS) {
      this.addLeafNodeToActiveBranch(ParseTreeLeafNode($.CS, ''));
    }
    this.newestNode().value += this.chunk.charAt(0);
    return 1;
  };

  Parser.prototype.activeBranchNode = function() {
    return last(this.activeStates);
  };

  Parser.prototype.peekActiveState = function(depth) {
    if (depth == null) {
      depth = 1;
    }
    return this.activeStates.slice(-depth)[0];
  };

  Parser.prototype.currentState = function() {
    return this.activeBranchNode().type;
  };

  Parser.prototype.newestNode = function() {
    return last(this.activeBranchNode().children) || this.activeBranchNode();
  };

  Parser.prototype.pushActiveBranchNode = function(node) {
    this.activeBranchNode().children.push(node);
    return this.activeStates.push(node);
  };

  Parser.prototype.popActiveBranchNode = function() {
    return this.activeStates.pop();
  };

  Parser.prototype.addLeafNodeToActiveBranch = function(node) {
    return this.activeBranchNode().children.push(node);
  };

  Parser.prototype.clean = function(code) {
    var ref1;
    if (code.charCodeAt(0) === BOM) {
      code = code.slice(1);
    }
    code = code.replace(/\r/g, '');
    if ((ref1 = this.opts) != null ? ref1.literate : void 0) {
      code = invertLiterate(code);
    }
    return code;
  };

  Parser.prototype.getLineAndColumnFromChunk = function(offset) {
    var column, lineCount, lines, string;
    if (offset === 0) {
      return [this.chunkLine, this.chunkColumn];
    }
    if (offset >= this.chunk.length) {
      string = this.chunk;
    } else {
      string = this.chunk.slice(0, +(offset - 1) + 1 || 9e9);
    }
    lineCount = count(string, '\n');
    column = this.chunkColumn;
    if (lineCount > 0) {
      lines = string.split('\n');
      column = last(lines).length;
    } else {
      column += string.length;
    }
    return [this.chunkLine + lineCount, column];
  };

  Parser.prototype.balancedString = function(str, end) {
    var continueCount, i, j, letter, match, prev, ref1, stack;
    continueCount = 0;
    stack = [end];
    for (i = j = 1, ref1 = str.length; 1 <= ref1 ? j < ref1 : j > ref1; i = 1 <= ref1 ? ++j : --j) {
      if (continueCount) {
        --continueCount;
        continue;
      }
      switch (letter = str.charAt(i)) {
        case '\\':
          ++continueCount;
          continue;
        case end:
          stack.pop();
          if (!stack.length) {
            return str.slice(0, +i + 1 || 9e9);
          }
          end = stack[stack.length - 1];
          continue;
      }
      if (end === '}' && (letter === '"' || letter === "'")) {
        stack.push(end = letter);
      } else if (end === '}' && letter === '/' && (match = HEREGEX.exec(str.slice(i)) || REGEX.exec(str.slice(i)))) {
        continueCount += match[0].length - 1;
      } else if (end === '}' && letter === '{') {
        stack.push(end = '}');
      } else if (end === '"' && prev === '#' && letter === '{') {
        stack.push(end = '}');
      }
      prev = letter;
    }
    return throwSyntaxError("missing " + (stack.pop()) + ", starting");
  };

  return Parser;

})();

OPENING_TAG = /^<(@?[-A-Za-z0-9_\.]+)((?:(?:(?:\s+[\w-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:{[\s\S]*?})|[^>\s]+)))|\s+[\w-]+|\s+\{\.\.\.\s*?[^\s{}]+?\s*?\})?)*?\s*)(\/?)>/;

CLOSING_TAG = /^<\/(@?[-A-Za-z0-9_\.]+)[^>]*>/;

TAG_ATTRIBUTES = /(?:([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|(?:{((?:\\.|[\s\S])*)})|([^>\s]+)))?)|(?:\{\.\.\.(\s*?[^\s{}]+?\s*?)\})|([\s\n]+)/;

PRAGMA = /^\s*#\s*@cjsx\s+(\S*)/i;

CJSX_ESC_COMMENT = /^\{#(.*)\}/;

BOM = 65279;

WHITESPACE = /^[^\n\S]+/;

COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;

TRAILING_SPACES = /\s+$/;

HEREDOC = /^("""|''')((?:\\[\s\S]|[^\\])*?)(?:\n[^\n\S]*)?\1/;

SIMPLESTR = /^'[^\\']*(?:\\[\s\S][^\\']*)*'/;

JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;

REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/;

HEREGEX = /^\/{3}((?:\\?[\s\S])+?)\/{3}([imgy]{0,4})(?!\w)/;
