74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
export const regexpMethods = new Set(["test", "exec", "toString"])
|
|
|
|
export const regexpProperties = new Set([
|
|
"source",
|
|
"flags",
|
|
"lastIndex",
|
|
"global",
|
|
"ignoreCase",
|
|
"multiline",
|
|
"sticky",
|
|
"unicode",
|
|
"dotAll",
|
|
])
|
|
|
|
export const regexFailureReason = (error: unknown): string =>
|
|
(error instanceof Error ? error.message : String(error)).replace(/^Invalid regular expression:\s*/i, "")
|
|
|
|
export const escapeRegexHint =
|
|
'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.'
|
|
|
|
export const toHostRegex = (arg: unknown, method: string, node: AstNode, extraFlags = ""): RegExp => {
|
|
if (arg instanceof CodeModeRegExp) return arg.regex
|
|
if (typeof arg === "string") {
|
|
try {
|
|
return new RegExp(arg, extraFlags)
|
|
} catch (error) {
|
|
throw new InterpreterRuntimeError(
|
|
`String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`,
|
|
node,
|
|
).as("SyntaxError")
|
|
}
|
|
}
|
|
throw new InterpreterRuntimeError(
|
|
`String.${method} expects a regular expression (a /pattern/flags literal or new RegExp(...)) or a string pattern, not ${arg === null ? "null" : typeof arg}.`,
|
|
node,
|
|
)
|
|
}
|
|
|
|
export const matchToValue = (match: RegExpMatchArray): Array<unknown> => {
|
|
const result: Array<unknown> = Array.from(match, (group) => group)
|
|
if (match.index !== undefined) (result as Record<string, unknown> & Array<unknown>).index = match.index
|
|
if (match.groups) {
|
|
const groups: SafeObject = Object.create(null) as SafeObject
|
|
for (const [key, group] of Object.entries(match.groups)) {
|
|
if (!isBlockedMember(key)) groups[key] = group
|
|
}
|
|
;(result as Record<string, unknown> & Array<unknown>).groups = groups
|
|
}
|
|
return result
|
|
}
|
|
|
|
export const invokeRegExpMethod = (
|
|
value: CodeModeRegExp,
|
|
name: string,
|
|
args: Array<unknown>,
|
|
node: AstNode,
|
|
): unknown => {
|
|
switch (name) {
|
|
case "test":
|
|
return value.regex.test(coerceToString(args[0]))
|
|
case "exec": {
|
|
const matched = value.regex.exec(coerceToString(args[0]))
|
|
return matched === null ? null : matchToValue(matched)
|
|
}
|
|
case "toString":
|
|
return coerceToString(value)
|
|
default:
|
|
throw new InterpreterRuntimeError(`RegExp method '${name}' is not available in CodeMode.`, node)
|
|
}
|
|
}
|
|
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
|
import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
|
|
import { CodeModeRegExp } from "../values.js"
|
|
import { coerceToString } from "./value.js"
|