Repository: DmitrySoshnikov/regexp-tree
Pull request #71 (2017-05-05 07:00:06)

convert single chars group disjunction to chargroup
(a|b|c) -> ([abc])
(?:a|b|c) -> [abc]

silentroach: all codestyle comments fixed

Patch:
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..b13df96
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+
+[*.js]
+indent_style = space
+indent_size = 2
\ No newline at end of file
diff --git a/src/optimizer/README.md b/src/optimizer/README.md
index 436374c..1096945 100644
--- a/src/optimizer/README.md
+++ b/src/optimizer/README.md
@@ -30,4 +30,5 @@ Is transformed into:
 - `[ \t\r\n\f]` -> `\s`
 - `[^ \t\r\n\f]` -> `\S`
 - `\e` -> `e` (remove unnecessary escape)
+- `(?:a|b|c)` -> `[abc]`
 - ...
diff --git a/src/optimizer/transforms/group-single-chars-to-char-class.js b/src/optimizer/transforms/group-single-chars-to-char-class.js
new file mode 100644
index 0000000..13adb97
--- /dev/null
+++ b/src/optimizer/transforms/group-single-chars-to-char-class.js
@@ -0,0 +1,57 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+/**
+ * A regexp-tree plugin to replace single char group disjunction to char group
+ *
+ * (a|b|c) -> ([abc])
+ * (?:a|b|c) -> [abc]
+ */
+module.exports = {
+  Group(path) {
+    const {node} = path;
+
+    const charset = new Map();
+
+    if (
+      !shouldProcessExpressionCharset(node.expression, charset) ||
+      !charset.size
+    ) {
+      return;
+    }
+
+    const characterClass = {
+      type: 'CharacterClass',
+      expressions: Array.from(charset.values())
+    };
+
+    if (node.capturing) {
+      node.expression = characterClass;
+    } else {
+      path.replace(characterClass);
+    }
+  }
+}
+
+function shouldProcessExpressionCharset(expression, charset) {
+  const {type} = expression;
+
+  if (type === 'Disjunction') {
+    const {left, right} = expression;
+
+    return shouldProcessExpressionCharset(left, charset)
+      && shouldProcessExpressionCharset(right, charset);
+  } else if (type === 'Char') {
+    const {value} = expression;
+
+    charset.set(value, expression);
+
+    return true;
+  }
+
+  return false;
+}
\ No newline at end of file
diff --git a/src/optimizer/transforms/index.js b/src/optimizer/transforms/index.js
index ded732f..28e997c 100644
--- a/src/optimizer/transforms/index.js
+++ b/src/optimizer/transforms/index.js
@@ -23,4 +23,7 @@ module.exports = [
 
   // \e -> e
   require('./char-escape-unescape-transform'),
+
+  // (a|b|c) -> [abc]
+  require('./group-single-chars-to-char-class')
 ];
\ No newline at end of file


Test patch:
diff --git a/src/optimizer/transforms/__tests__/group-single-chars-to-char-class-test.js b/src/optimizer/transforms/__tests__/group-single-chars-to-char-class-test.js
new file mode 100644
index 0000000..d692be7
--- /dev/null
+++ b/src/optimizer/transforms/__tests__/group-single-chars-to-char-class-test.js
@@ -0,0 +1,48 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+const {transform} = require('../../../transform');
+const singleCharsGroupToCharClass = require('../group-single-chars-to-char-class');
+
+describe('(a|b|c) -> ([abc])', () => {
+
+  it('replaces single chars group to char class', () => {
+    const re = transform(/(a|b|c)/, [
+      singleCharsGroupToCharClass,
+    ]);
+    expect(re.toString()).toBe('/([abc])/');
+  });
+
+  it('works with metachars too', () => {
+    const re = transform(/(\d|\n)/, [
+      singleCharsGroupToCharClass,
+    ]);
+    expect(re.toString()).toBe(/([\d\n])/.toString());
+  });
+
+  it('merges duplicates in group', () => {
+    const re = transform(/(a|b|a|b)/, [
+      singleCharsGroupToCharClass,
+    ]);
+    expect(re.toString()).toBe('/([ab])/');
+  });
+
+  it('removes non-capturing group', () => {
+    const re = transform(/(?:a|b|c)/, [
+      singleCharsGroupToCharClass,
+    ]);
+    expect(re.toString()).toBe('/[abc]/');
+  });
+
+  it('have no effect on multichar values', () => {
+    const re = transform(/(a|b|no)/, [
+      singleCharsGroupToCharClass,
+    ]);
+    expect(re.toString()).toBe('/(a|b|no)/');
+  });
+
+});
\ No newline at end of file