parsers.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. 'use strict';
  2. var brackets = require('expand-brackets');
  3. var define = require('define-property');
  4. var utils = require('./utils');
  5. /**
  6. * Characters to use in text regex (we want to "not" match
  7. * characters that are matched by other parsers)
  8. */
  9. var TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+';
  10. var not = utils.createRegex(TEXT_REGEX);
  11. /**
  12. * Extglob parsers
  13. */
  14. function parsers(extglob) {
  15. extglob.state = extglob.state || {};
  16. /**
  17. * Use `expand-brackets` parsers
  18. */
  19. extglob.use(brackets.parsers);
  20. extglob.parser.sets.paren = extglob.parser.sets.paren || [];
  21. extglob.parser
  22. /**
  23. * Extglob open: "*("
  24. */
  25. .capture('paren.open', function() {
  26. var parsed = this.parsed;
  27. var pos = this.position();
  28. var m = this.match(/^([!@*?+])?\(/);
  29. if (!m) return;
  30. var prev = this.prev();
  31. var prefix = m[1];
  32. var val = m[0];
  33. var open = pos({
  34. type: 'paren.open',
  35. parsed: parsed,
  36. val: val
  37. });
  38. var node = pos({
  39. type: 'paren',
  40. prefix: prefix,
  41. nodes: [open]
  42. });
  43. // if nested negation extglobs, just cancel them out to simplify
  44. if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') {
  45. prev.prefix = '@';
  46. node.prefix = '@';
  47. }
  48. define(node, 'rest', this.input);
  49. define(node, 'parsed', parsed);
  50. define(node, 'parent', prev);
  51. define(open, 'parent', node);
  52. this.push('paren', node);
  53. prev.nodes.push(node);
  54. })
  55. /**
  56. * Extglob close: ")"
  57. */
  58. .capture('paren.close', function() {
  59. var parsed = this.parsed;
  60. var pos = this.position();
  61. var m = this.match(/^\)/);
  62. if (!m) return;
  63. var parent = this.pop('paren');
  64. var node = pos({
  65. type: 'paren.close',
  66. rest: this.input,
  67. parsed: parsed,
  68. val: m[0]
  69. });
  70. if (!this.isType(parent, 'paren')) {
  71. if (this.options.strict) {
  72. throw new Error('missing opening paren: "("');
  73. }
  74. node.escaped = true;
  75. return node;
  76. }
  77. node.prefix = parent.prefix;
  78. parent.nodes.push(node);
  79. define(node, 'parent', parent);
  80. })
  81. /**
  82. * Escape: "\\."
  83. */
  84. .capture('escape', function() {
  85. var pos = this.position();
  86. var m = this.match(/^\\(.)/);
  87. if (!m) return;
  88. return pos({
  89. type: 'escape',
  90. val: m[0],
  91. ch: m[1]
  92. });
  93. })
  94. /**
  95. * Question marks: "?"
  96. */
  97. .capture('qmark', function() {
  98. var parsed = this.parsed;
  99. var pos = this.position();
  100. var m = this.match(/^\?+(?!\()/);
  101. if (!m) return;
  102. extglob.state.metachar = true;
  103. return pos({
  104. type: 'qmark',
  105. rest: this.input,
  106. parsed: parsed,
  107. val: m[0]
  108. });
  109. })
  110. /**
  111. * Character parsers
  112. */
  113. .capture('star', /^\*(?!\()/)
  114. .capture('plus', /^\+(?!\()/)
  115. .capture('dot', /^\./)
  116. .capture('text', not);
  117. };
  118. /**
  119. * Expose text regex string
  120. */
  121. module.exports.TEXT_REGEX = TEXT_REGEX;
  122. /**
  123. * Extglob parsers
  124. */
  125. module.exports = parsers;