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

Configure Feed

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

Switch to non-global state object

+98 -131
+52 -69
src/babel/__snapshots__/plugin.test.js.snap
··· 8 8 var _node_expression = (0, _reghex._pattern)(1), 9 9 _node_expression2 = (0, _reghex._pattern)(2); 10 10 11 - const node = function _node() { 11 + const node = function _node(state) { 12 12 var match, 13 - last_index = (0, _reghex._getLastIndex)(), 13 + last_index = state.index, 14 14 node = (0, _reghex.tag)([], 'node'); 15 15 16 - if (match = (0, _reghex._execPattern)(_node_expression)) { 16 + if (match = (0, _reghex._exec)(state, _node_expression)) { 17 17 node.push(match); 18 18 } else { 19 - (0, _reghex._setLastIndex)(last_index); 19 + state.index = last_index; 20 20 return; 21 21 } 22 22 23 - if (match = (0, _reghex._execPattern)(_node_expression2)) { 23 + if (match = (0, _reghex._exec)(state, _node_expression2)) { 24 24 node.push(match); 25 25 } else { 26 - (0, _reghex._setLastIndex)(last_index); 26 + state.index = last_index; 27 27 return; 28 28 } 29 29 ··· 32 32 `; 33 33 34 34 exports[`works with local recursion 1`] = ` 35 - "import { tag, _getLastIndex, _setLastIndex, _execPattern, _pattern } from 'reghex'; 35 + "import { tag, _exec, _pattern } from 'reghex'; 36 36 37 37 var _inner_expression = _pattern(/inner/); 38 38 39 - const inner = function _inner() { 39 + const inner = function _inner(state) { 40 40 var match, 41 - last_index = _getLastIndex(), 41 + last_index = state.index, 42 42 node = tag([], 'inner'); 43 43 44 - if (match = _execPattern(_inner_expression)) { 44 + if (match = _exec(state, _inner_expression)) { 45 45 node.push(match); 46 46 } else { 47 - _setLastIndex(last_index); 48 - 47 + state.index = last_index; 49 48 return; 50 49 } 51 50 52 51 return node; 53 52 }; 54 53 55 - const node = function _node() { 54 + const node = function _node(state) { 56 55 var match, 57 - last_index = _getLastIndex(), 56 + last_index = state.index, 58 57 node = tag([], 'node'); 59 58 60 - if (match = inner()) { 59 + if (match = inner(state)) { 61 60 node.push(match); 62 61 } else { 63 - _setLastIndex(last_index); 64 - 62 + state.index = last_index; 65 63 return; 66 64 } 67 65 ··· 70 68 `; 71 69 72 70 exports[`works with non-capturing groups 1`] = ` 73 - "import { _getLastIndex, _setLastIndex, _execPattern, _pattern, tag as _tag } from 'reghex'; 71 + "import { _exec, _pattern, tag as _tag } from 'reghex'; 74 72 75 73 var _node_expression = _pattern(1), 76 74 _node_expression2 = _pattern(2), 77 75 _node_expression3 = _pattern(3); 78 76 79 - const node = function _node() { 77 + const node = function _node(state) { 80 78 var match, 81 - last_index = _getLastIndex(), 79 + last_index = state.index, 82 80 node = _tag([], 'node'); 83 81 84 - if (match = _execPattern(_node_expression)) { 82 + if (match = _exec(state, _node_expression)) { 85 83 node.push(match); 86 84 } else { 87 - _setLastIndex(last_index); 88 - 85 + state.index = last_index; 89 86 return; 90 87 } 91 88 ··· 93 90 94 91 alternation_1: { 95 92 block_1: { 96 - var index_1 = _getLastIndex(); 93 + var index_1 = state.index; 97 94 98 - if (match = _execPattern(_node_expression2)) { 95 + if (match = _exec(state, _node_expression2)) { 99 96 node.push(match); 100 97 } else { 101 98 node.length = length_0; 102 - 103 - _setLastIndex(index_1); 104 - 99 + state.index = index_1; 105 100 break block_1; 106 101 } 107 102 ··· 109 104 } 110 105 111 106 loop_1: for (var iter_1 = 0; true; iter_1++) { 112 - var index_1 = _getLastIndex(); 107 + var index_1 = state.index; 113 108 114 - if (!_execPattern(_node_expression3)) { 109 + if (!_exec(state, _node_expression3)) { 115 110 if (iter_1) { 116 - _setLastIndex(index_1); 117 - 111 + state.index = index_1; 118 112 break loop_1; 119 113 } 120 114 121 115 node.length = length_0; 122 - 123 - _setLastIndex(last_index); 124 - 116 + state.index = last_index; 125 117 return; 126 118 } 127 119 } ··· 132 124 `; 133 125 134 126 exports[`works with standard features 1`] = ` 135 - "import { _getLastIndex, _setLastIndex, _execPattern, _pattern, tag as _tag } from \\"reghex\\"; 127 + "import { _exec, _pattern, tag as _tag } from \\"reghex\\"; 136 128 137 129 var _node_expression = _pattern(1), 138 130 _node_expression2 = _pattern(2), ··· 140 132 _node_expression4 = _pattern(4), 141 133 _node_expression5 = _pattern(5); 142 134 143 - const node = function _node() { 135 + const node = function _node(state) { 144 136 var match, 145 - last_index = _getLastIndex(), 137 + last_index = state.index, 146 138 node = _tag([], 'node'); 147 139 148 140 block_0: { 149 - var index_0 = _getLastIndex(); 141 + var index_0 = state.index; 150 142 151 143 loop_0: for (var iter_0 = 0; true; iter_0++) { 152 - var index_0 = _getLastIndex(); 144 + var index_0 = state.index; 153 145 154 - if (match = _execPattern(_node_expression)) { 146 + if (match = _exec(state, _node_expression)) { 155 147 node.push(match); 156 148 } else { 157 149 if (iter_0) { 158 - _setLastIndex(index_0); 159 - 150 + state.index = index_0; 160 151 break loop_0; 161 152 } 162 153 163 - _setLastIndex(index_0); 164 - 154 + state.index = index_0; 165 155 break block_0; 166 156 } 167 157 } ··· 170 160 } 171 161 172 162 loop_0: for (var iter_0 = 0; true; iter_0++) { 173 - var index_0 = _getLastIndex(); 163 + var index_0 = state.index; 174 164 175 - if (match = _execPattern(_node_expression2)) { 165 + if (match = _exec(state, _node_expression2)) { 176 166 node.push(match); 177 167 } else { 178 168 if (iter_0) { 179 - _setLastIndex(index_0); 180 - 169 + state.index = index_0; 181 170 break loop_0; 182 171 } 183 172 184 - _setLastIndex(last_index); 185 - 173 + state.index = last_index; 186 174 return; 187 175 } 188 176 } 189 177 190 178 loop_0: while (true) { 191 - var index_0 = _getLastIndex(); 192 - 179 + var index_0 = state.index; 193 180 var length_0 = node.length; 194 181 195 - if (match = _execPattern(_node_expression3)) { 182 + if (match = _exec(state, _node_expression3)) { 196 183 node.push(match); 197 184 } else { 198 185 node.length = length_0; 199 - 200 - _setLastIndex(index_0); 201 - 186 + state.index = index_0; 202 187 break loop_0; 203 188 } 204 189 205 - var index_2 = _getLastIndex(); 190 + var index_2 = state.index; 206 191 207 - if (match = _execPattern(_node_expression4)) { 192 + if (match = _exec(state, _node_expression4)) { 208 193 node.push(match); 209 194 } else { 210 - _setLastIndex(index_2); 195 + state.index = index_2; 211 196 } 212 197 213 - if (match = _execPattern(_node_expression5)) { 198 + if (match = _exec(state, _node_expression5)) { 214 199 node.push(match); 215 200 } else { 216 201 node.length = length_0; 217 - 218 - _setLastIndex(index_0); 219 - 202 + state.index = index_0; 220 203 break loop_0; 221 204 } 222 205 } ··· 226 209 `; 227 210 228 211 exports[`works with transform functions 1`] = ` 229 - "import { _getLastIndex, _setLastIndex, _execPattern, _pattern, tag as _tag } from 'reghex'; 212 + "import { _exec, _pattern, tag as _tag } from 'reghex'; 230 213 231 214 var _inner_transform = x => x; 232 215 233 - const first = function _inner() { 216 + const first = function _inner(state) { 234 217 var match, 235 - last_index = _getLastIndex(), 218 + last_index = state.index, 236 219 node = _tag([], 'inner'); 237 220 238 221 return _inner_transform(node); ··· 240 223 241 224 const transform = x => x; 242 225 243 - const second = function _node() { 226 + const second = function _node(state) { 244 227 var match, 245 - last_index = _getLastIndex(), 228 + last_index = state.index, 246 229 node = _tag([], 'node'); 247 230 248 231 return transform(node);
+4 -3
src/babel/__tests__/suite.js
··· 37 37 }; 38 38 39 39 const expectToParse = (node, input, result, lastIndex = 0) => { 40 - expect(reghex.parse(node)(input)).toEqual( 40 + const state = { input, index: 0 }; 41 + expect(node(state)).toEqual( 41 42 result === undefined ? result : reghex.tag(result, 'node') 42 43 ); 43 44 44 45 // NOTE: After parsing we expect the current index to exactly match the 45 46 // sum amount of matched characters 46 47 if (result === undefined) { 47 - expect(reghex._getLastIndex()).toBe(0); 48 + expect(state.index).toBe(0); 48 49 } else { 49 50 const index = lastIndex || result.reduce((acc, x) => acc + x.length, 0); 50 - expect(reghex._getLastIndex()).toBe(index); 51 + expect(state.index).toBe(index); 51 52 } 52 53 }; 53 54
+18 -11
src/babel/generator.js
··· 6 6 t = _t; 7 7 } 8 8 9 - /** var id = getLastIndex(); */ 9 + /** var id = state.index; */ 10 10 class AssignIndexNode { 11 11 constructor(id) { 12 12 this.id = id; 13 13 } 14 14 15 15 statement() { 16 - const call = t.callExpression(ids.getLastIndex, []); 17 - return t.variableDeclaration('var', [t.variableDeclarator(this.id, call)]); 16 + const member = t.memberExpression(ids.state, t.identifier('index')); 17 + return t.variableDeclaration('var', [ 18 + t.variableDeclarator(this.id, member), 19 + ]); 18 20 } 19 21 } 20 22 21 - /** setLastIndex(id); */ 23 + /** state.index = id; */ 22 24 class RestoreIndexNode { 23 25 constructor(id) { 24 26 this.id = id; 25 27 } 26 28 27 29 statement() { 28 - const expression = t.callExpression(ids.setLastIndex, [this.id]); 30 + const expression = t.assignmentExpression( 31 + '=', 32 + t.memberExpression(ids.state, t.identifier('index')), 33 + this.id 34 + ); 35 + 29 36 return t.expressionStatement(expression); 30 37 } 31 38 } ··· 224 231 const loopId = t.identifier(`loop_${this.depth}`); 225 232 const iterId = t.identifier(`iter_${this.depth}`); 226 233 const indexId = t.identifier(`index_${this.depth}`); 227 - const getLastIndex = t.callExpression(ids.getLastIndex, []); 234 + const lastIndex = t.memberExpression(ids.state, t.identifier('index')); 228 235 229 236 let statements; 230 237 if (quantifier && !quantifier.singular && quantifier.required) { ··· 239 246 t.updateExpression('++', iterId), 240 247 t.blockStatement([ 241 248 t.variableDeclaration('var', [ 242 - t.variableDeclarator(indexId, getLastIndex), 249 + t.variableDeclarator(indexId, lastIndex), 243 250 ]), 244 251 ...this.childNode.statements(), 245 252 ]) ··· 254 261 t.booleanLiteral(true), 255 262 t.blockStatement([ 256 263 t.variableDeclaration('var', [ 257 - t.variableDeclarator(indexId, getLastIndex), 264 + t.variableDeclarator(indexId, lastIndex), 258 265 ]), 259 266 ...this.childNode.statements(), 260 267 ]) ··· 264 271 } else if (quantifier && !quantifier.required) { 265 272 statements = [ 266 273 t.variableDeclaration('var', [ 267 - t.variableDeclarator(indexId, getLastIndex), 274 + t.variableDeclarator(indexId, lastIndex), 268 275 ]), 269 276 ...this.childNode.statements(), 270 277 ]; ··· 393 400 394 401 statements() { 395 402 const indexId = t.identifier('last_index'); 396 - const getLastIndex = t.callExpression(ids.getLastIndex, []); 403 + const lastIndex = t.memberExpression(ids.state, t.identifier('index')); 397 404 398 405 return [ 399 406 t.variableDeclaration('var', [ 400 407 t.variableDeclarator(ids.match), 401 - t.variableDeclarator(indexId, getLastIndex), 408 + t.variableDeclarator(indexId, lastIndex), 402 409 t.variableDeclarator( 403 410 ids.node, 404 411 t.callExpression(ids.tag, [t.arrayExpression(), this.nameNode])
+5 -11
src/babel/sharedIds.js
··· 1 1 export class SharedIds { 2 2 constructor(t) { 3 3 this.t = t; 4 - this.getLastIndexId = t.identifier('_getLastIndex'); 5 - this.setLastIndexId = t.identifier('_setLastIndex'); 6 - this.execPatternId = t.identifier('_execPattern'); 4 + this.execId = t.identifier('_exec'); 7 5 this.patternId = t.identifier('_pattern'); 8 6 this.tagId = t.identifier('tag'); 9 7 } ··· 16 14 return this.t.identifier('match'); 17 15 } 18 16 19 - get getLastIndex() { 20 - return this.t.identifier(this.getLastIndexId.name); 21 - } 22 - 23 - get setLastIndex() { 24 - return this.t.identifier(this.setLastIndexId.name); 17 + get state() { 18 + return this.t.identifier('state'); 25 19 } 26 20 27 - get execPattern() { 28 - return this.t.identifier(this.execPatternId.name); 21 + get exec() { 22 + return this.t.identifier(this.execId.name); 29 23 } 30 24 31 25 get pattern() {
+11 -17
src/babel/transform.js
··· 31 31 32 32 path.node.specifiers.push( 33 33 t.importSpecifier( 34 - (ids.getLastIndexId = path.scope.generateUidIdentifier( 35 - 'getLastIndex' 36 - )), 37 - t.identifier('_getLastIndex') 38 - ), 39 - t.importSpecifier( 40 - (ids.setLastIndexId = path.scope.generateUidIdentifier( 41 - 'setLastIndex' 42 - )), 43 - t.identifier('_setLastIndex') 44 - ), 45 - t.importSpecifier( 46 - (ids.execPatternId = path.scope.generateUidIdentifier('execPattern')), 47 - t.identifier('_execPattern') 34 + (ids.execId = path.scope.generateUidIdentifier('exec')), 35 + t.identifier('_exec') 48 36 ), 49 37 t.importSpecifier( 50 38 (ids.patternId = path.scope.generateUidIdentifier('pattern')), ··· 192 180 const binding = path.scope.getBinding(id.name); 193 181 if (binding && t.isVariableDeclarator(binding.path.node)) { 194 182 const matchPath = binding.path.get('init'); 195 - if (this.isMatch(matchPath)) return t.callExpression(id, []); 183 + if (this.isMatch(matchPath)) { 184 + return t.callExpression(id, [ids.state]); 185 + } 196 186 } 197 187 198 - return t.callExpression(ids.execPattern, [id]); 188 + return t.callExpression(ids.exec, [ids.state, id]); 199 189 }); 200 190 201 191 // Hoist transform argument if necessary ··· 212 202 const generator = new RootNode(ast, nameNode, transformNode); 213 203 const body = t.blockStatement(generator.statements()); 214 204 const matchFunctionId = path.scope.generateUidIdentifier(matchName); 215 - const matchFunction = t.functionExpression(matchFunctionId, [], body); 205 + const matchFunction = t.functionExpression( 206 + matchFunctionId, 207 + [ids.state], 208 + body 209 + ); 216 210 path.replaceWith(matchFunction); 217 211 }, 218 212 };
+8 -20
src/core.js
··· 1 1 const isStickySupported = typeof /./g.sticky === 'boolean'; 2 2 3 - let state$input = ''; 4 - let state$lastIndex = 0; 5 - 6 - export const _getLastIndex = () => { 7 - return state$lastIndex; 8 - }; 9 - 10 - export const _setLastIndex = (index) => { 11 - state$lastIndex = index; 12 - }; 13 - 14 3 export const _pattern = (input) => { 15 4 if (typeof input === 'function') return input; 16 5 ··· 20 9 : new RegExp(`^(?:${source})`, 'g'); 21 10 }; 22 11 23 - export const _execPattern = (pattern) => { 12 + export const _exec = (state, pattern) => { 24 13 if (typeof pattern === 'function') return pattern(); 25 14 26 15 let match; 27 16 if (isStickySupported) { 28 - pattern.lastIndex = state$lastIndex; 29 - match = pattern.exec(state$input); 30 - state$lastIndex = pattern.lastIndex; 17 + pattern.lastIndex = state.index; 18 + match = pattern.exec(state.input); 19 + state.index = pattern.lastIndex; 31 20 } else { 32 21 pattern.lastIndex = 0; 33 - match = pattern.exec(state$input.slice(state$lastIndex)); 34 - state$lastIndex += pattern.lastIndex; 22 + match = pattern.exec(state.input.slice(state.input)); 23 + state.index += pattern.lastIndex; 35 24 } 36 25 37 26 return match && match[0]; ··· 43 32 }; 44 33 45 34 export const parse = (pattern) => (input) => { 46 - state$input = input; 47 - state$lastIndex = 0; 48 - return pattern(); 35 + const state = { input, index: 0 }; 36 + return pattern(state); 49 37 }; 50 38 51 39 export const match = (_name) => {