forked from diangu/tvboxtg
440 lines
14 KiB
JavaScript
440 lines
14 KiB
JavaScript
/**
|
|
* 字符串分段处理
|
|
* @param {String} code 处理字符串
|
|
* @param {Array} options 分段数组
|
|
* @param {Array<Function|Boolean>} maps 分段映射函数
|
|
* @param {Boolean} all 是否完全映射
|
|
* @returns {Array} 映射结果
|
|
*/
|
|
function tsCode(code, options, maps, all) {
|
|
let start = 0, res = [];
|
|
if (maps) {
|
|
if (typeof maps === 'function') {
|
|
options.forEach((part, index) => {
|
|
res.push(maps(code.slice(start, part + start)));
|
|
start += part;
|
|
});
|
|
} else {
|
|
options.forEach((part, index) => {
|
|
if (typeof maps[index] === 'function') {
|
|
res.push(maps[index](code.slice(start, part + start)));
|
|
} else if (maps[index] !== undefined) {
|
|
if (maps[index] === true) res.push(code.slice(start, part + start));
|
|
else res.push(maps[index]);
|
|
} else if (all) res.push(code.slice(start, part + start));
|
|
start += part;
|
|
});
|
|
};
|
|
return res
|
|
} else {
|
|
options.forEach(part => {
|
|
res.push(code.slice(start, part + start));
|
|
start += part;
|
|
});
|
|
return res
|
|
}
|
|
}
|
|
|
|
function range(end, start = 0, reverse) {
|
|
if (reverse) {
|
|
const arr = [];
|
|
for (let i = end; i >= start; i--) {
|
|
arr.push(i);
|
|
};
|
|
return arr;
|
|
} else {
|
|
const arr = [];
|
|
for (let i = start; i <= end; i++) {
|
|
arr.push(i);
|
|
};
|
|
return arr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 正则函数调用及函数结果插入递归替换
|
|
* [[mark=func:regExp]] //正则函数调用(标记与函数不同名)
|
|
* [[func:regExp]] //正则函数调用(标记与函数同名)
|
|
* [[mark|func]] //按标记插入结果
|
|
* @param {RegExp} reg
|
|
*/
|
|
function splitRegExp({ oldRes, source, flags, split, res, fns, str, old, lastIndex, oldReg, oldLeft, oldRight, inner, oldSource }) {
|
|
function next() {//回溯处理
|
|
const oldData = old[old.length - 1];
|
|
if (oldData) {
|
|
const lefts = res[oldData.oldLeft];
|
|
if (Array.isArray(lefts)) {
|
|
return splitRegExp({ oldRes, flags, split, fns, str, old, ...oldData });
|
|
} else {
|
|
return splitRegExp({ oldRes, flags, split, fns, str, old, ...old.pop() });
|
|
}
|
|
}
|
|
};
|
|
|
|
if (oldReg) {//回溯处理
|
|
let oldNum, leftRes;
|
|
if (Array.isArray(res[oldLeft])) {
|
|
res[oldLeft].shift();
|
|
leftRes = oldRes[oldLeft] = res[oldLeft][0];
|
|
if (res[oldLeft].reg[0]) {
|
|
res[oldLeft].reg.shift();
|
|
inner = res[oldLeft].reg[0];
|
|
};
|
|
if (leftRes.length === 1) {
|
|
oldNum = 0;
|
|
}
|
|
} else {
|
|
oldNum = 1;
|
|
}
|
|
|
|
let reg = inner ? (
|
|
source = tsCode(oldSource, [lastIndex, res[oldLeft].len, Infinity], {
|
|
1: `(?:${inner})` //从结果池中以标记为键读取插入到原始正则之中
|
|
}, true).join(''),
|
|
new RegExp(inner, flags)
|
|
) : oldReg,
|
|
mats = reg.exec(str);
|
|
|
|
switch (oldNum) {
|
|
case 0:
|
|
old[old.length - 1] = { lastIndex, oldSource, source, oldReg: reg, oldLeft, oldRight, res: { ...res, [oldLeft]: leftRes }, inner };
|
|
break;
|
|
case 1:
|
|
res[oldLeft] = fns[oldRight](mats, reg);
|
|
old.push({ lastIndex, oldSource, source, oldReg: reg, oldLeft, oldRight, res: { ...res } });
|
|
break;
|
|
};
|
|
|
|
return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
|
|
|
|
} else {
|
|
const mat = split.exec(source);//匹配函数调用点或函数结果插入点
|
|
if (mat) {
|
|
let lastIndex = mat.index, so = mat[1], index = so.indexOf(':');
|
|
if (index !== -1) {//函数调用点
|
|
let [first, last] = tsCode(so, [index, 1, Infinity], {
|
|
0: true,
|
|
2: true
|
|
}),
|
|
reg = new RegExp(last, flags),
|
|
mats = reg.exec(str);
|
|
if (mats) {//匹配成功递归处理
|
|
const index2 = first.indexOf('='),
|
|
[left, right] = index2 === -1 ? [first, first] : tsCode(first, [index2, 1, Infinity], {
|
|
0: true,
|
|
2: true
|
|
}), fnRes = fns[right](mats, reg);
|
|
res[left] = fnRes;
|
|
if (Array.isArray(fnRes)) {
|
|
fnRes.len = mat[0].length;
|
|
if (fnRes.reg[0]) last = fnRes.reg[0];
|
|
oldRes[left] = fnRes[0];
|
|
};
|
|
const oldSource = source;
|
|
source = tsCode(source, [lastIndex, mat[0].length, Infinity], {
|
|
1: `(?:${last})` //从结果池中以标记为键读取插入到原始正则之中
|
|
}, true).join('');
|
|
old.push({ lastIndex, oldSource, source, oldReg: reg, oldLeft: left, oldRight: right, res: { ...res } });
|
|
return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
|
|
} else return next();
|
|
} else {//函数结果插入点
|
|
const [lenStr, name] = mat;
|
|
let inner;
|
|
if (res[name] === undefined) {
|
|
try {
|
|
inner = fns[name]();
|
|
if (inner === undefined) return next();
|
|
} catch (e) {
|
|
return //函数中主动抛出错误,触发匹配失败返回
|
|
}
|
|
} else if (Array.isArray(res[name])) {
|
|
inner = oldRes[name];
|
|
} else {
|
|
inner = res[name]
|
|
};
|
|
source = tsCode(source, [lastIndex, lenStr.length, Infinity], {
|
|
1: inner //从结果池中以标记为键读取插入到原始正则之中
|
|
}, true).join('');
|
|
return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
|
|
}
|
|
} else {
|
|
return { source, next }
|
|
};
|
|
|
|
};
|
|
};
|
|
|
|
/**
|
|
* 增强正则工具对象生成
|
|
* @param {RegExp} reg 增强型原始正则
|
|
* @param {Function} fns 插入正则的函数集
|
|
* @param {String|Function} str 待匹配字符串,或生成下一个正则时回调执行函数
|
|
* @param {Function} next 生成下一个正则时回调执行函数
|
|
* @returns {{regExp:RegExp,next:Function,exec:Function,test:Function,match:Function,matchAll:Function,replace:Function,search:Function,split:Function}} 正则工具对象
|
|
*/
|
|
function exRegExp({ reg, fns, str, next }) {
|
|
const oldSplit = /\[\[(.*?[^\\])\]\]/g;
|
|
let regExp, notFirst, source, flags, newFlags, nextCall, res = {}, oldRes = {}, old = [];
|
|
if (reg) {
|
|
source = reg.source.replace(/\\\[(?=\[)/g, '(?:\\[)');
|
|
if (!(newFlags = reg.flags.replace('y', 'g')).includes('g')) {
|
|
newFlags += 'g';
|
|
};
|
|
if (str) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
|
|
};
|
|
|
|
function matRegExp(mat) {
|
|
if (mat) {
|
|
const { source, next: callback } = mat,
|
|
regExp = new RegExp(source, flags);
|
|
if (next) {
|
|
nextCall = () => {
|
|
const genReg = matRegExp(callback());
|
|
next(genReg);
|
|
return genReg;
|
|
};
|
|
} else {
|
|
nextCall = () => matRegExp(callback());
|
|
}
|
|
return regExp;
|
|
} else {
|
|
//正则表达式出错
|
|
throw { reg, flags, fns, str }
|
|
};
|
|
};
|
|
function returnRegExp(oldRes, source, split, newFlags, fns, str, res, old, inner) {
|
|
return matRegExp(splitRegExp({
|
|
source,
|
|
split,
|
|
flags: newFlags,
|
|
fns,
|
|
str,
|
|
res,
|
|
old,
|
|
inner,
|
|
oldRes
|
|
}));
|
|
};
|
|
|
|
function preExec() {
|
|
if (notFirst) {
|
|
try {
|
|
regExp = nextCall();
|
|
} catch (e) {
|
|
return true
|
|
};
|
|
} else {
|
|
notFirst = true;
|
|
}
|
|
};
|
|
function exec() {
|
|
if (preExec()) return null;
|
|
const exec = regExp.exec(str);
|
|
if (exec) {
|
|
return exec
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return exec();
|
|
} catch (e) {
|
|
console.log(e);
|
|
return null
|
|
}
|
|
}
|
|
};
|
|
|
|
function test() {
|
|
const testRes = regExp.test(str);
|
|
if (testRes) {
|
|
return true
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return test();
|
|
} catch (e) {
|
|
console.log(e);
|
|
return false
|
|
}
|
|
}
|
|
};
|
|
|
|
function match() {
|
|
const matchRes = str.match(regExp);
|
|
if (matchRes) {
|
|
return matchRes
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return match();
|
|
} catch (e) {
|
|
console.log(e);
|
|
return null
|
|
}
|
|
}
|
|
};
|
|
|
|
function matchAll() {
|
|
const matchAllRes = str.matchAll(regExp);
|
|
if (matchAllRes) {
|
|
return matchAllRes
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return matchAll();
|
|
} catch (e) {
|
|
console.log(e);
|
|
return null
|
|
}
|
|
}
|
|
};
|
|
|
|
function replace(callback, change) {
|
|
const replaceRes = str.replace(regExp, callback);
|
|
if (change) {
|
|
if (replaceRes !== str) {
|
|
return replaceRes
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return replace(str);
|
|
} catch (e) {
|
|
console.log(e);
|
|
return str
|
|
}
|
|
};
|
|
} else {
|
|
return replaceRes
|
|
};
|
|
};
|
|
|
|
function search() {
|
|
const searchRes = str.search(regExp);
|
|
if (searchRes !== -1) {
|
|
return searchRes
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return search(str);
|
|
} catch (e) {
|
|
console.log(e);
|
|
return -1
|
|
}
|
|
};
|
|
};
|
|
|
|
function split(limit, change) {
|
|
const splitRes = str.split(regExp, limit);
|
|
if (change) {
|
|
if (splitRes.length !== 1) {
|
|
return splitRes
|
|
} else {
|
|
try {
|
|
regExp = nextCall();
|
|
return split(str);
|
|
} catch (e) {
|
|
console.log(e);
|
|
return [str]
|
|
}
|
|
};
|
|
} else {
|
|
return splitRes
|
|
};
|
|
};
|
|
|
|
return {
|
|
//当前生成的正则
|
|
get regExp() { return regExp },
|
|
set regExp(nv) {
|
|
reg = nv;
|
|
notFirst = false;
|
|
res = {};
|
|
oldRes = {};
|
|
old = [];
|
|
flags = reg.flags;
|
|
nextCall = undefined;
|
|
source = reg.source.replace(/\\\[(?=\[)/g, '(?:\\[)');
|
|
if (!(newFlags = flags.replace('y', 'g')).includes('g')) {
|
|
newFlags += 'g';
|
|
};
|
|
if (str) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
|
|
},
|
|
//当前匹配的字符串
|
|
get str() {
|
|
return str
|
|
},
|
|
set str(newStr) {
|
|
str = newStr;
|
|
notFirst = false;
|
|
res = {};
|
|
oldRes = {};
|
|
old = [];
|
|
nextCall = undefined;
|
|
if (reg) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
|
|
},
|
|
next() {//生成下一个有效正则,全部无效时报错
|
|
return nextCall()
|
|
},
|
|
|
|
exec, /**
|
|
exec() => regExp.exec(str)
|
|
无需主动执行next,当上个正则耗尽后,再调用exec则主动生成下一个有效正则
|
|
*/
|
|
|
|
test,/**
|
|
test() => regExp.test(str)
|
|
匹配失败时主动执行next生成下一个有效正则
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
|
|
match,/**
|
|
* match() => str.match(regExp)
|
|
匹配失败时主动执行next生成下一个有效正则
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
|
|
matchAll, /**
|
|
matchAll() => str.matchAll(regExp)
|
|
匹配失败时主动执行next生成下一个有效正则
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
|
|
replace, /**
|
|
//replace(callback, change) => str.replace(regExp,callback);
|
|
change为true时,如果替换后的值与替换前一样,则自动调用next生成下一个有效正则继续执行replace(callback, change)
|
|
change为false时,只要生成的正则有效则只执行一次replace
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
|
|
search, /**
|
|
search() => str.search(regExp)
|
|
匹配失败时主动执行next生成下一个有效正则
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
|
|
split /**
|
|
split(limit, change) => str.split(regExp,limit)
|
|
change为true时,如果切割后数组长度为1,则自动调用next生成下一个有效正则继续执行split(limit, change)
|
|
change为false时,只要生成的正则有效则只执行一次split
|
|
可主动执行next,主动生成下一个有效正则作为匹配正则
|
|
*/
|
|
}
|
|
};
|
|
|
|
//示例
|
|
const obj = exRegExp({
|
|
reg: /ersd[[reg:(ka)+]].*ef(fa){[[reg]]}.*sas/g,
|
|
fns: {
|
|
reg([$0, $1]) {
|
|
const range0 = range($0.length / $1.length, 1, true);
|
|
range0.reg = range0.map(it => new Array(it).fill($1).join(''));
|
|
return range0
|
|
}
|
|
},
|
|
str: 'bvgvgersdkakakaeffafaeffafafasaskuyfu'
|
|
});
|
|
|
|
console.log('match:', obj.match()); //match1: ['ersdkakakaeffafaeffafafasas', 'fa', index: 5, input: 'bvgvgersdkakakaeffafaeffafafasaskuyfu', groups: undefined]
|
|
|
|
obj.regExp = /[[reg:(ka)+]]ef(fa){[[reg]]}/g;
|
|
|
|
console.log('match2:', obj.match()); //match2: ['kakaeffafa']
|