103 lines
2.8 KiB
JavaScript
103 lines
2.8 KiB
JavaScript
// ==========================================
|
|
// Regex Tester — Browser Extension
|
|
// ==========================================
|
|
// Tests regular expressions against input strings.
|
|
// Registers the regex_test tool so the LLM can verify
|
|
// patterns instead of guessing.
|
|
// ==========================================
|
|
|
|
Extensions.register({
|
|
id: 'regex-tester',
|
|
|
|
async init(ctx) {
|
|
const self = this;
|
|
|
|
ctx.tools.handle('regex_test', async (args) => {
|
|
return self._test(args.pattern, args.flags || '', args.inputs || []);
|
|
});
|
|
},
|
|
|
|
_test(pattern, flags, inputs) {
|
|
// Validate pattern
|
|
let regex;
|
|
try {
|
|
regex = new RegExp(pattern, flags);
|
|
} catch (e) {
|
|
return {
|
|
success: false,
|
|
error: `Invalid regex: ${e.message}`,
|
|
pattern,
|
|
flags,
|
|
};
|
|
}
|
|
|
|
const results = inputs.map(input => {
|
|
// Reset lastIndex for global/sticky patterns
|
|
regex.lastIndex = 0;
|
|
|
|
const isGlobal = regex.global;
|
|
const matches = [];
|
|
|
|
if (isGlobal) {
|
|
// Collect all matches for global flag
|
|
let match;
|
|
while ((match = regex.exec(input)) !== null) {
|
|
matches.push(this._formatMatch(match));
|
|
// Prevent infinite loops on zero-length matches
|
|
if (match[0].length === 0) regex.lastIndex++;
|
|
}
|
|
} else {
|
|
const match = regex.exec(input);
|
|
if (match) {
|
|
matches.push(this._formatMatch(match));
|
|
}
|
|
}
|
|
|
|
return {
|
|
input,
|
|
matched: matches.length > 0,
|
|
matchCount: matches.length,
|
|
matches,
|
|
};
|
|
});
|
|
|
|
const totalMatched = results.filter(r => r.matched).length;
|
|
|
|
return {
|
|
success: true,
|
|
pattern,
|
|
flags,
|
|
summary: `${totalMatched}/${inputs.length} inputs matched`,
|
|
results,
|
|
};
|
|
},
|
|
|
|
_formatMatch(match) {
|
|
const result = {
|
|
fullMatch: match[0],
|
|
index: match.index,
|
|
length: match[0].length,
|
|
};
|
|
|
|
// Named groups (ES2018)
|
|
if (match.groups) {
|
|
result.namedGroups = {};
|
|
for (const [name, value] of Object.entries(match.groups)) {
|
|
result.namedGroups[name] = value;
|
|
}
|
|
}
|
|
|
|
// Numbered capture groups
|
|
if (match.length > 1) {
|
|
result.groups = [];
|
|
for (let i = 1; i < match.length; i++) {
|
|
result.groups.push(match[i] !== undefined ? match[i] : null);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
destroy() {}
|
|
});
|