"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }

var PatternParser = /*#__PURE__*/function () {
  function PatternParser() {
    _classCallCheck(this, PatternParser);
  }

  _createClass(PatternParser, [{
    key: "parse",
    value: function parse(pattern) {
      this.context = [{
        or: true,
        instructions: []
      }];
      this.parsePattern(pattern);

      if (this.context.length !== 1) {
        throw new Error('Non-finalized contexts left when pattern parse ended');
      }

      var _this$context$ = this.context[0],
          branches = _this$context$.branches,
          instructions = _this$context$.instructions;

      if (branches) {
        return {
          op: '|',
          args: branches.concat([expandSingleElementArray(instructions)])
        };
      }
      /* istanbul ignore if */


      if (instructions.length === 0) {
        throw new Error('Pattern is required');
      }

      if (instructions.length === 1) {
        return instructions[0];
      }

      return instructions;
    }
  }, {
    key: "startContext",
    value: function startContext(context) {
      this.context.push(context);
    }
  }, {
    key: "endContext",
    value: function endContext() {
      this.context.pop();
    }
  }, {
    key: "getContext",
    value: function getContext() {
      return this.context[this.context.length - 1];
    }
  }, {
    key: "parsePattern",
    value: function parsePattern(pattern) {
      if (!pattern) {
        throw new Error('Pattern is required');
      }

      var match = pattern.match(OPERATOR);

      if (!match) {
        if (ILLEGAL_CHARACTER_REGEXP.test(pattern)) {
          throw new Error("Illegal characters found in a pattern: ".concat(pattern));
        }

        this.getContext().instructions = this.getContext().instructions.concat(pattern.split(''));
        return;
      }

      var operator = match[1];
      var before = pattern.slice(0, match.index);
      var rightPart = pattern.slice(match.index + operator.length);

      switch (operator) {
        case '(?:':
          if (before) {
            this.parsePattern(before);
          }

          this.startContext({
            or: true,
            instructions: [],
            branches: []
          });
          break;

        case ')':
          if (!this.getContext().or) {
            throw new Error('")" operator must be preceded by "(?:" operator');
          }

          if (before) {
            this.parsePattern(before);
          }

          if (this.getContext().instructions.length === 0) {
            throw new Error('No instructions found after "|" operator in an "or" group');
          }

          var _this$getContext = this.getContext(),
              branches = _this$getContext.branches;

          branches.push(expandSingleElementArray(this.getContext().instructions));
          this.endContext();
          this.getContext().instructions.push({
            op: '|',
            args: branches
          });
          break;

        case '|':
          if (!this.getContext().or) {
            throw new Error('"|" operator can only be used inside "or" groups');
          }

          if (before) {
            this.parsePattern(before);
          } // The top-level is an implicit "or" group, if required.


          if (!this.getContext().branches) {
            // `branches` are not defined only for the root implicit "or" operator.

            /* istanbul ignore else */
            if (this.context.length === 1) {
              this.getContext().branches = [];
            } else {
              throw new Error('"branches" not found in an "or" group context');
            }
          }

          this.getContext().branches.push(expandSingleElementArray(this.getContext().instructions));
          this.getContext().instructions = [];
          break;

        case '[':
          if (before) {
            this.parsePattern(before);
          }

          this.startContext({
            oneOfSet: true
          });
          break;

        case ']':
          if (!this.getContext().oneOfSet) {
            throw new Error('"]" operator must be preceded by "[" operator');
          }

          this.endContext();
          this.getContext().instructions.push({
            op: '[]',
            args: parseOneOfSet(before)
          });
          break;

        /* istanbul ignore next */

        default:
          throw new Error("Unknown operator: ".concat(operator));
      }

      if (rightPart) {
        this.parsePattern(rightPart);
      }
    }
  }]);

  return PatternParser;
}();

exports["default"] = PatternParser;

function parseOneOfSet(pattern) {
  var values = [];
  var i = 0;

  while (i < pattern.length) {
    if (pattern[i] === '-') {
      if (i === 0 || i === pattern.length - 1) {
        throw new Error("Couldn't parse a one-of set pattern: ".concat(pattern));
      }

      var prevValue = pattern[i - 1].charCodeAt(0) + 1;
      var nextValue = pattern[i + 1].charCodeAt(0) - 1;
      var value = prevValue;

      while (value <= nextValue) {
        values.push(String.fromCharCode(value));
        value++;
      }
    } else {
      values.push(pattern[i]);
    }

    i++;
  }

  return values;
}

var ILLEGAL_CHARACTER_REGEXP = /[\(\)\[\]\?\:\|]/;
var OPERATOR = new RegExp( // any of:
'(' + // or operator
'\\|' + // or
'|' + // or group start
'\\(\\?\\:' + // or
'|' + // or group end
'\\)' + // or
'|' + // one-of set start
'\\[' + // or
'|' + // one-of set end
'\\]' + ')');

function expandSingleElementArray(array) {
  if (array.length === 1) {
    return array[0];
  }

  return array;
}
//# sourceMappingURL=AsYouTypeFormatter.PatternParser.js.map