regexgen
Advanced tools
Comparing version 1.2.1 to 1.2.2
{ | ||
"name": "regexgen", | ||
"version": "1.2.1", | ||
"version": "1.2.2", | ||
"description": "Generate regular expressions that match a set of strings", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -24,3 +24,3 @@ const jsesc = require('jsesc'); | ||
constructor(a, b) { | ||
this.precedence = 2; | ||
this.precedence = 1; | ||
this.set = regenerate(a, b); | ||
@@ -57,21 +57,22 @@ } | ||
getLiteral(side) { | ||
if (side === 'start' && this.a instanceof Literal) { | ||
return this.a; | ||
if (side === 'start' && this.a.getLiteral) { | ||
return this.a.getLiteral(side); | ||
} | ||
if (side === 'end' && this.b instanceof Literal) { | ||
return this.b; | ||
if (side === 'end' && this.b.getLiteral) { | ||
return this.b.getLiteral(side); | ||
} | ||
} | ||
simplify() { | ||
if (this.a.isEmpty) { | ||
return this.b; | ||
removeSubstring(side, len) { | ||
let {a, b} = this; | ||
if (side === 'start' && a.removeSubstring) { | ||
a = a.removeSubstring(side, len); | ||
} | ||
if (this.b.isEmpty) { | ||
return this.a; | ||
if (side === 'end' && b.removeSubstring) { | ||
b = b.removeSubstring(side, len); | ||
} | ||
return this; | ||
return a.isEmpty ? b : b.isEmpty ? a : new Concatenation(a, b); | ||
} | ||
@@ -123,4 +124,14 @@ } | ||
getLiteral() { | ||
return this; | ||
return this.value; | ||
} | ||
removeSubstring(side, len) { | ||
if (side === 'start') { | ||
return new Literal(this.value.slice(len)); | ||
} | ||
if (side === 'end') { | ||
return new Literal(this.value.slice(0, this.value.length - len)); | ||
} | ||
} | ||
} | ||
@@ -127,0 +138,0 @@ |
@@ -68,12 +68,13 @@ const {Alternation, CharClass, Concatenation, Repetition, Literal} = require('./ast'); | ||
// Hoist common substrings at the start and end of the options | ||
let start = removeCommonSubstring(a, b, 'start'); | ||
let end = removeCommonSubstring(a, b, 'end'); | ||
let res; | ||
let start, end, res; | ||
[a, b, start] = removeCommonSubstring(a, b, 'start'); | ||
[a, b, end] = removeCommonSubstring(a, b, 'end'); | ||
a = a.simplify ? a.simplify() : a; | ||
b = b.simplify ? b.simplify() : b; | ||
// If a or b is empty, make an optional group instead | ||
if (a.isEmpty || b.isEmpty) { | ||
res = new Repetition(a.isEmpty ? b : a, '?'); | ||
} else if (a instanceof Repetition && a.type === '?') { | ||
res = new Repetition(new Alternation(a.expr, b), '?'); | ||
} else if (b instanceof Repetition && b.type === '?') { | ||
res = new Repetition(new Alternation(a, b.expr), '?'); | ||
} else { | ||
@@ -108,17 +109,17 @@ // Check if we can make a character class instead of an alternation | ||
function removeCommonSubstring(a, b, side) { | ||
a = a.getLiteral && a.getLiteral(side); | ||
b = b.getLiteral && b.getLiteral(side); | ||
if (!a || !b) return null; | ||
let al = a.getLiteral && a.getLiteral(side); | ||
let bl = b.getLiteral && b.getLiteral(side); | ||
if (!al || !bl) { | ||
return [a, b, null]; | ||
} | ||
let s = commonSubstring(a.value, b.value, side); | ||
if (side === 'start') { | ||
a.value = a.value.slice(s.length); | ||
b.value = b.value.slice(s.length); | ||
} else { | ||
a.value = a.value.slice(0, a.value.length - s.length); | ||
b.value = b.value.slice(0, b.value.length - s.length); | ||
let s = commonSubstring(al, bl, side); | ||
if (!s) { | ||
return [a, b, '']; | ||
} | ||
return s; | ||
a = a.removeSubstring(side, s.length); | ||
b = b.removeSubstring(side, s.length); | ||
return [a, b, s]; | ||
} | ||
@@ -125,0 +126,0 @@ |
@@ -65,2 +65,6 @@ const assert = require('assert'); | ||
}); | ||
it('should correctly extract common prefix from multiple alternations', function () { | ||
assert.deepEqual(regexgen(['abjv', 'abxcjv', 'abydjv', 'abzejv']), /ab(?:ze|yd|xc)?jv/); | ||
}); | ||
}); |
19187
581