Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

202 строки
7.8 KiB

  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. /*!
  6. * XRegExp.matchRecursive 4.0.0
  7. * <xregexp.com>
  8. * Steven Levithan (c) 2009-2017 MIT License
  9. */
  10. exports.default = function (XRegExp) {
  11. /**
  12. * Returns a match detail object composed of the provided values.
  13. *
  14. * @private
  15. */
  16. function row(name, value, start, end) {
  17. return {
  18. name: name,
  19. value: value,
  20. start: start,
  21. end: end
  22. };
  23. }
  24. /**
  25. * Returns an array of match strings between outermost left and right delimiters, or an array of
  26. * objects with detailed match parts and position data. An error is thrown if delimiters are
  27. * unbalanced within the data.
  28. *
  29. * @memberOf XRegExp
  30. * @param {String} str String to search.
  31. * @param {String} left Left delimiter as an XRegExp pattern.
  32. * @param {String} right Right delimiter as an XRegExp pattern.
  33. * @param {String} [flags] Any native or XRegExp flags, used for the left and right delimiters.
  34. * @param {Object} [options] Lets you specify `valueNames` and `escapeChar` options.
  35. * @returns {Array} Array of matches, or an empty array.
  36. * @example
  37. *
  38. * // Basic usage
  39. * let str = '(t((e))s)t()(ing)';
  40. * XRegExp.matchRecursive(str, '\\(', '\\)', 'g');
  41. * // -> ['t((e))s', '', 'ing']
  42. *
  43. * // Extended information mode with valueNames
  44. * str = 'Here is <div> <div>an</div></div> example';
  45. * XRegExp.matchRecursive(str, '<div\\s*>', '</div>', 'gi', {
  46. * valueNames: ['between', 'left', 'match', 'right']
  47. * });
  48. * // -> [
  49. * // {name: 'between', value: 'Here is ', start: 0, end: 8},
  50. * // {name: 'left', value: '<div>', start: 8, end: 13},
  51. * // {name: 'match', value: ' <div>an</div>', start: 13, end: 27},
  52. * // {name: 'right', value: '</div>', start: 27, end: 33},
  53. * // {name: 'between', value: ' example', start: 33, end: 41}
  54. * // ]
  55. *
  56. * // Omitting unneeded parts with null valueNames, and using escapeChar
  57. * str = '...{1}.\\{{function(x,y){return {y:x}}}';
  58. * XRegExp.matchRecursive(str, '{', '}', 'g', {
  59. * valueNames: ['literal', null, 'value', null],
  60. * escapeChar: '\\'
  61. * });
  62. * // -> [
  63. * // {name: 'literal', value: '...', start: 0, end: 3},
  64. * // {name: 'value', value: '1', start: 4, end: 5},
  65. * // {name: 'literal', value: '.\\{', start: 6, end: 9},
  66. * // {name: 'value', value: 'function(x,y){return {y:x}}', start: 10, end: 37}
  67. * // ]
  68. *
  69. * // Sticky mode via flag y
  70. * str = '<1><<<2>>><3>4<5>';
  71. * XRegExp.matchRecursive(str, '<', '>', 'gy');
  72. * // -> ['1', '<<2>>', '3']
  73. */
  74. XRegExp.matchRecursive = function (str, left, right, flags, options) {
  75. flags = flags || '';
  76. options = options || {};
  77. var global = flags.indexOf('g') !== -1;
  78. var sticky = flags.indexOf('y') !== -1;
  79. // Flag `y` is controlled internally
  80. var basicFlags = flags.replace(/y/g, '');
  81. var escapeChar = options.escapeChar;
  82. var vN = options.valueNames;
  83. var output = [];
  84. var openTokens = 0;
  85. var delimStart = 0;
  86. var delimEnd = 0;
  87. var lastOuterEnd = 0;
  88. var outerStart = void 0;
  89. var innerStart = void 0;
  90. var leftMatch = void 0;
  91. var rightMatch = void 0;
  92. var esc = void 0;
  93. left = XRegExp(left, basicFlags);
  94. right = XRegExp(right, basicFlags);
  95. if (escapeChar) {
  96. if (escapeChar.length > 1) {
  97. throw new Error('Cannot use more than one escape character');
  98. }
  99. escapeChar = XRegExp.escape(escapeChar);
  100. // Example of concatenated `esc` regex:
  101. // `escapeChar`: '%'
  102. // `left`: '<'
  103. // `right`: '>'
  104. // Regex is: /(?:%[\S\s]|(?:(?!<|>)[^%])+)+/
  105. esc = new RegExp('(?:' + escapeChar + '[\\S\\s]|(?:(?!' +
  106. // Using `XRegExp.union` safely rewrites backreferences in `left` and `right`.
  107. // Intentionally not passing `basicFlags` to `XRegExp.union` since any syntax
  108. // transformation resulting from those flags was already applied to `left` and
  109. // `right` when they were passed through the XRegExp constructor above.
  110. XRegExp.union([left, right], '', { conjunction: 'or' }).source + ')[^' + escapeChar + '])+)+',
  111. // Flags `gy` not needed here
  112. flags.replace(/[^imu]+/g, ''));
  113. }
  114. while (true) {
  115. // If using an escape character, advance to the delimiter's next starting position,
  116. // skipping any escaped characters in between
  117. if (escapeChar) {
  118. delimEnd += (XRegExp.exec(str, esc, delimEnd, 'sticky') || [''])[0].length;
  119. }
  120. leftMatch = XRegExp.exec(str, left, delimEnd);
  121. rightMatch = XRegExp.exec(str, right, delimEnd);
  122. // Keep the leftmost match only
  123. if (leftMatch && rightMatch) {
  124. if (leftMatch.index <= rightMatch.index) {
  125. rightMatch = null;
  126. } else {
  127. leftMatch = null;
  128. }
  129. }
  130. // Paths (LM: leftMatch, RM: rightMatch, OT: openTokens):
  131. // LM | RM | OT | Result
  132. // 1 | 0 | 1 | loop
  133. // 1 | 0 | 0 | loop
  134. // 0 | 1 | 1 | loop
  135. // 0 | 1 | 0 | throw
  136. // 0 | 0 | 1 | throw
  137. // 0 | 0 | 0 | break
  138. // The paths above don't include the sticky mode special case. The loop ends after the
  139. // first completed match if not `global`.
  140. if (leftMatch || rightMatch) {
  141. delimStart = (leftMatch || rightMatch).index;
  142. delimEnd = delimStart + (leftMatch || rightMatch)[0].length;
  143. } else if (!openTokens) {
  144. break;
  145. }
  146. if (sticky && !openTokens && delimStart > lastOuterEnd) {
  147. break;
  148. }
  149. if (leftMatch) {
  150. if (!openTokens) {
  151. outerStart = delimStart;
  152. innerStart = delimEnd;
  153. }
  154. ++openTokens;
  155. } else if (rightMatch && openTokens) {
  156. if (! --openTokens) {
  157. if (vN) {
  158. if (vN[0] && outerStart > lastOuterEnd) {
  159. output.push(row(vN[0], str.slice(lastOuterEnd, outerStart), lastOuterEnd, outerStart));
  160. }
  161. if (vN[1]) {
  162. output.push(row(vN[1], str.slice(outerStart, innerStart), outerStart, innerStart));
  163. }
  164. if (vN[2]) {
  165. output.push(row(vN[2], str.slice(innerStart, delimStart), innerStart, delimStart));
  166. }
  167. if (vN[3]) {
  168. output.push(row(vN[3], str.slice(delimStart, delimEnd), delimStart, delimEnd));
  169. }
  170. } else {
  171. output.push(str.slice(innerStart, delimStart));
  172. }
  173. lastOuterEnd = delimEnd;
  174. if (!global) {
  175. break;
  176. }
  177. }
  178. } else {
  179. throw new Error('Unbalanced delimiter found in string');
  180. }
  181. // If the delimiter matched an empty string, avoid an infinite loop
  182. if (delimStart === delimEnd) {
  183. ++delimEnd;
  184. }
  185. }
  186. if (global && !sticky && vN && vN[0] && str.length > lastOuterEnd) {
  187. output.push(row(vN[0], str.slice(lastOuterEnd), lastOuterEnd, str.length));
  188. }
  189. return output;
  190. };
  191. };
  192. module.exports = exports['default'];