regexplus
Version:
RegExp Plus
103 lines (97 loc) • 2.77 kB
JavaScript
var RE_PLUS = /\((\?<=|\?<!|\?<\w+>|.)/g;
var RE_NAMED = /\(\?<(\w+)>/g; // 命名分组。
var RE_REV_PRE = /\(\?<=/g; // 反向预搜索。
var RE_REV_PRE_NOT = /\(\?<!/g; // 反向预搜索不匹配。
function RegExplus(source, flags) {
if (!flags) {
flags = '';
}
this.source = source;
this._plus = [null];
this._reverse_pre_search = [];
this._reverse_pre_search_not_match = [];
var _source = source.replace(RE_PLUS, function($0, flag, subreg) {
if (flag === '?:') { // unmatched group.
return $0;
} else if (flag === '?=') { // 正向预搜索。
return $0;
} else if (flag === '?=') { // 正向预搜索不匹配。
return $0;
} else if (flag === '?<=') { // 反向预搜索。
this._plus.push({
type: 'reverse-match',
regexp: new RegExp(subreg, flags),
});
} else if (flag === '?<!') { // 反向预搜索不匹配。
this._plus.push({
type: 'reverse-mismatch',
regexp: new RegExp(subreg, flags),
});
} else if (flag.startsWith('?<') && flag.endsWith('>')) { // 命名分组。
this._plus.push({
type: 'named-group',
name: flag.substring(2, flag.length - 1),
});
} else {// 普通分组。
this._plus.push({
type: 'group',
});
return $0;
}
return '(';
}.bind(this));
this._regexp = new RegExp(_source, flags);
this.lastIndex = 0;
this.global = flags.indexOf('g') >= 0;
this.multiline = flags.indexOf('m') >= 0;
this.ignoreCase = flags.indexOf('i') >= 0;
}
RegExplus.prototype = {
exec: function(string) {
var matched = this._regexp.exec(string);
if (!matched) {
return null;
}
// named group.
for (var i=1, l=matched.length; i<l; i++) {
switch(this._plus[i].type){
case 'group':
break;
case 'named-group':
matched[ this._plus[i].name ] = matched[i];
break;
case 'reverse-match':
if (this._plus[i].regexp.test(matched[i])) {
matched.splice(i, 1);
} else {
return null;
}
break;
case 'reverse-mismatch':
if (!this._plus[i].regexp.test(matched[i])) {
matched.splice(i, 1);
} else {
return null;
}
break;
default:
// TODO: 反向预搜索
throw new Error("Unknow type.");
}
}
return matched;
},
test: function(string) {
return this.exec(string) !== null;
},
valueOf: function() {
return this._regexp;
},
toString: function() {
return '/' + this.source + '/' +
(this.ignoreCase ? 'i' : '') +
(this.multiline ? 'm' : '') +
(this.global ? 'g' : '');
},
};
module.exports = RegExplus;