quantifiers-merge-transform.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * The MIT License (MIT)
  3. * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
  4. */
  5. 'use strict';
  6. var _require = require('../../transform/utils'),
  7. increaseQuantifierByOne = _require.increaseQuantifierByOne;
  8. /**
  9. * A regexp-tree plugin to merge quantifiers
  10. *
  11. * a+a+ -> a{2,}
  12. * a{2}a{3} -> a{5}
  13. * a{1,2}a{2,3} -> a{3,5}
  14. */
  15. module.exports = {
  16. Repetition: function Repetition(path) {
  17. var node = path.node,
  18. parent = path.parent;
  19. if (parent.type !== 'Alternative' || !path.index) {
  20. return;
  21. }
  22. var previousSibling = path.getPreviousSibling();
  23. if (!previousSibling) {
  24. return;
  25. }
  26. if (previousSibling.node.type === 'Repetition') {
  27. if (!previousSibling.getChild().hasEqualSource(path.getChild())) {
  28. return;
  29. }
  30. var _extractFromTo = extractFromTo(previousSibling.node.quantifier),
  31. previousSiblingFrom = _extractFromTo.from,
  32. previousSiblingTo = _extractFromTo.to;
  33. var _extractFromTo2 = extractFromTo(node.quantifier),
  34. nodeFrom = _extractFromTo2.from,
  35. nodeTo = _extractFromTo2.to;
  36. // It's does not seem reliable to merge quantifiers with different greediness
  37. // when none of both is a greedy open range
  38. if (previousSibling.node.quantifier.greedy !== node.quantifier.greedy && !isGreedyOpenRange(previousSibling.node.quantifier) && !isGreedyOpenRange(node.quantifier)) {
  39. return;
  40. }
  41. // a*a* -> a*
  42. // a*a+ -> a+
  43. // a+a+ -> a{2,}
  44. // a{2}a{4} -> a{6}
  45. // a{1,2}a{2,3} -> a{3,5}
  46. // a{1,}a{2,} -> a{3,}
  47. // a+a{2,} -> a{3,}
  48. // a??a{2,} -> a{2,}
  49. // a*?a{2,} -> a{2,}
  50. // a+?a{2,} -> a{3,}
  51. node.quantifier.kind = 'Range';
  52. node.quantifier.from = previousSiblingFrom + nodeFrom;
  53. if (previousSiblingTo && nodeTo) {
  54. node.quantifier.to = previousSiblingTo + nodeTo;
  55. } else {
  56. delete node.quantifier.to;
  57. }
  58. if (isGreedyOpenRange(previousSibling.node.quantifier) || isGreedyOpenRange(node.quantifier)) {
  59. node.quantifier.greedy = true;
  60. }
  61. previousSibling.remove();
  62. } else {
  63. if (!previousSibling.hasEqualSource(path.getChild())) {
  64. return;
  65. }
  66. increaseQuantifierByOne(node.quantifier);
  67. previousSibling.remove();
  68. }
  69. }
  70. };
  71. function isGreedyOpenRange(quantifier) {
  72. return quantifier.greedy && (quantifier.kind === '+' || quantifier.kind === '*' || quantifier.kind === 'Range' && !quantifier.to);
  73. }
  74. function extractFromTo(quantifier) {
  75. var from = void 0,
  76. to = void 0;
  77. if (quantifier.kind === '*') {
  78. from = 0;
  79. } else if (quantifier.kind === '+') {
  80. from = 1;
  81. } else if (quantifier.kind === '?') {
  82. from = 0;
  83. to = 1;
  84. } else {
  85. from = quantifier.from;
  86. if (quantifier.to) {
  87. to = quantifier.to;
  88. }
  89. }
  90. return { from: from, to: to };
  91. }