Mirror: The magical sticky regex-based parser generator 🧙

Make node names optional

Changed files
+152 -34
src
+116 -11
src/babel/__snapshots__/plugin.test.js.snap
··· 30 30 return; 31 31 } 32 32 33 - node.tag = 'a'; 33 + if ('a') node.tag = 'a'; 34 34 return node; 35 35 }; 36 36 ··· 58 58 return; 59 59 } 60 60 61 - node.tag = 'b'; 61 + if ('b') node.tag = 'b'; 62 62 return node; 63 63 };" 64 64 `; ··· 93 93 return; 94 94 } 95 95 96 - node.tag = 'node'; 96 + if ('node') node.tag = 'node'; 97 97 return node; 98 98 };" 99 99 `; ··· 122 122 return; 123 123 } 124 124 125 - node.tag = 'inner'; 125 + if ('inner') node.tag = 'inner'; 126 126 return node; 127 127 }; 128 128 ··· 140 140 return; 141 141 } 142 142 143 - node.tag = 'node'; 143 + if ('node') node.tag = 'node'; 144 + return node; 145 + };" 146 + `; 147 + 148 + exports[`works with nameless matchers 1`] = ` 149 + "import { match, __pattern as _pattern } from \\"reghex\\"; 150 + 151 + var _objectObject_expression = _pattern(1), 152 + _objectObject_expression2 = _pattern(2), 153 + _objectObject_expression3 = _pattern(3), 154 + _objectObject_expression4 = _pattern(4), 155 + _objectObject_expression5 = _pattern(5); 156 + 157 + const node = function (state) { 158 + var y1 = state.y, 159 + x1 = state.x; 160 + var node = []; 161 + var x; 162 + 163 + alt_2: { 164 + block_2: { 165 + var y2 = state.y, 166 + x2 = state.x; 167 + 168 + if ((x = _objectObject_expression(state)) != null) { 169 + node.push(x); 170 + } else { 171 + state.y = y2; 172 + state.x = x2; 173 + break block_2; 174 + } 175 + 176 + group_2: for (;;) { 177 + var y2 = state.y, 178 + x2 = state.x; 179 + 180 + if ((x = _objectObject_expression(state)) != null) { 181 + node.push(x); 182 + } else { 183 + state.y = y2; 184 + state.x = x2; 185 + break group_2; 186 + } 187 + } 188 + 189 + break alt_2; 190 + } 191 + 192 + if ((x = _objectObject_expression2(state)) != null) { 193 + node.push(x); 194 + } else { 195 + state.y = y1; 196 + state.x = x1; 197 + return; 198 + } 199 + 200 + group_2: for (;;) { 201 + var y2 = state.y, 202 + x2 = state.x; 203 + 204 + if ((x = _objectObject_expression2(state)) != null) { 205 + node.push(x); 206 + } else { 207 + state.y = y2; 208 + state.x = x2; 209 + break group_2; 210 + } 211 + } 212 + 213 + group_2: for (;;) { 214 + var y2 = state.y, 215 + x2 = state.x; 216 + var ln2 = node.length; 217 + 218 + if ((x = _objectObject_expression3(state)) != null) { 219 + node.push(x); 220 + } else { 221 + state.y = y2; 222 + state.x = x2; 223 + node.length = ln2; 224 + break group_2; 225 + } 226 + 227 + var y4 = state.y, 228 + x4 = state.x; 229 + 230 + if ((x = _objectObject_expression4(state)) != null) { 231 + node.push(x); 232 + } else { 233 + state.y = y4; 234 + state.x = x4; 235 + } 236 + 237 + if ((x = _objectObject_expression5(state)) != null) { 238 + node.push(x); 239 + } else { 240 + state.y = y2; 241 + state.x = x2; 242 + node.length = ln2; 243 + break group_2; 244 + } 245 + } 246 + } 247 + 248 + if (null) node.tag = null; 144 249 return node; 145 250 };" 146 251 `; ··· 204 309 } 205 310 } 206 311 207 - node.tag = 'node'; 312 + if ('node') node.tag = 'node'; 208 313 return node; 209 314 };" 210 315 `; ··· 226 331 return; 227 332 } 228 333 229 - node.tag = 'inner'; 334 + if ('inner') node.tag = 'inner'; 230 335 return node; 231 336 }; 232 337 ··· 244 349 return; 245 350 } 246 351 247 - node.tag = 'node'; 352 + if ('node') node.tag = 'node'; 248 353 return node; 249 354 };" 250 355 `; ··· 349 454 } 350 455 } 351 456 352 - node.tag = 'node'; 457 + if ('node') node.tag = 'node'; 353 458 return node; 354 459 };" 355 460 `; ··· 364 469 x1 = state.x; 365 470 var node = []; 366 471 var x; 367 - node.tag = 'inner'; 472 + if ('inner') node.tag = 'inner'; 368 473 return _inner_transform(node); 369 474 }; 370 475 ··· 375 480 x1 = state.x; 376 481 var node = []; 377 482 var x; 378 - node.tag = 'node'; 483 + if ('node') node.tag = 'node'; 379 484 return transform(node); 380 485 };" 381 486 `;
+15
src/babel/plugin.test.js
··· 16 16 ).toMatchSnapshot(); 17 17 }); 18 18 19 + it('works with nameless matchers', () => { 20 + const code = ` 21 + import { match } from 'reghex/macro'; 22 + 23 + const node = match()\` 24 + \${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )* 25 + \`; 26 + `; 27 + 28 + expect( 29 + transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] }) 30 + .code 31 + ).toMatchSnapshot(); 32 + }); 33 + 19 34 it('works while only minifying', () => { 20 35 const code = ` 21 36 import { match } from 'reghex/macro';
+12 -22
src/babel/transform.js
··· 83 83 getMatchName(path) { 84 84 t.assertTaggedTemplateExpression(path.node); 85 85 const nameArgumentPath = path.get('tag.arguments.0'); 86 - const { confident, value } = nameArgumentPath.evaluate(); 87 - if (!confident && t.isIdentifier(nameArgumentPath.node)) { 88 - return nameArgumentPath.node.name; 89 - } else if (confident && typeof value === 'string') { 90 - return value; 91 - } else { 92 - return path.scope.generateUidIdentifierBasedOnNode(path.node); 86 + if (nameArgumentPath) { 87 + const { confident, value } = nameArgumentPath.evaluate(); 88 + if (!confident && t.isIdentifier(nameArgumentPath.node)) { 89 + return nameArgumentPath.node.name; 90 + } else if (confident && typeof value === 'string') { 91 + return value; 92 + } 93 93 } 94 + 95 + return path.scope.generateUidIdentifierBasedOnNode(path.node); 94 96 }, 95 97 96 98 /** Given a match, hoists its expressions in front of the match's statement */ ··· 194 196 }, 195 197 196 198 minifyMatch(path) { 197 - if (!path.node.tag.arguments.length) { 198 - throw path 199 - .get('tag') 200 - .buildCodeFrameError( 201 - 'match() must at least be called with a node name' 202 - ); 203 - } 204 - 205 199 const quasis = path.node.quasi.quasis.map((x) => 206 200 t.stringLiteral(x.value.cooked.replace(/\s*/g, '')) 207 201 ); ··· 217 211 }, 218 212 219 213 transformMatch(path) { 220 - if (!path.node.tag.arguments.length) { 221 - throw path 222 - .get('tag') 223 - .buildCodeFrameError( 224 - 'match() must at least be called with a node name' 225 - ); 214 + let name = path.node.tag.arguments[0]; 215 + if (!name) { 216 + name = t.nullLiteral(); 226 217 } 227 218 228 - const name = path.node.tag.arguments[0]; 229 219 const quasis = path.node.quasi.quasis.map((x) => x.value.cooked); 230 220 231 221 const expressions = this._prepareExpressions(path);
+1 -1
src/codegen.js
··· 194 194 capture: true, 195 195 })} 196 196 197 - ${_node}.tag = ${name}; 197 + if (${name}) ${_node}.tag = ${name}; 198 198 return ${transform ? js`(${transform})(${_node})` : _node}; 199 199 }) 200 200 `;
+8
src/core.test.js
··· 15 15 } 16 16 }; 17 17 18 + describe('can create nameless matchers', () => { 19 + it('matches without tagging', () => { 20 + const state = { quasis: ['1'], expressions: [], x: 0, y: 0 }; 21 + const node = match(null)`${/1/}`; 22 + expect(node(state)).toEqual(['1']); 23 + }); 24 + }); 25 + 18 26 describe('required matcher', () => { 19 27 const node = match('node')`${/1/}`; 20 28 it.each`