diff --git a/README.md b/README.md
index 271f82e..e7783f6 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,19 @@ The returned `flags` (as well as the `pattern`, of course) might be different th
If the only keys returned are `pattern` and `flags`, they can optionally be provided to JavaScript's `RegExp` constructor instead. Setting option `avoidSubclass` to `true` ensures that this is always the case, and any patterns that rely on `EmulatedRegExp`'s additional handling for emulation throw an error.
+### `toOnigurumaAst`
+
+Generates an Oniguruma AST from an Oniguruma pattern.
+
+```ts
+function toOnigurumaAst(
+ pattern: string,
+ options?: {
+ flags?: OnigurumaFlags;
+ }
+): OnigurumaAst;
+```
+
### `EmulatedRegExp`
Works the same as the native JavaScript `RegExp` constructor in all contexts, but can be provided results from `toDetails` to produce the same result as `toRegExp`.
@@ -135,19 +148,6 @@ class EmulatedRegExp extends RegExp {
};
```
-### `toOnigurumaAst`
-
-Generates an Oniguruma AST from an Oniguruma pattern.
-
-```ts
-function toOnigurumaAst(
- pattern: string,
- options?: {
- flags?: OnigurumaFlags;
- }
-): OnigurumaAst;
-```
-
## 🔩 Options
The following options are shared by functions [`toRegExp`](#toregexp) and [`toDetails`](#todetails).
@@ -620,7 +620,7 @@ Notice that nearly every feature below has at least subtle differences from Java
✅ |
✅ |
- ✔ Same as JS ^ $ without flag m
+ ✔ Same as JS ^ $ without JS flag m
|
diff --git a/demo/demo.css b/demo/demo.css
index bd8f4e2..fb3dbdc 100644
--- a/demo/demo.css
+++ b/demo/demo.css
@@ -200,7 +200,6 @@ pre, code, kbd, textarea {
background: repeating-linear-gradient(45deg, #edfff1, #edfff1 3px, #deffd5 3px, #deffd5 10px);
}
-#info {
- margin-top: -12px;
- padding: 0.6em;
+.info {
+ font-size: 0.9em;
}
diff --git a/demo/demo.js b/demo/demo.js
index 62b93e8..865aa10 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -1,3 +1,9 @@
+const ui = {
+ input: document.getElementById('input'),
+ output: document.getElementById('output'),
+ subclassInfo: document.getElementById('subclass-info'),
+ alternateInfo: document.getElementById('alternate-info'),
+};
const state = {
flags: {
i: getValue('flag-i'),
@@ -15,44 +21,95 @@ const state = {
},
};
-const inputEl = document.getElementById('input');
-autoGrow(inputEl);
-showOutput(inputEl);
+autoGrow();
+showTranspiled();
+
+function autoGrow() {
+ ui.input.style.height = '0';
+ ui.input.style.height = (ui.input.scrollHeight + 5) + 'px';
+}
-function showOutput(el) {
- const input = el.value;
- const flags = `${state.flags.i ? 'i' : ''}${state.flags.m ? 'm' : ''}${state.flags.x ? 'x' : ''}`;
- const outputEl = document.getElementById('output');
- const infoEl = document.getElementById('info');
- outputEl.classList.remove('error', 'subclass');
- infoEl.classList.add('hidden');
- const opts = {
+function showTranspiled() {
+ ui.output.classList.remove('error', 'subclass');
+ ui.subclassInfo.classList.add('hidden');
+ const options = {
...state.opts,
- flags,
+ flags: `${state.flags.i ? 'i' : ''}${state.flags.m ? 'm' : ''}${state.flags.x ? 'x' : ''}`,
maxRecursionDepth: state.opts.maxRecursionDepth === '' ? null : +state.opts.maxRecursionDepth,
};
- let output = '';
+ const errorObj = {error: true};
+ let details;
+ let result = '';
try {
- // Use `toDetails` but display output as if `toRegExp` was called. This avoids erroring when
- // the selected `target` includes features that don't work in the user's browser
- const details = OnigurumaToES.toDetails(input, opts);
+ // Use `toDetails` but display as if `toRegExp` was called. This avoids erroring when the
+ // selected `target` includes features that don't work in the user's browser
+ details = OnigurumaToES.toDetails(ui.input.value, options);
if (details.strategy) {
- infoEl.classList.remove('hidden');
- outputEl.classList.add('subclass');
- output = getFormattedSubclass(details.pattern, details.flags, details.strategy);
+ result = getFormattedSubclass(details.pattern, details.flags, details.strategy);
+ ui.subclassInfo.classList.remove('hidden');
+ ui.output.classList.add('subclass');
} else {
- output = `/${getRegExpLiteralPattern(details.pattern)}/${details.flags}`;
+ result = `/${getRegExpLiteralPattern(details.pattern)}/${details.flags}`;
}
} catch (err) {
- outputEl.classList.add('error');
- output = `Error: ${err.message}`;
+ details = errorObj;
+ result = `Error: ${err.message}`;
+ ui.output.classList.add('error');
+ }
+ ui.output.innerHTML = escapeHtml(result);
+
+ // ## Compare to all other accuracy/target combinations
+ const otherTargetAccuracyCombinations = ['ES2018', 'ES2024', 'ESNext'].flatMap(
+ t => ['loose', 'default', 'strict'].map(a => ({target: t, accuracy: a}))
+ ).filter(c => c.target !== options.target || c.accuracy !== options.accuracy);
+ const differents = [];
+ // Collect the different results, including differences in error status
+ for (const other of otherTargetAccuracyCombinations) {
+ let otherDetails;
+ try {
+ otherDetails = OnigurumaToES.toDetails(ui.input.value, {...options, ...other});
+ } catch (err) {
+ otherDetails = errorObj;
+ } finally {
+ if (!areDetailsEqual(details, otherDetails)) {
+ differents.push({
+ ...other,
+ error: !!otherDetails.error,
+ });
+ }
+ }
+ }
+ // Compose and display message about differences or lack thereof
+ if (differents.length) {
+ let str = '🔀';
+ const withError = [];
+ const withDiff = [];
+ differents.forEach(d => (d.error ? withError : withDiff).push(d));
+ if (withError.length) {
+ str += ` Can't emulate for ${listDifferents(withError)}.`;
+ }
+ if (withDiff.length) {
+ str += ` Emulation uses different details for ${listDifferents(withDiff)}.`;
+ }
+ ui.alternateInfo.innerHTML = str;
+ } else {
+ ui.alternateInfo.innerHTML = `
🟰 Results are the same ${
+ details.error ? '' : '(apart from flag u
/v
) '
+ }with all other targets and accuracies.
`;
}
- outputEl.innerHTML = escapeHtml(output);
}
-function autoGrow(el) {
- el.style.height = '0';
- el.style.height = (el.scrollHeight + 5) + 'px';
+function areDetailsEqual(a, b) {
+ if (a.error && b.error) {
+ return true;
+ }
+ if (a.error !== b.error) {
+ return false;
+ }
+ return a.pattern === b.pattern &&
+ a.flags.replace(/[vu]/, '') === b.flags.replace(/[vu]/, '') &&
+ a.strategy?.name === b.strategy?.name &&
+ a.strategy?.subpattern === b.strategy?.subpattern;
}
function escapeHtml(str) {
@@ -76,12 +133,25 @@ function getValue(id) {
return el.type === 'checkbox' ? el.checked : el.value;
}
+function listDifferents(arr) {
+ const target = {};
+ for (const a of arr) {
+ target[a.target] ?? (target[a.target] = []);
+ target[a.target].push(a.accuracy);
+ }
+ return Object.keys(target).map(t => {
+ return `target '${t}'
with ${
+ target[t].length > 1 ? 'accuracies' : 'accuracy'
+ } '${target[t].join("'
/'")}'
`;
+ }).join(', ');
+}
+
function setFlag(flag, value) {
state.flags[flag] = value;
- showOutput(inputEl);
+ showTranspiled();
}
function setOption(option, value) {
state.opts[option] = value;
- showOutput(inputEl);
+ showTranspiled();
}
diff --git a/demo/index.html b/demo/index.html
index ab0e5b1..4adeec6 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -17,7 +17,7 @@
Use this page to test the output of Oniguruma-To-ES, an Oniguruma to JavaScript regex transpiler. See Readme: Supported features.
Try it
-
+