"use strict";Object.defineProperty(exports, "__esModule", {value: true});








var _index = require('../../tokenizer/index');
var _types = require('../../tokenizer/types');
var _base = require('../../traverser/base');
var _expression = require('../../traverser/expression');
var _util = require('../../traverser/util');
var _charcodes = require('../../util/charcodes');
var _identifier = require('../../util/identifier');
var _typescript = require('../typescript');

// Reads inline JSX contents token.
function jsxReadToken() {
  for (;;) {
    if (_base.state.pos >= _base.input.length) {
      _util.unexpected.call(void 0, "Unterminated JSX contents");
      return;
    }

    const ch = _base.input.charCodeAt(_base.state.pos);

    switch (ch) {
      case _charcodes.charCodes.lessThan:
      case _charcodes.charCodes.leftCurlyBrace:
        if (_base.state.pos === _base.state.start) {
          if (ch === _charcodes.charCodes.lessThan) {
            _base.state.pos++;
            _index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
            return;
          }
          _index.getTokenFromCode.call(void 0, ch);
          return;
        }
        _index.finishToken.call(void 0, _types.TokenType.jsxText);
        return;

      default:
        _base.state.pos++;
    }
  }
}

function jsxReadString(quote) {
  _base.state.pos++;
  for (;;) {
    if (_base.state.pos >= _base.input.length) {
      _util.unexpected.call(void 0, "Unterminated string constant");
      return;
    }

    const ch = _base.input.charCodeAt(_base.state.pos);
    if (ch === quote) {
      _base.state.pos++;
      break;
    }
    _base.state.pos++;
  }
  _index.finishToken.call(void 0, _types.TokenType.string);
}

// Read a JSX identifier (valid tag or attribute name).
//
// Optimized version since JSX identifiers can't contain
// escape characters and so can be read as single slice.
// Also assumes that first character was already checked
// by isIdentifierStart in readToken.

function jsxReadWord() {
  let ch;
  do {
    if (_base.state.pos > _base.input.length) {
      _util.unexpected.call(void 0, "Unexpectedly reached the end of input.");
      return;
    }
    ch = _base.input.charCodeAt(++_base.state.pos);
  } while (_identifier.IS_IDENTIFIER_CHAR[ch] || ch === _charcodes.charCodes.dash);
  _index.finishToken.call(void 0, _types.TokenType.jsxName);
}

// Parse next token as JSX identifier
function jsxParseIdentifier() {
  nextJSXTagToken();
}

// Parse namespaced identifier.
function jsxParseNamespacedName(identifierRole) {
  jsxParseIdentifier();
  if (!_index.eat.call(void 0, _types.TokenType.colon)) {
    // Plain identifier, so this is an access.
    _base.state.tokens[_base.state.tokens.length - 1].identifierRole = identifierRole;
    return;
  }
  // Process the second half of the namespaced name.
  jsxParseIdentifier();
}

// Parses element name in any form - namespaced, member
// or single identifier.
function jsxParseElementName() {
  jsxParseNamespacedName(_index.IdentifierRole.Access);
  while (_index.match.call(void 0, _types.TokenType.dot)) {
    nextJSXTagToken();
    jsxParseIdentifier();
  }
}

// Parses any type of JSX attribute value.
function jsxParseAttributeValue() {
  switch (_base.state.type) {
    case _types.TokenType.braceL:
      _index.next.call(void 0, );
      jsxParseExpressionContainer();
      nextJSXTagToken();
      return;

    case _types.TokenType.jsxTagStart:
      jsxParseElement();
      nextJSXTagToken();
      return;

    case _types.TokenType.string:
      nextJSXTagToken();
      return;

    default:
      _util.unexpected.call(void 0, "JSX value should be either an expression or a quoted JSX text");
  }
}

function jsxParseEmptyExpression() {
  // Do nothing.
}

// Parse JSX spread child, after already processing the {
// Does not parse the closing }
function jsxParseSpreadChild() {
  _util.expect.call(void 0, _types.TokenType.ellipsis);
  _expression.parseExpression.call(void 0, );
}

// Parses JSX expression enclosed into curly brackets, after already processing the {
// Does not parse the closing }
function jsxParseExpressionContainer() {
  if (_index.match.call(void 0, _types.TokenType.braceR)) {
    jsxParseEmptyExpression();
  } else {
    _expression.parseExpression.call(void 0, );
  }
}

// Parses following JSX attribute name-value pair.
function jsxParseAttribute() {
  if (_index.eat.call(void 0, _types.TokenType.braceL)) {
    _util.expect.call(void 0, _types.TokenType.ellipsis);
    _expression.parseMaybeAssign.call(void 0, );
    // }
    nextJSXTagToken();
    return;
  }
  jsxParseNamespacedName(_index.IdentifierRole.ObjectKey);
  if (_index.match.call(void 0, _types.TokenType.eq)) {
    nextJSXTagToken();
    jsxParseAttributeValue();
  }
}

// Parses JSX opening tag starting after "<".
// Returns true if the tag was self-closing.
// Does not parse the last token.
function jsxParseOpeningElement() {
  if (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
    // This is an open-fragment.
    return false;
  }
  jsxParseElementName();
  if (_base.isTypeScriptEnabled) {
    _typescript.tsTryParseJSXTypeArgument.call(void 0, );
  }
  while (!_index.match.call(void 0, _types.TokenType.slash) && !_index.match.call(void 0, _types.TokenType.jsxTagEnd) && !_base.state.error) {
    jsxParseAttribute();
  }
  const isSelfClosing = _index.match.call(void 0, _types.TokenType.slash);
  if (isSelfClosing) {
    // /
    nextJSXTagToken();
  }
  return isSelfClosing;
}

// Parses JSX closing tag starting after "</".
// Does not parse the last token.
function jsxParseClosingElement() {
  if (_index.match.call(void 0, _types.TokenType.jsxTagEnd)) {
    // Fragment syntax, so we immediately have a tag end.
    return;
  }
  jsxParseElementName();
}

// Parses entire JSX element, including its opening tag
// (starting after "<"), attributes, contents and closing tag.
// Does not parse the last token.
function jsxParseElementAt() {
  const isSelfClosing = jsxParseOpeningElement();
  if (!isSelfClosing) {
    nextJSXExprToken();
    while (true) {
      switch (_base.state.type) {
        case _types.TokenType.jsxTagStart:
          nextJSXTagToken();
          if (_index.match.call(void 0, _types.TokenType.slash)) {
            nextJSXTagToken();
            jsxParseClosingElement();
            return;
          }
          jsxParseElementAt();
          nextJSXExprToken();
          break;

        case _types.TokenType.jsxText:
          nextJSXExprToken();
          break;

        case _types.TokenType.braceL:
          _index.next.call(void 0, );
          if (_index.match.call(void 0, _types.TokenType.ellipsis)) {
            jsxParseSpreadChild();
            nextJSXExprToken();
          } else {
            jsxParseExpressionContainer();
            nextJSXExprToken();
          }

          break;

        // istanbul ignore next - should never happen
        default:
          _util.unexpected.call(void 0, );
          return;
      }
    }
  }
}

// Parses entire JSX element from current position.
// Does not parse the last token.
 function jsxParseElement() {
  nextJSXTagToken();
  jsxParseElementAt();
} exports.jsxParseElement = jsxParseElement;

// ==================================
// Overrides
// ==================================

 function nextJSXTagToken() {
  _base.state.tokens.push(new (0, _index.Token)());
  _index.skipSpace.call(void 0, );
  _base.state.start = _base.state.pos;
  const code = _base.input.charCodeAt(_base.state.pos);

  if (_identifier.IS_IDENTIFIER_START[code]) {
    jsxReadWord();
  } else if (code === _charcodes.charCodes.quotationMark || code === _charcodes.charCodes.apostrophe) {
    jsxReadString(code);
  } else {
    // The following tokens are just one character each.
    ++_base.state.pos;
    switch (code) {
      case _charcodes.charCodes.greaterThan:
        _index.finishToken.call(void 0, _types.TokenType.jsxTagEnd);
        break;
      case _charcodes.charCodes.lessThan:
        _index.finishToken.call(void 0, _types.TokenType.jsxTagStart);
        break;
      case _charcodes.charCodes.slash:
        _index.finishToken.call(void 0, _types.TokenType.slash);
        break;
      case _charcodes.charCodes.equalsTo:
        _index.finishToken.call(void 0, _types.TokenType.eq);
        break;
      case _charcodes.charCodes.leftCurlyBrace:
        _index.finishToken.call(void 0, _types.TokenType.braceL);
        break;
      case _charcodes.charCodes.dot:
        _index.finishToken.call(void 0, _types.TokenType.dot);
        break;
      case _charcodes.charCodes.colon:
        _index.finishToken.call(void 0, _types.TokenType.colon);
        break;
      default:
        _util.unexpected.call(void 0, );
    }
  }
} exports.nextJSXTagToken = nextJSXTagToken;

function nextJSXExprToken() {
  _base.state.tokens.push(new (0, _index.Token)());
  _base.state.start = _base.state.pos;
  jsxReadToken();
}
