Mirror: The magical sticky regex-based parser generator 🧙
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Refactor to remove __private.exec in favour of just pattern functions

+62 -78
+9 -6
src/babel/__snapshots__/plugin.test.js.snap
··· 1 1 // Jest Snapshot v1, https://goo.gl/fbAQLP 2 2 3 3 exports[`deduplicates hoisted expressions 1`] = ` 4 - "import { match, __private } from \\"reghex\\"; 4 + "import { match, __pattern as _pattern } from \\"reghex\\"; 5 5 const re = /1/; 6 6 const str = '1'; 7 7 8 - var _re_expression = __private.pattern(re); 8 + var _re_expression = _pattern(re), 9 + _str_expression = _pattern(str); 9 10 10 11 const a = function (state) { 11 12 var y1 = state.y, ··· 13 14 var node = []; 14 15 var x; 15 16 16 - if ((x = __private.exec(state, _re_expression)) != null) { 17 + if ((x = _re_expression(state)) != null) { 17 18 node.push(x); 18 19 } else { 19 20 state.y = y1; ··· 21 22 return; 22 23 } 23 24 24 - if ((x = __private.exec(state, str)) != null) { 25 + if ((x = _str_expression(state)) != null) { 25 26 node.push(x); 26 27 } else { 27 28 state.y = y1; ··· 33 34 return node; 34 35 }; 35 36 37 + var _b_expression = _pattern('2'); 38 + 36 39 const b = function (state) { 37 40 var y1 = state.y, 38 41 x1 = state.x; 39 42 var node = []; 40 43 var x; 41 44 42 - if ((x = __private.exec(state, _re_expression)) != null) { 45 + if ((x = _re_expression(state)) != null) { 43 46 node.push(x); 44 47 } else { 45 48 state.y = y1; ··· 47 50 return; 48 51 } 49 52 50 - if ((x = __private.exec(state, \\"2\\")) != null) { 53 + if ((x = _b_expression(state)) != null) { 51 54 node.push(x); 52 55 } else { 53 56 state.y = y1;
+7 -11
src/babel/transform.js
··· 1 - import { astRoot, _private } from '../codegen'; 1 + import { astRoot } from '../codegen'; 2 2 import { parse } from '../parser'; 3 3 4 4 export function makeHelpers({ types: t, template }) { ··· 8 8 9 9 let _hasUpdatedImport = false; 10 10 let _matchId = t.identifier('match'); 11 - let _privateId = t.identifier(_private); 11 + let _patternId = t.identifier('__pattern'); 12 12 13 13 const _hoistedExpressions = new Map(); 14 14 15 - const privateMethod = (name) => 16 - t.memberExpression(t.identifier(_privateId.name), t.identifier(name)); 17 - 18 15 return { 19 16 /** Adds the reghex import declaration to the Program scope */ 20 17 updateImport(path) { ··· 26 23 path.node.source = t.stringLiteral(importName); 27 24 } 28 25 29 - path.node.specifiers.push(t.importSpecifier(_privateId, _privateId)); 26 + _patternId = path.scope.generateUidIdentifier('_pattern'); 27 + path.node.specifiers.push( 28 + t.importSpecifier(_patternId, t.identifier('__pattern')) 29 + ); 30 30 31 31 const tagImport = path.node.specifiers.find((node) => { 32 32 return t.isImportSpecifier(node) && node.imported.name === 'match'; ··· 116 116 t.isIdentifier(expression.body.body[0].argument) 117 117 ) { 118 118 expression = expression.body.body[0].argument; 119 - } else if (t.isStringLiteral(expression)) { 120 - return expression; 121 119 } 122 120 123 121 const isBindingExpression = ··· 128 126 if (t.isVariableDeclarator(binding.path.node)) { 129 127 const matchPath = binding.path.get('init'); 130 128 if (this.isMatch(matchPath)) { 131 - return expression; 132 - } else if (t.isStringLiteral(matchPath)) { 133 129 return expression; 134 130 } else if (_hoistedExpressions.has(expression.name)) { 135 131 return t.identifier(_hoistedExpressions.get(expression.name)); ··· 146 142 variableDeclarators.push( 147 143 t.variableDeclarator( 148 144 id, 149 - t.callExpression(privateMethod('pattern'), [expression]) 145 + t.callExpression(t.identifier(_patternId.name), [expression]) 150 146 ) 151 147 ); 152 148
+2 -7
src/codegen.js
··· 1 - export const _private = '__private'; 2 - 3 1 const _state = 'state'; 4 2 const _node = 'node'; 5 3 const _match = 'x'; ··· 31 29 const restoreLength = 32 30 (opts.length && opts.abort && js`${_node}.length = ln${opts.length};`) || 33 31 ''; 34 - const expression = ast.expression.fn 35 - ? `${ast.expression.id}(${_state})` 36 - : `${_private}.exec(${_state}, ${ast.expression.id})`; 37 - 32 + const expression = `${ast.expression.id}(${_state})`; 38 33 return js` 39 - if ((${_match} = ${expression}) != null) { 34 + if ((${_match} = ${ast.expression.id}(${_state})) != null) { 40 35 ${opts.capture ? js`${_node}.push(${_match})` : ''} 41 36 } else { 42 37 ${opts.onAbort}
+44 -54
src/core.js
··· 1 - import { astRoot, _private as privateId } from './codegen'; 1 + import { astRoot } from './codegen'; 2 2 import { parse as parseDSL } from './parser'; 3 3 4 4 const isStickySupported = typeof /./g.sticky === 'boolean'; 5 5 6 - export const __private = { 7 - pattern(input) { 8 - if (typeof input === 'function' || typeof input === 'string') { 9 - return input; 10 - } 6 + const execLambda = (pattern) => { 7 + if (pattern.length) return pattern; 8 + return (state) => pattern()(state); 9 + }; 11 10 12 - const source = typeof input !== 'string' ? input.source : input; 13 - return isStickySupported 14 - ? new RegExp(source, 'y') 15 - : new RegExp(source + '|()', 'g'); 16 - }, 17 - 18 - exec(state, pattern) { 19 - let match; 20 - 21 - if (typeof pattern === 'function') { 22 - if (!pattern.length) pattern = pattern(); 23 - return pattern(state); 11 + const execString = (pattern) => (state) => { 12 + const input = state.quasis[state.x]; 13 + if (input && state.y < input.length) { 14 + const sub = input.slice(state.y, state.y + pattern.length); 15 + if (sub === pattern) { 16 + state.y += pattern.length; 17 + return sub; 24 18 } 19 + } 20 + }; 25 21 22 + const execRegex = (pattern) => { 23 + pattern = isStickySupported 24 + ? new RegExp(pattern.source, 'y') 25 + : new RegExp(pattern.source + '|()', 'g'); 26 + return (state) => { 26 27 const input = state.quasis[state.x]; 27 28 if (input && state.y < input.length) { 28 - if (typeof pattern === 'string') { 29 - const end = state.y + pattern.length; 30 - const sub = input.slice(state.y, end); 31 - if (sub === pattern) { 32 - state.y = end; 33 - match = sub; 34 - } 35 - } else { 36 - pattern.lastIndex = state.y; 37 - if (isStickySupported) { 38 - if (pattern.test(input)) 39 - match = input.slice(state.y, pattern.lastIndex); 40 - } else { 41 - const x = pattern.exec(input); 42 - match = x[1] == null ? x[0] : match; 43 - } 29 + pattern.lastIndex = state.y; 44 30 45 - state.y = pattern.lastIndex; 31 + let match; 32 + if (isStickySupported) { 33 + if (pattern.test(input)) 34 + match = input.slice(state.y, pattern.lastIndex); 35 + } else { 36 + const x = pattern.exec(input); 37 + if (x[1] == null) match = x[0]; 46 38 } 39 + 40 + state.y = pattern.lastIndex; 41 + return match; 47 42 } 43 + }; 44 + }; 48 45 49 - return match; 50 - }, 46 + export const __pattern = (input) => { 47 + if (typeof input === 'function') { 48 + return execLambda(input); 49 + } else if (typeof input === 'string') { 50 + return execString(input); 51 + } else { 52 + return execRegex(input); 53 + } 51 54 }; 52 55 53 56 export const interpolation = (predicate) => (state) => { ··· 74 77 export const match = (name, transform) => (quasis, ...expressions) => { 75 78 const ast = parseDSL( 76 79 quasis, 77 - expressions.map((expression, i) => ({ 78 - fn: typeof expression === 'function' && expression.length, 79 - id: `_${i}`, 80 - })) 80 + expressions.map((_, i) => ({ id: `_${i}` })) 81 81 ); 82 - 83 - const makeMatcher = new Function( 84 - privateId + 85 - ',_n,_t,' + 86 - expressions.map((_expression, i) => `_${i}`).join(','), 82 + return new Function( 83 + '_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','), 87 84 'return ' + astRoot(ast, '_n', transform ? '_t' : null) 88 - ); 89 - 90 - return makeMatcher( 91 - __private, 92 - name, 93 - transform, 94 - ...expressions.map(__private.pattern) 95 - ); 85 + )(name, transform, ...expressions.map(__pattern)); 96 86 };