group-single-chars-to-char-class.js 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /**
  2. * The MIT License (MIT)
  3. * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
  4. */
  5. 'use strict';
  6. /**
  7. * A regexp-tree plugin to replace single char group disjunction to char group
  8. *
  9. * a|b|c -> [abc]
  10. * [12]|3|4 -> [1234]
  11. * (a|b|c) -> ([abc])
  12. * (?:a|b|c) -> [abc]
  13. */
  14. module.exports = {
  15. Disjunction: function Disjunction(path) {
  16. var node = path.node,
  17. parent = path.parent;
  18. if (!handlers[parent.type]) {
  19. return;
  20. }
  21. var charset = new Map();
  22. if (!shouldProcess(node, charset) || !charset.size) {
  23. return;
  24. }
  25. var characterClass = {
  26. type: 'CharacterClass',
  27. expressions: Array.from(charset.keys()).sort().map(function (key) {
  28. return charset.get(key);
  29. })
  30. };
  31. handlers[parent.type](path.getParent(), characterClass);
  32. }
  33. };
  34. var handlers = {
  35. RegExp: function RegExp(path, characterClass) {
  36. var node = path.node;
  37. node.body = characterClass;
  38. },
  39. Group: function Group(path, characterClass) {
  40. var node = path.node;
  41. if (node.capturing) {
  42. node.expression = characterClass;
  43. } else {
  44. path.replace(characterClass);
  45. }
  46. }
  47. };
  48. function shouldProcess(expression, charset) {
  49. if (!expression) {
  50. // Abort on empty disjunction part
  51. return false;
  52. }
  53. var type = expression.type;
  54. if (type === 'Disjunction') {
  55. var left = expression.left,
  56. right = expression.right;
  57. return shouldProcess(left, charset) && shouldProcess(right, charset);
  58. } else if (type === 'Char') {
  59. var value = expression.value;
  60. charset.set(value, expression);
  61. return true;
  62. } else if (type === 'CharacterClass' && !expression.negative) {
  63. return expression.expressions.every(function (expression) {
  64. return shouldProcess(expression, charset);
  65. });
  66. }
  67. return false;
  68. }