From adf99f2fb1ffe1d89f936574549bb36a5437fee2 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 12 Sep 2024 20:49:19 +0200 Subject: [PATCH] feat(engine-js): introduce `simulation` option --- packages/engine-javascript/src/index.ts | 36 +++++++++++++------ .../engine-javascript/test/verify.test.ts | 2 +- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/engine-javascript/src/index.ts b/packages/engine-javascript/src/index.ts index 77df016e3..9a639a74e 100644 --- a/packages/engine-javascript/src/index.ts +++ b/packages/engine-javascript/src/index.ts @@ -8,13 +8,22 @@ import { onigurumaToRegexp } from 'oniguruma-to-js' export interface JavaScriptRegexEngineOptions { /** * Whether to allow invalid regex patterns. + * + * @default false */ forgiving?: boolean + /** + * Use JavaScript to simulate some unsupported regex features. + * + * @default true + */ + simulation?: boolean + /** * Cache for regex patterns. */ - cache?: Map + cache?: Map | null /** * Custom pattern to RegExp constructor. @@ -45,13 +54,18 @@ export class JavaScriptScanner implements PatternScanner { constructor( public patterns: string[], - public cache: Map, - public forgiving: boolean, - public regexConstructor: (pattern: string) => RegExp = defaultJavaScriptRegexConstructor, + public options: JavaScriptRegexEngineOptions = {}, ) { + const { + forgiving = false, + cache, + simulation = true, + regexConstructor = defaultJavaScriptRegexConstructor, + } = options + this.contiguousAnchorSimulation = Array.from({ length: patterns.length }, () => false) this.regexps = patterns.map((p, idx) => { - if (p.startsWith('(^|\\G)') || p.startsWith('(\\G|^)')) + if (simulation && (p.startsWith('(^|\\G)') || p.startsWith('(\\G|^)'))) this.contiguousAnchorSimulation[idx] = true const cached = cache?.get(p) if (cached) { @@ -129,7 +143,7 @@ export class JavaScriptScanner implements PatternScanner { pending.push([i, match, offset]) } catch (e) { - if (this.forgiving) + if (this.options.forgiving) continue throw e } @@ -159,14 +173,14 @@ export class JavaScriptScanner implements PatternScanner { * @experimental */ export function createJavaScriptRegexEngine(options: JavaScriptRegexEngineOptions = {}): RegexEngine { - const { - forgiving = false, - cache = new Map(), - } = options + const _options = { + cache: new Map(), + ...options, + } return { createScanner(patterns: string[]) { - return new JavaScriptScanner(patterns, cache, forgiving, options.regexConstructor) + return new JavaScriptScanner(patterns, _options) }, createString(s: string) { return { diff --git a/packages/engine-javascript/test/verify.test.ts b/packages/engine-javascript/test/verify.test.ts index f04d9f22a..67e1b3099 100644 --- a/packages/engine-javascript/test/verify.test.ts +++ b/packages/engine-javascript/test/verify.test.ts @@ -28,7 +28,7 @@ for (const file of files) { for (const instance of instances) { i += 1 describe(`instances ${i}`, () => { - const scanner = new JavaScriptScanner(instance.constractor[0], cache, false) + const scanner = new JavaScriptScanner(instance.constractor[0], { cache }) let j = 0 for (const execution of instance.executions) { j += 1