Source: lib/util/text_parser.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.TextParser');
  7. goog.require('goog.asserts');
  8. /**
  9. * Reads elements from strings.
  10. */
  11. shaka.util.TextParser = class {
  12. /**
  13. * @param {string} data
  14. */
  15. constructor(data) {
  16. /**
  17. * @const
  18. * @private {string}
  19. */
  20. this.data_ = data;
  21. /** @private {number} */
  22. this.position_ = 0;
  23. }
  24. /** @return {boolean} Whether it is at the end of the string. */
  25. atEnd() {
  26. return this.position_ == this.data_.length;
  27. }
  28. /**
  29. * Reads a line from the parser. This will read but not return the newline.
  30. * Returns null at the end.
  31. *
  32. * @return {?string}
  33. */
  34. readLine() {
  35. return this.readRegexReturnCapture_(/(.*?)(\n|$)/gm, 1);
  36. }
  37. /**
  38. * Reads a word from the parser. This will not read or return any whitespace
  39. * before or after the word (including newlines). Returns null at the end.
  40. *
  41. * @return {?string}
  42. */
  43. readWord() {
  44. return this.readRegexReturnCapture_(/[^ \t\n]*/gm, 0);
  45. }
  46. /**
  47. * Skips any continuous whitespace from the parser. Returns null at the end.
  48. */
  49. skipWhitespace() {
  50. this.readRegex(/[ \t]+/gm);
  51. }
  52. /**
  53. * Reads the given regular expression from the parser. This requires the
  54. * match to be at the current position; there is no need to include a head
  55. * anchor.
  56. * This requires that the regex have the global flag to be set so that it can
  57. * set lastIndex to start the search at the current position. Returns null at
  58. * the end or if the regex does not match the current position.
  59. *
  60. * @param {!RegExp} regex
  61. * @return {Array.<string>}
  62. */
  63. readRegex(regex) {
  64. const index = this.indexOf_(regex);
  65. if (this.atEnd() || index == null || index.position != this.position_) {
  66. return null;
  67. }
  68. this.position_ += index.length;
  69. return index.results;
  70. }
  71. /**
  72. * Reads a regex from the parser and returns the given capture.
  73. *
  74. * @param {!RegExp} regex
  75. * @param {number} index
  76. * @return {?string}
  77. * @private
  78. */
  79. readRegexReturnCapture_(regex, index) {
  80. if (this.atEnd()) {
  81. return null;
  82. }
  83. const ret = this.readRegex(regex);
  84. if (!ret) {
  85. return null;
  86. } else {
  87. return ret[index];
  88. }
  89. }
  90. /**
  91. * Returns the index info about a regular expression match.
  92. *
  93. * @param {!RegExp} regex
  94. * @return {?{position: number, length: number, results: !Array.<string>}}
  95. * @private
  96. */
  97. indexOf_(regex) {
  98. // The global flag is required to use lastIndex.
  99. goog.asserts.assert(regex.global, 'global flag should be set');
  100. regex.lastIndex = this.position_;
  101. const results = regex.exec(this.data_);
  102. if (results == null) {
  103. return null;
  104. } else {
  105. return {
  106. position: results.index,
  107. length: results[0].length,
  108. results: results,
  109. };
  110. }
  111. }
  112. };