Mirror: The magical sticky regex-based parser generator 🧙

Update codegen to use a combined __private object of methods

+129 -111
+76 -43
src/babel/__snapshots__/plugin.test.js.snap
··· 5 5 6 6 var _reghex = require(\\"reghex\\"); 7 7 8 - var _node_expression = (0, _reghex._pattern)(1), 9 - _node_expression2 = (0, _reghex._pattern)(2); 8 + var _node_expression = _reghex.__private.pattern(1), 9 + _node_expression2 = _reghex.__private.pattern(2); 10 10 11 11 const node = function (state) { 12 - var y1 = state.y; 12 + var y1 = state.y, 13 + x1 = state.x; 13 14 var node = []; 14 15 var x; 15 16 16 - if (x = (0, _reghex._exec)(state, _node_expression)) { 17 + if (x = _reghex.__private.exec(state, _node_expression)) { 17 18 node.push(x); 18 19 } else { 19 20 state.y = y1; 21 + state.x = x1; 20 22 return; 21 23 } 22 24 23 - if (x = (0, _reghex._exec)(state, _node_expression2)) { 25 + if (x = _reghex.__private.exec(state, _node_expression2)) { 24 26 node.push(x); 25 27 } else { 26 28 state.y = y1; 29 + state.x = x1; 27 30 return; 28 31 } 29 32 ··· 38 41 `; 39 42 40 43 exports[`works with local recursion 1`] = ` 41 - "import { match as m, tag, _exec, _pattern } from 'reghex'; 44 + "import { match as m, tag, __private } from 'reghex'; 42 45 43 - var _inner_expression = _pattern(/inner/); 46 + var _inner_expression = __private.pattern(/inner/); 44 47 45 48 const inner = function (state) { 46 - var y1 = state.y; 49 + var y1 = state.y, 50 + x1 = state.x; 47 51 var node = []; 48 52 var x; 49 53 50 - if (x = _exec(state, _inner_expression)) { 54 + if (x = __private.exec(state, _inner_expression)) { 51 55 node.push(x); 52 56 } else { 53 57 state.y = y1; 58 + state.x = x1; 54 59 return; 55 60 } 56 61 ··· 59 64 }; 60 65 61 66 const node = function (state) { 62 - var y1 = state.y; 67 + var y1 = state.y, 68 + x1 = state.x; 63 69 var node = []; 64 70 var x; 65 71 ··· 67 73 node.push(x); 68 74 } else { 69 75 state.y = y1; 76 + state.x = x1; 70 77 return; 71 78 } 72 79 ··· 76 83 `; 77 84 78 85 exports[`works with non-capturing groups 1`] = ` 79 - "import { match, _exec, _pattern, tag as _tag } from 'reghex'; 86 + "import { match, __private } from 'reghex'; 80 87 81 - var _node_expression = _pattern(1), 82 - _node_expression2 = _pattern(2), 83 - _node_expression3 = _pattern(3); 88 + var _node_expression = __private.pattern(1), 89 + _node_expression2 = __private.pattern(2), 90 + _node_expression3 = __private.pattern(3); 84 91 85 92 const node = function (state) { 86 - var y1 = state.y; 93 + var y1 = state.y, 94 + x1 = state.x; 87 95 var node = []; 88 96 var x; 89 97 90 - if (x = _exec(state, _node_expression)) { 98 + if (x = __private.exec(state, _node_expression)) { 91 99 node.push(x); 92 100 } else { 93 101 state.y = y1; 102 + state.x = x1; 94 103 return; 95 104 } 96 105 ··· 98 107 99 108 alt_3: { 100 109 block_3: { 101 - var y3 = state.y; 110 + var y3 = state.y, 111 + x3 = state.x; 102 112 103 - if (x = _exec(state, _node_expression2)) { 113 + if (x = __private.exec(state, _node_expression2)) { 104 114 node.push(x); 105 115 } else { 106 116 state.y = y3; 117 + state.x = x3; 107 118 node.length = ln2; 108 119 break block_3; 109 120 } ··· 112 123 } 113 124 114 125 loop_3: for (var j3 = 0; 1; j3++) { 115 - var y3 = state.y; 126 + var y3 = state.y, 127 + x3 = state.x; 116 128 117 - if (x = _exec(state, _node_expression3)) {} else { 129 + if (x = __private.exec(state, _node_expression3)) {} else { 118 130 if (j3) { 119 131 state.y = y3; 132 + state.x = x3; 120 133 break loop_3; 121 134 } else {} 122 135 123 136 state.y = y1; 137 + state.x = x1; 124 138 node.length = ln2; 125 139 return; 126 140 } ··· 133 147 `; 134 148 135 149 exports[`works with self-referential thunks 1`] = ` 136 - "import { match, tag, _exec, _pattern } from 'reghex'; 150 + "import { match, tag, __private } from 'reghex'; 137 151 138 152 const inner = function (state) { 139 - var y1 = state.y; 153 + var y1 = state.y, 154 + x1 = state.x; 140 155 var node = []; 141 156 var x; 142 157 ··· 144 159 node.push(x); 145 160 } else { 146 161 state.y = y1; 162 + state.x = x1; 147 163 return; 148 164 } 149 165 ··· 152 168 }; 153 169 154 170 const node = function (state) { 155 - var y1 = state.y; 171 + var y1 = state.y, 172 + x1 = state.x; 156 173 var node = []; 157 174 var x; 158 175 ··· 160 177 node.push(x); 161 178 } else { 162 179 state.y = y1; 180 + state.x = x1; 163 181 return; 164 182 } 165 183 ··· 169 187 `; 170 188 171 189 exports[`works with standard features 1`] = ` 172 - "import { match, _exec, _pattern, tag as _tag } from \\"reghex\\"; 190 + "import { match, __private } from \\"reghex\\"; 173 191 174 - var _node_expression = _pattern(1), 175 - _node_expression2 = _pattern(2), 176 - _node_expression3 = _pattern(3), 177 - _node_expression4 = _pattern(4), 178 - _node_expression5 = _pattern(5); 192 + var _node_expression = __private.pattern(1), 193 + _node_expression2 = __private.pattern(2), 194 + _node_expression3 = __private.pattern(3), 195 + _node_expression4 = __private.pattern(4), 196 + _node_expression5 = __private.pattern(5); 179 197 180 198 const node = function (state) { 181 - var y1 = state.y; 199 + var y1 = state.y, 200 + x1 = state.x; 182 201 var node = []; 183 202 var x; 184 203 185 204 alt_2: { 186 205 block_2: { 187 - var y2 = state.y; 206 + var y2 = state.y, 207 + x2 = state.x; 188 208 189 209 loop_2: for (var j2 = 0; 1; j2++) { 190 - var y2 = state.y; 210 + var y2 = state.y, 211 + x2 = state.x; 191 212 192 - if (x = _exec(state, _node_expression)) { 213 + if (x = __private.exec(state, _node_expression)) { 193 214 node.push(x); 194 215 } else { 195 216 if (j2) { 196 217 state.y = y2; 218 + state.x = x2; 197 219 break loop_2; 198 220 } else {} 199 221 200 222 state.y = y2; 223 + state.x = x2; 201 224 break block_2; 202 225 } 203 226 } ··· 206 229 } 207 230 208 231 loop_2: for (var j2 = 0; 1; j2++) { 209 - var y2 = state.y; 232 + var y2 = state.y, 233 + x2 = state.x; 210 234 211 - if (x = _exec(state, _node_expression2)) { 235 + if (x = __private.exec(state, _node_expression2)) { 212 236 node.push(x); 213 237 } else { 214 238 if (j2) { 215 239 state.y = y2; 240 + state.x = x2; 216 241 break loop_2; 217 242 } else {} 218 243 219 244 state.y = y1; 245 + state.x = x1; 220 246 return; 221 247 } 222 248 } 223 249 224 250 loop_2: for (;;) { 225 - var y2 = state.y; 251 + var y2 = state.y, 252 + x2 = state.x; 226 253 var ln2 = node.length; 227 254 228 - if (x = _exec(state, _node_expression3)) { 255 + if (x = __private.exec(state, _node_expression3)) { 229 256 node.push(x); 230 257 } else { 231 258 state.y = y2; 259 + state.x = x2; 232 260 node.length = ln2; 233 261 break loop_2; 234 262 } 235 263 236 - var y4 = state.y; 264 + var y4 = state.y, 265 + x4 = state.x; 237 266 238 - if (x = _exec(state, _node_expression4)) { 267 + if (x = __private.exec(state, _node_expression4)) { 239 268 node.push(x); 240 269 } else { 241 270 state.y = y4; 271 + state.x = x4; 242 272 } 243 273 244 - if (x = _exec(state, _node_expression5)) { 274 + if (x = __private.exec(state, _node_expression5)) { 245 275 node.push(x); 246 276 } else { 247 277 state.y = y2; 278 + state.x = x2; 248 279 node.length = ln2; 249 280 break loop_2; 250 281 } ··· 257 288 `; 258 289 259 290 exports[`works with transform functions 1`] = ` 260 - "import { match, _exec, _pattern, tag as _tag } from 'reghex'; 291 + "import { match, __private } from 'reghex'; 261 292 262 293 var _inner_transform = x => x; 263 294 264 295 const first = function (state) { 265 - var y1 = state.y; 296 + var y1 = state.y, 297 + x1 = state.x; 266 298 var node = []; 267 299 var x; 268 300 node.tag = 'inner'; ··· 272 304 const transform = x => x; 273 305 274 306 const second = function (state) { 275 - var y1 = state.y; 307 + var y1 = state.y, 308 + x1 = state.x; 276 309 var node = []; 277 310 var x; 278 311 node.tag = 'node';
-20
src/babel/sharedIds.js
··· 1 - export class SharedIds { 2 - constructor(t) { 3 - this.t = t; 4 - this.execId = t.identifier('_exec'); 5 - this.patternId = t.identifier('_pattern'); 6 - this.tagId = t.identifier('tag'); 7 - } 8 - 9 - get exec() { 10 - return this.t.identifier(this.execId.name); 11 - } 12 - 13 - get pattern() { 14 - return this.t.identifier(this.patternId.name); 15 - } 16 - 17 - get tag() { 18 - return this.t.identifier(this.tagId.name); 19 - } 20 - }
+12 -18
src/babel/transform.js
··· 1 + import { astRoot, _private } from '../codegen'; 1 2 import { parse } from '../parser'; 2 - import { astRoot } from '../codegen'; 3 - import { SharedIds } from './sharedIds'; 4 3 5 4 export function makeHelpers({ types: t, template }) { 6 5 const regexPatternsRe = /^[()\[\]|.+?*]|[^\\][()\[\]|.+?*$^]|\\[wdsWDS]/; 7 6 const importSourceRe = /reghex$|^reghex\/macro/; 8 7 const importName = 'reghex'; 9 - const ids = new SharedIds(t); 10 8 11 9 let _hasUpdatedImport = false; 10 + let _matchId = t.identifier('match'); 11 + let _privateId = t.identifier(_private); 12 + 13 + const privateMethod = (name) => 14 + t.memberExpression(t.identifier(_privateId.name), t.identifier(name)); 12 15 13 16 return { 14 17 /** Adds the reghex import declaration to the Program scope */ ··· 21 24 path.node.source = t.stringLiteral(importName); 22 25 } 23 26 24 - path.node.specifiers.push( 25 - t.importSpecifier( 26 - (ids.execId = path.scope.generateUidIdentifier('exec')), 27 - t.identifier('_exec') 28 - ), 29 - t.importSpecifier( 30 - (ids.patternId = path.scope.generateUidIdentifier('pattern')), 31 - t.identifier('_pattern') 32 - ) 33 - ); 27 + path.node.specifiers.push(t.importSpecifier(_privateId, _privateId)); 34 28 35 29 const tagImport = path.node.specifiers.find((node) => { 36 - return t.isImportSpecifier(node) && node.imported.name === 'tag'; 30 + return t.isImportSpecifier(node) && node.imported.name === 'match'; 37 31 }); 38 32 39 33 if (!tagImport) { 40 34 path.node.specifiers.push( 41 35 t.importSpecifier( 42 - (ids.tagId = path.scope.generateUidIdentifier('tag')), 43 - t.identifier('tag') 36 + (_matchId = path.scope.generateUidIdentifier('match')), 37 + t.identifier('match') 44 38 ) 45 39 ); 46 40 } else { 47 - ids.tagId = tagImport.imported; 41 + _matchId = tagImport.imported; 48 42 } 49 43 }, 50 44 ··· 140 134 variableDeclarators.push( 141 135 t.variableDeclarator( 142 136 id, 143 - t.callExpression(ids.pattern, [expression]) 137 + t.callExpression(privateMethod('pattern'), [expression]) 144 138 ) 145 139 ); 146 140
+3 -2
src/codegen.js
··· 1 - export const _exec = '_exec'; 1 + export const _private = '__private'; 2 + 2 3 const _state = 'state'; 3 4 const _node = 'node'; 4 5 const _match = 'x'; ··· 32 33 ''; 33 34 const expression = ast.expression.fn 34 35 ? `${ast.expression.id}(${_state})` 35 - : `${_exec}(${_state}, ${ast.expression.id})`; 36 + : `${_private}.exec(${_state}, ${ast.expression.id})`; 36 37 37 38 return js` 38 39 if (${_match} = ${expression}) {
+38 -28
src/core.js
··· 1 - import { astRoot, _exec as execId } from './codegen'; 1 + import { astRoot, _private as privateId } 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 _pattern = (input) => { 7 - if (typeof input === 'function') return input; 8 - const source = typeof input !== 'string' ? input.source : input; 9 - return isStickySupported 10 - ? new RegExp(source, 'y') 11 - : new RegExp(source + '|()', 'g'); 12 - }; 6 + export const __private = { 7 + pattern(input) { 8 + if (typeof input === 'function') return input; 9 + const source = typeof input !== 'string' ? input.source : input; 10 + return isStickySupported 11 + ? new RegExp(source, 'y') 12 + : new RegExp(source + '|()', 'g'); 13 + }, 13 14 14 - export const _exec = (state, pattern) => { 15 - let match; 16 - 17 - if (typeof pattern === 'function') { 18 - if (!pattern.length) pattern = pattern(); 19 - return pattern(state); 20 - } 15 + exec(state, pattern) { 16 + let match; 21 17 22 - const input = state.quasis[state.x]; 23 - if (input && (pattern.lastIndex = state.y) < input.length) { 24 - if (isStickySupported) { 25 - if (pattern.test(input)) match = input.slice(state.y, pattern.lastIndex); 26 - } else { 27 - match = pattern.exec(input)[0] || match; 18 + if (typeof pattern === 'function') { 19 + if (!pattern.length) pattern = pattern(); 20 + return pattern(state); 28 21 } 29 22 30 - state.y = pattern.lastIndex; 31 - } 23 + const input = state.quasis[state.x]; 24 + if (input && (pattern.lastIndex = state.y) < input.length) { 25 + if (isStickySupported) { 26 + if (pattern.test(input)) 27 + match = input.slice(state.y, pattern.lastIndex); 28 + } else { 29 + match = pattern.exec(input)[0] || match; 30 + } 32 31 33 - return match; 32 + state.y = pattern.lastIndex; 33 + } 34 + 35 + return match; 36 + }, 34 37 }; 35 38 36 39 export const interpolation = (predicate) => (state) => { ··· 48 51 return match; 49 52 }; 50 53 51 - export const parse = (pattern) => (quasis, ...expressions) => { 54 + export const parse = (matcher) => (quasis, ...expressions) => { 52 55 if (typeof quasis === 'string') quasis = [quasis]; 53 56 const state = { quasis, expressions, x: 0, y: 0 }; 54 - return pattern(state); 57 + return matcher(state); 55 58 }; 56 59 57 60 export const match = (name, transform) => (quasis, ...expressions) => { ··· 64 67 ); 65 68 66 69 const makeMatcher = new Function( 67 - execId + ',_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','), 70 + privateId + 71 + ',_n,_t,' + 72 + expressions.map((_expression, i) => `_${i}`).join(','), 68 73 'return ' + astRoot(ast, '_n', transform ? '_t' : null) 69 74 ); 70 75 71 - return makeMatcher(_exec, name, transform, ...expressions.map(_pattern)); 76 + return makeMatcher( 77 + __private, 78 + name, 79 + transform, 80 + ...expressions.map(__private.pattern) 81 + ); 72 82 };