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

Configure Feed

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

Implement initial tagged template parsing support

+95 -77
+33 -33
src/babel/__snapshots__/plugin.test.js.snap
··· 9 9 _node_expression2 = (0, _reghex._pattern)(2); 10 10 11 11 const node = function (state) { 12 - var idx1 = state.index; 12 + var y1 = state.y; 13 13 var node = []; 14 14 var x; 15 15 16 16 if (x = (0, _reghex._exec)(state, _node_expression)) { 17 17 node.push(x); 18 18 } else { 19 - state.index = idx1; 19 + state.y = y1; 20 20 return; 21 21 } 22 22 23 23 if (x = (0, _reghex._exec)(state, _node_expression2)) { 24 24 node.push(x); 25 25 } else { 26 - state.index = idx1; 26 + state.y = y1; 27 27 return; 28 28 } 29 29 ··· 43 43 var _inner_expression = _pattern(/inner/); 44 44 45 45 const inner = function (state) { 46 - var idx1 = state.index; 46 + var y1 = state.y; 47 47 var node = []; 48 48 var x; 49 49 50 50 if (x = _exec(state, _inner_expression)) { 51 51 node.push(x); 52 52 } else { 53 - state.index = idx1; 53 + state.y = y1; 54 54 return; 55 55 } 56 56 ··· 59 59 }; 60 60 61 61 const node = function (state) { 62 - var idx1 = state.index; 62 + var y1 = state.y; 63 63 var node = []; 64 64 var x; 65 65 66 66 if (x = inner(state)) { 67 67 node.push(x); 68 68 } else { 69 - state.index = idx1; 69 + state.y = y1; 70 70 return; 71 71 } 72 72 ··· 83 83 _node_expression3 = _pattern(3); 84 84 85 85 const node = function (state) { 86 - var idx1 = state.index; 86 + var y1 = state.y; 87 87 var node = []; 88 88 var x; 89 89 90 90 if (x = _exec(state, _node_expression)) { 91 91 node.push(x); 92 92 } else { 93 - state.index = idx1; 93 + state.y = y1; 94 94 return; 95 95 } 96 96 ··· 98 98 99 99 alt_3: { 100 100 block_3: { 101 - var idx3 = state.index; 101 + var y3 = state.y; 102 102 103 103 if (x = _exec(state, _node_expression2)) { 104 104 node.push(x); 105 105 } else { 106 - state.index = idx3; 106 + state.y = y3; 107 107 node.length = ln2; 108 108 break block_3; 109 109 } ··· 112 112 } 113 113 114 114 loop_3: for (var j3 = 0; 1; j3++) { 115 - var idx3 = state.index; 115 + var y3 = state.y; 116 116 117 117 if (!_exec(state, _node_expression3)) { 118 118 if (j3) { 119 - state.index = idx3; 119 + state.y = y3; 120 120 break loop_3; 121 121 } else {} 122 122 123 - state.index = idx1; 123 + state.y = y1; 124 124 node.length = ln2; 125 125 return; 126 126 } ··· 136 136 "import { match, tag, _exec, _pattern } from 'reghex'; 137 137 138 138 const inner = function (state) { 139 - var idx1 = state.index; 139 + var y1 = state.y; 140 140 var node = []; 141 141 var x; 142 142 143 143 if (x = node(state)) { 144 144 node.push(x); 145 145 } else { 146 - state.index = idx1; 146 + state.y = y1; 147 147 return; 148 148 } 149 149 ··· 152 152 }; 153 153 154 154 const node = function (state) { 155 - var idx1 = state.index; 155 + var y1 = state.y; 156 156 var node = []; 157 157 var x; 158 158 159 159 if (x = inner(state)) { 160 160 node.push(x); 161 161 } else { 162 - state.index = idx1; 162 + state.y = y1; 163 163 return; 164 164 } 165 165 ··· 178 178 _node_expression5 = _pattern(5); 179 179 180 180 const node = function (state) { 181 - var idx1 = state.index; 181 + var y1 = state.y; 182 182 var node = []; 183 183 var x; 184 184 185 185 alt_2: { 186 186 block_2: { 187 - var idx2 = state.index; 187 + var y2 = state.y; 188 188 189 189 loop_2: for (var j2 = 0; 1; j2++) { 190 - var idx2 = state.index; 190 + var y2 = state.y; 191 191 192 192 if (x = _exec(state, _node_expression)) { 193 193 node.push(x); 194 194 } else { 195 195 if (j2) { 196 - state.index = idx2; 196 + state.y = y2; 197 197 break loop_2; 198 198 } else {} 199 199 200 - state.index = idx2; 200 + state.y = y2; 201 201 break block_2; 202 202 } 203 203 } ··· 206 206 } 207 207 208 208 loop_2: for (var j2 = 0; 1; j2++) { 209 - var idx2 = state.index; 209 + var y2 = state.y; 210 210 211 211 if (x = _exec(state, _node_expression2)) { 212 212 node.push(x); 213 213 } else { 214 214 if (j2) { 215 - state.index = idx2; 215 + state.y = y2; 216 216 break loop_2; 217 217 } else {} 218 218 219 - state.index = idx1; 219 + state.y = y1; 220 220 return; 221 221 } 222 222 } 223 223 224 224 loop_2: for (;;) { 225 - var idx2 = state.index; 225 + var y2 = state.y; 226 226 var ln2 = node.length; 227 227 228 228 if (x = _exec(state, _node_expression3)) { 229 229 node.push(x); 230 230 } else { 231 - state.index = idx2; 231 + state.y = y2; 232 232 node.length = ln2; 233 233 break loop_2; 234 234 } 235 235 236 - var idx4 = state.index; 236 + var y4 = state.y; 237 237 238 238 if (x = _exec(state, _node_expression4)) { 239 239 node.push(x); 240 240 } else { 241 - state.index = idx4; 241 + state.y = y4; 242 242 } 243 243 244 244 if (x = _exec(state, _node_expression5)) { 245 245 node.push(x); 246 246 } else { 247 - state.index = idx2; 247 + state.y = y2; 248 248 node.length = ln2; 249 249 break loop_2; 250 250 } ··· 262 262 var _inner_transform = x => x; 263 263 264 264 const first = function (state) { 265 - var idx1 = state.index; 265 + var y1 = state.y; 266 266 var node = []; 267 267 var x; 268 268 node.tag = 'inner'; ··· 272 272 const transform = x => x; 273 273 274 274 const second = function (state) { 275 - var idx1 = state.index; 275 + var y1 = state.y; 276 276 var node = []; 277 277 var x; 278 278 node.tag = 'node';
+27 -27
src/codegen.js
··· 3 3 const _node = 'node'; 4 4 const _match = 'x'; 5 5 6 + let _interpolations = 0; 7 + 6 8 function js(/* arguments */) { 7 9 let body = arguments[0][0]; 8 10 for (let i = 1; i < arguments.length; i++) ··· 17 19 }; 18 20 19 21 const assignIndex = (depth) => 20 - depth ? js`var idx${depth} = ${_state}.index;` : ''; 22 + js`var y${depth} = ${_state}.y` + 23 + (_interpolations ? js`, var x${depth} = ${_state}.x;` : ';'); 21 24 22 25 const restoreIndex = (depth) => 23 - depth ? js`${_state}.index = idx${depth};` : ''; 24 - 25 - const abortOnCondition = (condition, hooks) => js` 26 - if (${condition}) { 27 - ${restoreIndex(opts.index)} 28 - ${opts.abort} 29 - } else { 30 - ${opts.onAbort} 31 - } 32 - `; 26 + js`${_state}.y = y${depth}` + 27 + (_interpolations ? js`, ${_state}.x = x${depth};` : ';'); 33 28 34 29 const astExpression = (ast, depth, opts) => { 35 30 const restoreLength = ··· 200 195 `; 201 196 }; 202 197 203 - const astRoot = (ast, name, transform) => js` 204 - (function (${_state}) { 205 - ${assignIndex(1)} 206 - var ${_node} = []; 207 - var ${_match}; 198 + const astRoot = (ast, name, transform, interpolations) => { 199 + _interpolations = interpolations; 200 + 201 + return js` 202 + (function (${_state}) { 203 + ${assignIndex(1)} 204 + var ${_node} = []; 205 + var ${_match}; 208 206 209 - ${astSequence(ast, 2, { 210 - index: 1, 211 - length: 0, 212 - onAbort: '', 213 - abort: js`return;`, 214 - capture: true, 215 - })} 207 + ${astSequence(ast, 2, { 208 + interpolations, 209 + index: 1, 210 + length: 0, 211 + onAbort: '', 212 + abort: js`return;`, 213 + capture: true, 214 + })} 216 215 217 - ${_node}.tag = ${name}; 218 - return ${transform ? js`(${transform})(${_node})` : _node}; 219 - }) 220 - `; 216 + ${_node}.tag = ${name}; 217 + return ${transform ? js`(${transform})(${_node})` : _node}; 218 + }) 219 + `; 220 + }; 221 221 222 222 export { astRoot };
+32 -14
src/core.js
··· 19 19 return pattern(state); 20 20 } 21 21 22 - pattern.lastIndex = state.index; 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; 28 + } 29 + } 30 + 31 + state.y = pattern.lastIndex; 32 + return match; 33 + }; 23 34 24 - if (isStickySupported) { 25 - if (pattern.test(state.input)) 26 - match = state.input.slice(state.index, pattern.lastIndex); 27 - } else { 28 - match = pattern.exec(state.input)[0] || match; 35 + export const interpolation = (state) => { 36 + let match; 37 + 38 + const input = state.quasis[state.x]; 39 + if (!input || state.y >= input.length) { 40 + state.y = 0; 41 + match = state.expressions[state.x++] || match; 29 42 } 30 43 31 - state.index = pattern.lastIndex; 32 44 return match; 33 45 }; 34 46 35 - export const parse = (pattern) => (input) => { 36 - const state = { input, index: 0 }; 47 + export const parse = (pattern) => (quasis, ...expressions) => { 48 + if (typeof quasis === 'string') quasis = [quasis]; 49 + const state = { quasis, expressions, x: 0, y: 0 }; 37 50 return pattern(state); 38 51 }; 39 52 40 53 export const match = (name, transform) => (quasis, ...expressions) => { 54 + let interpolations = 0; 55 + 41 56 const ast = parseDSL( 42 57 quasis, 43 - expressions.map((expression, i) => ({ 44 - fn: typeof expression === 'function' && expression.length, 45 - id: `_${i}`, 46 - })) 58 + expressions.map((expression, i) => { 59 + if (expression === interpolation) interpolations++; 60 + return { 61 + fn: typeof expression === 'function' && expression.length, 62 + id: `_${i}`, 63 + }; 64 + }) 47 65 ); 48 66 49 67 const makeMatcher = new Function( 50 68 execId + ',_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','), 51 - 'return ' + astRoot(ast, '_n', transform ? '_t' : null) 69 + 'return ' + astRoot(ast, '_n', transform ? '_t' : null, interpolations) 52 70 ); 53 71 54 72 return makeMatcher(_exec, name, transform, ...expressions.map(_pattern));
+3 -3
src/core.test.js
··· 1 1 import { match } from './core'; 2 2 3 3 const expectToParse = (node, input, result, lastIndex = 0) => { 4 - const state = { input, index: 0 }; 4 + const state = { quasis: [input], expressions: [], x: 0, y: 0 }; 5 5 if (result) result.tag = 'node'; 6 6 expect(node(state)).toEqual(result); 7 7 8 8 // NOTE: After parsing we expect the current index to exactly match the 9 9 // sum amount of matched characters 10 10 if (result === undefined) { 11 - expect(state.index).toBe(0); 11 + expect(state.y).toBe(0); 12 12 } else { 13 13 const index = lastIndex || result.reduce((acc, x) => acc + x.length, 0); 14 - expect(state.index).toBe(index); 14 + expect(state.y).toBe(index); 15 15 } 16 16 }; 17 17