parsers.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. 'use strict';
  2. var utils = require('./utils');
  3. var define = require('define-property');
  4. /**
  5. * Text regex
  6. */
  7. var TEXT_REGEX = '(\\[(?=.*\\])|\\])+';
  8. var not = utils.createRegex(TEXT_REGEX);
  9. /**
  10. * Brackets parsers
  11. */
  12. function parsers(brackets) {
  13. brackets.state = brackets.state || {};
  14. brackets.parser.sets.bracket = brackets.parser.sets.bracket || [];
  15. brackets.parser
  16. .capture('escape', function() {
  17. if (this.isInside('bracket')) return;
  18. var pos = this.position();
  19. var m = this.match(/^\\(.)/);
  20. if (!m) return;
  21. return pos({
  22. type: 'escape',
  23. val: m[0]
  24. });
  25. })
  26. /**
  27. * Text parser
  28. */
  29. .capture('text', function() {
  30. if (this.isInside('bracket')) return;
  31. var pos = this.position();
  32. var m = this.match(not);
  33. if (!m || !m[0]) return;
  34. return pos({
  35. type: 'text',
  36. val: m[0]
  37. });
  38. })
  39. /**
  40. * POSIX character classes: "[[:alpha:][:digits:]]"
  41. */
  42. .capture('posix', function() {
  43. var pos = this.position();
  44. var m = this.match(/^\[:(.*?):\](?=.*\])/);
  45. if (!m) return;
  46. var inside = this.isInside('bracket');
  47. if (inside) {
  48. brackets.posix++;
  49. }
  50. return pos({
  51. type: 'posix',
  52. insideBracket: inside,
  53. inner: m[1],
  54. val: m[0]
  55. });
  56. })
  57. /**
  58. * Bracket (noop)
  59. */
  60. .capture('bracket', function() {})
  61. /**
  62. * Open: '['
  63. */
  64. .capture('bracket.open', function() {
  65. var parsed = this.parsed;
  66. var pos = this.position();
  67. var m = this.match(/^\[(?=.*\])/);
  68. if (!m) return;
  69. var prev = this.prev();
  70. var last = utils.last(prev.nodes);
  71. if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) {
  72. last.val = last.val.slice(0, last.val.length - 1);
  73. return pos({
  74. type: 'escape',
  75. val: m[0]
  76. });
  77. }
  78. var open = pos({
  79. type: 'bracket.open',
  80. val: m[0]
  81. });
  82. if (last.type === 'bracket.open' || this.isInside('bracket')) {
  83. open.val = '\\' + open.val;
  84. open.type = 'bracket.inner';
  85. open.escaped = true;
  86. return open;
  87. }
  88. var node = pos({
  89. type: 'bracket',
  90. nodes: [open]
  91. });
  92. define(node, 'parent', prev);
  93. define(open, 'parent', node);
  94. this.push('bracket', node);
  95. prev.nodes.push(node);
  96. })
  97. /**
  98. * Bracket text
  99. */
  100. .capture('bracket.inner', function() {
  101. if (!this.isInside('bracket')) return;
  102. var pos = this.position();
  103. var m = this.match(not);
  104. if (!m || !m[0]) return;
  105. var next = this.input.charAt(0);
  106. var val = m[0];
  107. var node = pos({
  108. type: 'bracket.inner',
  109. val: val
  110. });
  111. if (val === '\\\\') {
  112. return node;
  113. }
  114. var first = val.charAt(0);
  115. var last = val.slice(-1);
  116. if (first === '!') {
  117. val = '^' + val.slice(1);
  118. }
  119. if (last === '\\' || (val === '^' && next === ']')) {
  120. val += this.input[0];
  121. this.consume(1);
  122. }
  123. node.val = val;
  124. return node;
  125. })
  126. /**
  127. * Close: ']'
  128. */
  129. .capture('bracket.close', function() {
  130. var parsed = this.parsed;
  131. var pos = this.position();
  132. var m = this.match(/^\]/);
  133. if (!m) return;
  134. var prev = this.prev();
  135. var last = utils.last(prev.nodes);
  136. if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) {
  137. last.val = last.val.slice(0, last.val.length - 1);
  138. return pos({
  139. type: 'escape',
  140. val: m[0]
  141. });
  142. }
  143. var node = pos({
  144. type: 'bracket.close',
  145. rest: this.input,
  146. val: m[0]
  147. });
  148. if (last.type === 'bracket.open') {
  149. node.type = 'bracket.inner';
  150. node.escaped = true;
  151. return node;
  152. }
  153. var bracket = this.pop('bracket');
  154. if (!this.isType(bracket, 'bracket')) {
  155. if (this.options.strict) {
  156. throw new Error('missing opening "["');
  157. }
  158. node.type = 'bracket.inner';
  159. node.escaped = true;
  160. return node;
  161. }
  162. bracket.nodes.push(node);
  163. define(node, 'parent', bracket);
  164. });
  165. }
  166. /**
  167. * Brackets parsers
  168. */
  169. module.exports = parsers;
  170. /**
  171. * Expose text regex
  172. */
  173. module.exports.TEXT_REGEX = TEXT_REGEX;