Mirror: The magical sticky regex-based parser generator 🧙

Merge pull request #11 from kitten/feat/string-matchers

Add support for string matchers

Changed files
+61 -9
src
+17
src/babel/__snapshots__/plugin.test.js.snap
···
exports[`deduplicates hoisted expressions 1`] = `
"import { match, __private } from \\"reghex\\";
const re = /1/;
+
const str = '1';
var _re_expression = __private.pattern(re);
···
var x;
if (x = __private.exec(state, _re_expression)) {
+
node.push(x);
+
} else {
+
state.y = y1;
+
state.x = x1;
+
return;
+
}
+
+
if (x = __private.exec(state, str)) {
node.push(x);
} else {
state.y = y1;
···
var x;
if (x = __private.exec(state, _re_expression)) {
+
node.push(x);
+
} else {
+
state.y = y1;
+
state.x = x1;
+
return;
+
}
+
+
if (x = __private.exec(state, \\"2\\")) {
node.push(x);
} else {
state.y = y1;
+4 -1
src/babel/plugin.test.js
···
).toMatchSnapshot();
});
-
it('deduplicates hoisted expressions', () => {
+
it.only('deduplicates hoisted expressions', () => {
const code = `
import { match } from 'reghex/macro';
const re = /1/;
+
const str = '1';
const a = match('a')\`
\${re}
+
\${str}
\`;
const b = match('b')\`
\${re}
+
\${'2'}
\`;
`;
+5
src/babel/transform.js
···
t.isIdentifier(expression.body.body[0].argument)
) {
expression = expression.body.body[0].argument;
+
} else if (t.isStringLiteral(expression)) {
+
return expression;
}
const isBindingExpression =
···
if (t.isVariableDeclarator(binding.path.node)) {
const matchPath = binding.path.get('init');
if (this.isMatch(matchPath)) {
+
return expression;
+
} else if (t.isStringLiteral(matchPath)) {
return expression;
} else if (_hoistedExpressions.has(expression.name)) {
return t.identifier(_hoistedExpressions.get(expression.name));
···
if (t.isIdentifier(expression)) {
_hoistedExpressions.set(expression.name, id.name);
}
+
return id;
}
);
+21 -8
src/core.js
···
export const __private = {
pattern(input) {
-
if (typeof input === 'function') return input;
+
if (typeof input === 'function' || typeof input === 'string') {
+
return input;
+
}
+
const source = typeof input !== 'string' ? input.source : input;
return isStickySupported
? new RegExp(source, 'y')
···
}
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);
+
if (input && state.y < input.length) {
+
if (typeof pattern === 'string') {
+
const end = state.y + pattern.length;
+
const sub = input.slice(state.y, end);
+
if (sub === pattern) {
+
state.y = end;
+
match = sub;
+
}
} else {
-
match = pattern.exec(input)[0] || match;
-
}
+
pattern.lastIndex = state.y;
+
if (isStickySupported) {
+
if (pattern.test(input))
+
match = input.slice(state.y, pattern.lastIndex);
+
} else {
+
match = pattern.exec(input)[0] || match;
+
}
-
state.y = pattern.lastIndex;
+
state.y = pattern.lastIndex;
+
}
}
return match;
+14
src/core.test.js
···
expect(parse(node)`1${1}3`).toBe(undefined);
});
});
+
+
describe('string matching', () => {
+
const node = match('node')`
+
${'1'}
+
${'2'}
+
`;
+
+
it('matches strings', () => {
+
const expected = ['1', '2'];
+
expected.tag = 'node';
+
expect(parse(node)('12')).toEqual(expected);
+
expect(parse(node)('13')).toBe(undefined);
+
});
+
});