77 lines
2.5 KiB
JavaScript
77 lines
2.5 KiB
JavaScript
/* IMPORT */
|
|
/* MAIN */
|
|
const getNodes = (node) => {
|
|
const nodes = new Set();
|
|
const queue = [node];
|
|
for (let i = 0; i < queue.length; i++) {
|
|
const node = queue[i];
|
|
if (nodes.has(node))
|
|
continue;
|
|
nodes.add(node);
|
|
const { children } = node;
|
|
if (!children?.length)
|
|
continue;
|
|
for (let ci = 0, cl = children.length; ci < cl; ci++) {
|
|
queue.push(children[ci]);
|
|
}
|
|
}
|
|
return Array.from(nodes);
|
|
};
|
|
const getNodeFlags = (node) => {
|
|
let flags = '';
|
|
const nodes = getNodes(node);
|
|
for (let i = 0, l = nodes.length; i < l; i++) { // From root to leaves
|
|
const node = nodes[i];
|
|
if (!node.regex)
|
|
continue;
|
|
const nodeFlags = node.regex.flags;
|
|
flags || (flags = nodeFlags);
|
|
if (flags === nodeFlags)
|
|
continue;
|
|
throw new Error(`Inconsistent RegExp flags used: "${flags}" and "${nodeFlags}"`);
|
|
}
|
|
return flags;
|
|
};
|
|
const getNodeSourceWithCache = (node, partial, cache) => {
|
|
const cached = cache.get(node);
|
|
if (cached !== undefined)
|
|
return cached;
|
|
const isNodePartial = node.partial ?? partial;
|
|
let source = '';
|
|
if (node.regex) {
|
|
source += isNodePartial ? '(?:$|' : '';
|
|
source += node.regex.source;
|
|
}
|
|
if (node.children?.length) {
|
|
const children = uniq(node.children.map(node => getNodeSourceWithCache(node, partial, cache)).filter(Boolean));
|
|
if (children?.length) {
|
|
const isSomeChildNonPartial = node.children.some(child => !child.regex || !(child.partial ?? partial));
|
|
const needsWrapperGroup = (children.length > 1) || (isNodePartial && (!source.length || isSomeChildNonPartial));
|
|
source += needsWrapperGroup ? isNodePartial ? '(?:$|' : '(?:' : '';
|
|
source += children.join('|');
|
|
source += needsWrapperGroup ? ')' : '';
|
|
}
|
|
}
|
|
if (node.regex) {
|
|
source += isNodePartial ? ')' : '';
|
|
}
|
|
cache.set(node, source);
|
|
return source;
|
|
};
|
|
const getNodeSource = (node, partial) => {
|
|
const cache = new Map();
|
|
const nodes = getNodes(node);
|
|
for (let i = nodes.length - 1; i >= 0; i--) { // From leaves to root
|
|
const source = getNodeSourceWithCache(nodes[i], partial, cache);
|
|
if (i > 0)
|
|
continue;
|
|
return source;
|
|
}
|
|
return '';
|
|
};
|
|
const uniq = (values) => {
|
|
return Array.from(new Set(values));
|
|
};
|
|
/* EXPORT */
|
|
export { getNodeFlags, getNodeSource };
|