Mirror: The magical sticky regex-based parser generator 🧙

Update codegen to use a combined __private object of methods

Changed files
+129 -111
src
+76 -43
src/babel/__snapshots__/plugin.test.js.snap
···
var _reghex = require(\\"reghex\\");
-
var _node_expression = (0, _reghex._pattern)(1),
-
_node_expression2 = (0, _reghex._pattern)(2);
+
var _node_expression = _reghex.__private.pattern(1),
+
_node_expression2 = _reghex.__private.pattern(2);
const node = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
-
if (x = (0, _reghex._exec)(state, _node_expression)) {
+
if (x = _reghex.__private.exec(state, _node_expression)) {
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
-
if (x = (0, _reghex._exec)(state, _node_expression2)) {
+
if (x = _reghex.__private.exec(state, _node_expression2)) {
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
`;
exports[`works with local recursion 1`] = `
-
"import { match as m, tag, _exec, _pattern } from 'reghex';
+
"import { match as m, tag, __private } from 'reghex';
-
var _inner_expression = _pattern(/inner/);
+
var _inner_expression = __private.pattern(/inner/);
const inner = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
-
if (x = _exec(state, _inner_expression)) {
+
if (x = __private.exec(state, _inner_expression)) {
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
};
const node = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
···
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
`;
exports[`works with non-capturing groups 1`] = `
-
"import { match, _exec, _pattern, tag as _tag } from 'reghex';
+
"import { match, __private } from 'reghex';
-
var _node_expression = _pattern(1),
-
_node_expression2 = _pattern(2),
-
_node_expression3 = _pattern(3);
+
var _node_expression = __private.pattern(1),
+
_node_expression2 = __private.pattern(2),
+
_node_expression3 = __private.pattern(3);
const node = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
-
if (x = _exec(state, _node_expression)) {
+
if (x = __private.exec(state, _node_expression)) {
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
alt_3: {
block_3: {
-
var y3 = state.y;
+
var y3 = state.y,
+
x3 = state.x;
-
if (x = _exec(state, _node_expression2)) {
+
if (x = __private.exec(state, _node_expression2)) {
node.push(x);
} else {
state.y = y3;
+
state.x = x3;
node.length = ln2;
break block_3;
}
···
}
loop_3: for (var j3 = 0; 1; j3++) {
-
var y3 = state.y;
+
var y3 = state.y,
+
x3 = state.x;
-
if (x = _exec(state, _node_expression3)) {} else {
+
if (x = __private.exec(state, _node_expression3)) {} else {
if (j3) {
state.y = y3;
+
state.x = x3;
break loop_3;
} else {}
state.y = y1;
+
state.x = x1;
node.length = ln2;
return;
}
···
`;
exports[`works with self-referential thunks 1`] = `
-
"import { match, tag, _exec, _pattern } from 'reghex';
+
"import { match, tag, __private } from 'reghex';
const inner = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
···
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
};
const node = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
···
node.push(x);
} else {
state.y = y1;
+
state.x = x1;
return;
}
···
`;
exports[`works with standard features 1`] = `
-
"import { match, _exec, _pattern, tag as _tag } from \\"reghex\\";
+
"import { match, __private } from \\"reghex\\";
-
var _node_expression = _pattern(1),
-
_node_expression2 = _pattern(2),
-
_node_expression3 = _pattern(3),
-
_node_expression4 = _pattern(4),
-
_node_expression5 = _pattern(5);
+
var _node_expression = __private.pattern(1),
+
_node_expression2 = __private.pattern(2),
+
_node_expression3 = __private.pattern(3),
+
_node_expression4 = __private.pattern(4),
+
_node_expression5 = __private.pattern(5);
const node = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
alt_2: {
block_2: {
-
var y2 = state.y;
+
var y2 = state.y,
+
x2 = state.x;
loop_2: for (var j2 = 0; 1; j2++) {
-
var y2 = state.y;
+
var y2 = state.y,
+
x2 = state.x;
-
if (x = _exec(state, _node_expression)) {
+
if (x = __private.exec(state, _node_expression)) {
node.push(x);
} else {
if (j2) {
state.y = y2;
+
state.x = x2;
break loop_2;
} else {}
state.y = y2;
+
state.x = x2;
break block_2;
}
}
···
}
loop_2: for (var j2 = 0; 1; j2++) {
-
var y2 = state.y;
+
var y2 = state.y,
+
x2 = state.x;
-
if (x = _exec(state, _node_expression2)) {
+
if (x = __private.exec(state, _node_expression2)) {
node.push(x);
} else {
if (j2) {
state.y = y2;
+
state.x = x2;
break loop_2;
} else {}
state.y = y1;
+
state.x = x1;
return;
}
}
loop_2: for (;;) {
-
var y2 = state.y;
+
var y2 = state.y,
+
x2 = state.x;
var ln2 = node.length;
-
if (x = _exec(state, _node_expression3)) {
+
if (x = __private.exec(state, _node_expression3)) {
node.push(x);
} else {
state.y = y2;
+
state.x = x2;
node.length = ln2;
break loop_2;
}
-
var y4 = state.y;
+
var y4 = state.y,
+
x4 = state.x;
-
if (x = _exec(state, _node_expression4)) {
+
if (x = __private.exec(state, _node_expression4)) {
node.push(x);
} else {
state.y = y4;
+
state.x = x4;
}
-
if (x = _exec(state, _node_expression5)) {
+
if (x = __private.exec(state, _node_expression5)) {
node.push(x);
} else {
state.y = y2;
+
state.x = x2;
node.length = ln2;
break loop_2;
}
···
`;
exports[`works with transform functions 1`] = `
-
"import { match, _exec, _pattern, tag as _tag } from 'reghex';
+
"import { match, __private } from 'reghex';
var _inner_transform = x => x;
const first = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
node.tag = 'inner';
···
const transform = x => x;
const second = function (state) {
-
var y1 = state.y;
+
var y1 = state.y,
+
x1 = state.x;
var node = [];
var x;
node.tag = 'node';
-20
src/babel/sharedIds.js
···
-
export class SharedIds {
-
constructor(t) {
-
this.t = t;
-
this.execId = t.identifier('_exec');
-
this.patternId = t.identifier('_pattern');
-
this.tagId = t.identifier('tag');
-
}
-
-
get exec() {
-
return this.t.identifier(this.execId.name);
-
}
-
-
get pattern() {
-
return this.t.identifier(this.patternId.name);
-
}
-
-
get tag() {
-
return this.t.identifier(this.tagId.name);
-
}
-
}
+12 -18
src/babel/transform.js
···
+
import { astRoot, _private } from '../codegen';
import { parse } from '../parser';
-
import { astRoot } from '../codegen';
-
import { SharedIds } from './sharedIds';
export function makeHelpers({ types: t, template }) {
const regexPatternsRe = /^[()\[\]|.+?*]|[^\\][()\[\]|.+?*$^]|\\[wdsWDS]/;
const importSourceRe = /reghex$|^reghex\/macro/;
const importName = 'reghex';
-
const ids = new SharedIds(t);
let _hasUpdatedImport = false;
+
let _matchId = t.identifier('match');
+
let _privateId = t.identifier(_private);
+
+
const privateMethod = (name) =>
+
t.memberExpression(t.identifier(_privateId.name), t.identifier(name));
return {
/** Adds the reghex import declaration to the Program scope */
···
path.node.source = t.stringLiteral(importName);
}
-
path.node.specifiers.push(
-
t.importSpecifier(
-
(ids.execId = path.scope.generateUidIdentifier('exec')),
-
t.identifier('_exec')
-
),
-
t.importSpecifier(
-
(ids.patternId = path.scope.generateUidIdentifier('pattern')),
-
t.identifier('_pattern')
-
)
-
);
+
path.node.specifiers.push(t.importSpecifier(_privateId, _privateId));
const tagImport = path.node.specifiers.find((node) => {
-
return t.isImportSpecifier(node) && node.imported.name === 'tag';
+
return t.isImportSpecifier(node) && node.imported.name === 'match';
});
if (!tagImport) {
path.node.specifiers.push(
t.importSpecifier(
-
(ids.tagId = path.scope.generateUidIdentifier('tag')),
-
t.identifier('tag')
+
(_matchId = path.scope.generateUidIdentifier('match')),
+
t.identifier('match')
)
);
} else {
-
ids.tagId = tagImport.imported;
+
_matchId = tagImport.imported;
}
},
···
variableDeclarators.push(
t.variableDeclarator(
id,
-
t.callExpression(ids.pattern, [expression])
+
t.callExpression(privateMethod('pattern'), [expression])
)
);
+3 -2
src/codegen.js
···
-
export const _exec = '_exec';
+
export const _private = '__private';
+
const _state = 'state';
const _node = 'node';
const _match = 'x';
···
'';
const expression = ast.expression.fn
? `${ast.expression.id}(${_state})`
-
: `${_exec}(${_state}, ${ast.expression.id})`;
+
: `${_private}.exec(${_state}, ${ast.expression.id})`;
return js`
if (${_match} = ${expression}) {
+38 -28
src/core.js
···
-
import { astRoot, _exec as execId } from './codegen';
+
import { astRoot, _private as privateId } from './codegen';
import { parse as parseDSL } from './parser';
const isStickySupported = typeof /./g.sticky === 'boolean';
-
export const _pattern = (input) => {
-
if (typeof input === 'function') return input;
-
const source = typeof input !== 'string' ? input.source : input;
-
return isStickySupported
-
? new RegExp(source, 'y')
-
: new RegExp(source + '|()', 'g');
-
};
+
export const __private = {
+
pattern(input) {
+
if (typeof input === 'function') return input;
+
const source = typeof input !== 'string' ? input.source : input;
+
return isStickySupported
+
? new RegExp(source, 'y')
+
: new RegExp(source + '|()', 'g');
+
},
-
export const _exec = (state, pattern) => {
-
let match;
-
-
if (typeof pattern === 'function') {
-
if (!pattern.length) pattern = pattern();
-
return pattern(state);
-
}
+
exec(state, pattern) {
+
let match;
-
const input = state.quasis[state.x];
-
if (input && (pattern.lastIndex = state.y) < input.length) {
-
if (isStickySupported) {
-
if (pattern.test(input)) match = input.slice(state.y, pattern.lastIndex);
-
} else {
-
match = pattern.exec(input)[0] || match;
+
if (typeof pattern === 'function') {
+
if (!pattern.length) pattern = pattern();
+
return pattern(state);
}
-
state.y = pattern.lastIndex;
-
}
+
const input = state.quasis[state.x];
+
if (input && (pattern.lastIndex = state.y) < input.length) {
+
if (isStickySupported) {
+
if (pattern.test(input))
+
match = input.slice(state.y, pattern.lastIndex);
+
} else {
+
match = pattern.exec(input)[0] || match;
+
}
-
return match;
+
state.y = pattern.lastIndex;
+
}
+
+
return match;
+
},
};
export const interpolation = (predicate) => (state) => {
···
return match;
};
-
export const parse = (pattern) => (quasis, ...expressions) => {
+
export const parse = (matcher) => (quasis, ...expressions) => {
if (typeof quasis === 'string') quasis = [quasis];
const state = { quasis, expressions, x: 0, y: 0 };
-
return pattern(state);
+
return matcher(state);
};
export const match = (name, transform) => (quasis, ...expressions) => {
···
);
const makeMatcher = new Function(
-
execId + ',_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','),
+
privateId +
+
',_n,_t,' +
+
expressions.map((_expression, i) => `_${i}`).join(','),
'return ' + astRoot(ast, '_n', transform ? '_t' : null)
);
-
return makeMatcher(_exec, name, transform, ...expressions.map(_pattern));
+
return makeMatcher(
+
__private,
+
name,
+
transform,
+
...expressions.map(__private.pattern)
+
);
};