@comunica/actor-function-factory-term-regex
Version:
A term-regex function-factory actor
104 lines • 4.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TermFunctionRegex = void 0;
const bus_function_factory_1 = require("@comunica/bus-function-factory");
const utils_expression_evaluator_1 = require("@comunica/utils-expression-evaluator");
/**
* https://www.w3.org/TR/sparql11-query/#func-regex
*/
class TermFunctionRegex extends bus_function_factory_1.TermFunctionBase {
constructor() {
super({
arity: [2, 3],
operator: utils_expression_evaluator_1.SparqlOperator.REGEX,
overloads: (0, utils_expression_evaluator_1.declare)(utils_expression_evaluator_1.SparqlOperator.REGEX)
.onBinaryTyped([utils_expression_evaluator_1.TypeAlias.SPARQL_STRINGLY, utils_expression_evaluator_1.TypeURL.XSD_STRING], TermFunctionRegex.regex2)
.onTernaryTyped([utils_expression_evaluator_1.TypeAlias.SPARQL_STRINGLY, utils_expression_evaluator_1.TypeURL.XSD_STRING, utils_expression_evaluator_1.TypeURL.XSD_STRING], TermFunctionRegex.regex3)
.collect(),
});
}
static regex2() {
return (text, pattern) => (0, utils_expression_evaluator_1.bool)(TermFunctionRegex.matches(text, pattern));
}
static regex3() {
return (text, pattern, flags) => (0, utils_expression_evaluator_1.bool)(TermFunctionRegex.matches(text, pattern, flags));
}
// https://www.w3.org/TR/xpath-functions/#func-matches
// https://www.w3.org/TR/xpath-functions/#flags
static matches(text, pattern, flags = '') {
// Flags:
// i: case-insensitive: same as the 'i' flag in JavaScript
// m: multi-line mode: same as the 'm' flag in JavaScript
// s: dot-all mode: matches 's' flag in JavaScript very well.
// x: whitespace characters (#x9, #xA, #xD and #x20)
// in the regular expression are removed prior to matching with one exception:
// whitespace characters within character class expressions.
// q: regex-no-metacharacters:
// all characters in the regular expression are treated as representing themselves, not as metacharacters
// If it is used together with the m, s, or x flag, that flag has no effect.
flags = TermFunctionRegex.cleanFlags(flags);
if (flags.includes('x')) {
pattern = TermFunctionRegex.flagX(pattern);
}
if (flags.includes('q')) {
pattern = TermFunctionRegex.flagQ(pattern);
}
const reg = new RegExp(pattern, flags.replaceAll(/[qx]/gu, ''));
return reg.test(text);
}
static cleanFlags(flags) {
// Check flag validity
if (!/^[imsxq]*$/u.test(flags)) {
throw new Error('Invalid flags');
}
const duplicateFlag = [...flags]
.find((value, index, self) => self.indexOf(value) !== index);
if (duplicateFlag) {
throw new Error(`Duplicate flag: ${duplicateFlag}`);
}
// If the 'q' flag is used, the 'm', 's', and 'x' flags have no effect
if (flags?.includes('q')) {
flags = flags.replaceAll(/[msx]/gu, '');
}
// Add the JS 'u' flag to the flags to allow for safer regex execution.
// See reasons given by [ESLint](https://eslint.org/docs/latest/rules/require-unicode-regexp).
// Disable [Annex B](https://262.ecma-international.org/6.0/#sec-regular-expressions-patterns)
return `${flags}u`;
}
static flagX(pattern) {
if (!pattern) {
return pattern;
}
// Remove all spaces in the pattern, excluding those in character classes
let prev = pattern[0];
while (['\u0009', '\u000A', '\u000D', '\u0020'].includes(prev)) {
pattern = pattern.slice(1);
prev = pattern[0];
}
let inClass = prev === '[';
for (let i = 1; i < pattern.length; i++) {
const c = pattern[i];
if (['\u0009', '\u000A', '\u000D', '\u0020'].includes(c) && !inClass) {
pattern = pattern.slice(0, i) + pattern.slice(i + 1);
i--;
}
else if (c === '[' && prev !== '\\') {
inClass = true;
}
else if (c === ']' && prev !== '\\') {
inClass = false;
}
prev = c;
}
return pattern;
}
static flagQ(pattern) {
// Escape all metacharacters in the pattern
return pattern
.replaceAll(/([?+*.{}()[\]\\|])/gu, '\\$1')
.replaceAll(/^\^/gu, '\\^')
.replaceAll(/\$$/gu, '\\$');
}
}
exports.TermFunctionRegex = TermFunctionRegex;
//# sourceMappingURL=TermFunctionRegex.js.map