Source: lib/util/text_parser.js

/**
 * @license
 * Copyright 2016 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

goog.provide('shaka.util.TextParser');

goog.require('goog.asserts');


/**
 * Reads elements from strings.
 *
 * @param {string} data
 * @constructor
 * @struct
 */
shaka.util.TextParser = function(data) {
  /**
   * @const
   * @private {string}
   */
  this.data_ = data;

  /** @private {number} */
  this.position_ = 0;
};


/** @return {boolean} Whether it is at the end of the string. */
shaka.util.TextParser.prototype.atEnd = function() {
  return this.position_ == this.data_.length;
};


/**
 * Reads a line from the parser.  This will read but not return the newline.
 * Returns null at the end.
 *
 * @return {?string}
 */
shaka.util.TextParser.prototype.readLine = function() {
  return this.readRegexReturnCapture_(/(.*?)(\n|$)/gm, 1);
};


/**
 * Reads a word from the parser.  This will not read or return any whitespace
 * before or after the word (including newlines).  Returns null at the end.
 *
 * @return {?string}
 */
shaka.util.TextParser.prototype.readWord = function() {
  return this.readRegexReturnCapture_(/[^ \t\n]*/gm, 0);
};


/**
 * Skips any continuous whitespace from the parser.  Returns null at the end.
 */
shaka.util.TextParser.prototype.skipWhitespace = function() {
  this.readRegex(/[ \t]+/gm);
};


/**
 * Reads the given regular expression from the parser.  This requires the match
 * to be at the current position; there is no need to include a head anchor.
 * This requires that the regex have the global flag to be set so that it can
 * set lastIndex to start the search at the current position.  Returns null at
 * the end or if the regex does not match the current position.
 *
 * @param {!RegExp} regex
 * @return {Array.<string>}
 */
shaka.util.TextParser.prototype.readRegex = function(regex) {
  let index = this.indexOf_(regex);
  if (this.atEnd() || index == null || index.position != this.position_) {
    return null;
  }

  this.position_ += index.length;
  return index.results;
};


/**
 * Reads a regex from the parser and returns the given capture.
 *
 * @param {!RegExp} regex
 * @param {number} index
 * @return {?string}
 * @private
 */
shaka.util.TextParser.prototype.readRegexReturnCapture_ = function(
    regex, index) {
  if (this.atEnd()) {
    return null;
  }

  let ret = this.readRegex(regex);
  if (!ret) {
    return null;
  } else {
    return ret[index];
  }
};


/**
 * Returns the index info about a regular expression match.
 *
 * @param {!RegExp} regex
 * @return {?{position: number, length: number, results: !Array.<string>}}
 * @private
 */
shaka.util.TextParser.prototype.indexOf_ = function(regex) {
  // The global flag is required to use lastIndex.
  goog.asserts.assert(regex.global, 'global flag should be set');

  regex.lastIndex = this.position_;
  let results = regex.exec(this.data_);
  if (results == null) {
    return null;
  } else {
    return {
      position: results.index,
      length: results[0].length,
      results: results,
    };
  }
};