Mirror: The magical sticky regex-based parser generator 馃
at v1.0.2 11 kB view raw
1let t; 2let ids = {}; 3 4export function initGenerator(_ids, _t) { 5 ids = _ids; 6 t = _t; 7} 8 9/** var id = state.index; */ 10class AssignIndexNode { 11 constructor(id) { 12 this.id = id; 13 } 14 15 statement() { 16 const member = t.memberExpression(ids.state, t.identifier('index')); 17 return t.variableDeclaration('var', [ 18 t.variableDeclarator(this.id, member), 19 ]); 20 } 21} 22 23/** state.index = id; */ 24class RestoreIndexNode { 25 constructor(id) { 26 this.id = id; 27 } 28 29 statement() { 30 const expression = t.assignmentExpression( 31 '=', 32 t.memberExpression(ids.state, t.identifier('index')), 33 this.id 34 ); 35 36 return t.expressionStatement(expression); 37 } 38} 39 40/** var id = node.length; */ 41class AssignLengthNode { 42 constructor(id) { 43 this.id = id; 44 } 45 46 statement() { 47 return t.variableDeclaration('var', [ 48 t.variableDeclarator( 49 this.id, 50 t.memberExpression(ids.node, t.identifier('length')) 51 ), 52 ]); 53 } 54} 55 56/** node.length = id; */ 57class RestoreLengthNode { 58 constructor(id) { 59 this.id = id; 60 } 61 62 statement() { 63 const expression = t.assignmentExpression( 64 '=', 65 t.memberExpression(ids.node, t.identifier('length')), 66 this.id 67 ); 68 69 return t.expressionStatement(expression); 70 } 71} 72 73/** return; break id; */ 74class AbortNode { 75 constructor(id) { 76 this.id = id || null; 77 } 78 79 statement() { 80 const statement = this.id ? t.breakStatement(this.id) : t.returnStatement(); 81 return statement; 82 } 83} 84 85/** if (condition) { return; break id; } */ 86class AbortConditionNode { 87 constructor(condition, opts) { 88 this.condition = condition || null; 89 90 this.abort = opts.abort; 91 this.abortCondition = opts.abortCondition || null; 92 this.restoreIndex = opts.restoreIndex; 93 } 94 95 statement() { 96 return t.ifStatement( 97 this.condition, 98 t.blockStatement( 99 [this.restoreIndex.statement(), this.abort.statement()].filter(Boolean) 100 ), 101 this.abortCondition ? this.abortCondition.statement() : null 102 ); 103 } 104} 105 106/** Generates a full matcher for an expression */ 107class ExpressionNode { 108 constructor(ast, depth, opts) { 109 this.ast = ast; 110 this.depth = depth || 0; 111 this.capturing = !!opts.capturing; 112 this.restoreIndex = opts.restoreIndex; 113 this.restoreLength = opts.restoreLength || null; 114 this.abortCondition = opts.abortCondition || null; 115 this.abort = opts.abort || null; 116 } 117 118 statements() { 119 const execMatch = this.ast.expression; 120 const assignMatch = t.assignmentExpression('=', ids.match, execMatch); 121 122 const successNodes = t.blockStatement([ 123 t.expressionStatement( 124 t.callExpression(t.memberExpression(ids.node, t.identifier('push')), [ 125 ids.match, 126 ]) 127 ), 128 ]); 129 130 const abortNodes = t.blockStatement( 131 [ 132 this.abortCondition && this.abortCondition.statement(), 133 this.abort && this.restoreLength && this.restoreLength.statement(), 134 this.restoreIndex && this.restoreIndex.statement(), 135 this.abort && this.abort.statement(), 136 ].filter(Boolean) 137 ); 138 139 return [ 140 !this.capturing 141 ? t.ifStatement(t.unaryExpression('!', execMatch), abortNodes) 142 : t.ifStatement(assignMatch, successNodes, abortNodes), 143 ]; 144 } 145} 146 147/** Generates a full matcher for a group */ 148class GroupNode { 149 constructor(ast, depth, opts) { 150 this.ast = ast; 151 this.depth = depth || 0; 152 if (ast.sequence.length === 1) { 153 return new ExpressionNode(ast.sequence[0], depth, opts); 154 } 155 156 const lengthId = t.identifier(`length_${depth}`); 157 const childOpts = { 158 ...opts, 159 capturing: !!opts.capturing && !!ast.capturing, 160 }; 161 162 this.assignLength = null; 163 if (!childOpts.restoreLength && childOpts.capturing) { 164 this.assignLength = new AssignLengthNode(lengthId); 165 childOpts.restoreLength = new RestoreLengthNode(lengthId); 166 } 167 168 this.alternation = new AlternationNode(ast.sequence, depth + 1, childOpts); 169 } 170 171 statements() { 172 return [ 173 this.assignLength && this.assignLength.statement(), 174 ...this.alternation.statements(), 175 ].filter(Boolean); 176 } 177} 178 179/** Generates looping logic around another group or expression matcher */ 180class QuantifierNode { 181 constructor(ast, depth, opts) { 182 const { quantifier } = ast; 183 this.ast = ast; 184 this.depth = depth || 0; 185 186 const invertId = t.identifier(`invert_${this.depth}`); 187 const loopId = t.identifier(`loop_${this.depth}`); 188 const iterId = t.identifier(`iter_${this.depth}`); 189 const indexId = t.identifier(`index_${this.depth}`); 190 const ChildNode = ast.type === 'group' ? GroupNode : ExpressionNode; 191 const childOpts = { ...opts }; 192 193 this.assignIndex = null; 194 this.restoreIndex = null; 195 this.blockId = null; 196 this.abort = null; 197 198 if (ast.type === 'group' && !!ast.lookahead) { 199 this.restoreIndex = new RestoreIndexNode(indexId); 200 this.assignIndex = new AssignIndexNode(indexId); 201 } 202 203 if (ast.type === 'group' && ast.lookahead === 'negative') { 204 childOpts.abort = new AbortNode(invertId); 205 childOpts.restoreIndex = this.restoreIndex; 206 this.restoreIndex = opts.restoreIndex; 207 this.blockId = invertId; 208 this.abort = opts.abort; 209 } 210 211 if (quantifier && !quantifier.singular && quantifier.required) { 212 childOpts.abortCondition = new AbortConditionNode(iterId, { 213 ...opts, 214 restoreIndex: new RestoreIndexNode(indexId), 215 abort: new AbortNode(loopId), 216 }); 217 } else if (quantifier && !quantifier.singular) { 218 childOpts.restoreLength = null; 219 childOpts.restoreIndex = new RestoreIndexNode(indexId); 220 childOpts.abort = new AbortNode(loopId); 221 childOpts.abortCondition = null; 222 } else if (quantifier && !quantifier.required) { 223 childOpts.restoreIndex = new RestoreIndexNode(indexId); 224 childOpts.abortCondition = null; 225 childOpts.abort = null; 226 } 227 228 this.childNode = new ChildNode(ast, depth, childOpts); 229 } 230 231 statements() { 232 const { quantifier } = this.ast; 233 const loopId = t.identifier(`loop_${this.depth}`); 234 const iterId = t.identifier(`iter_${this.depth}`); 235 const indexId = t.identifier(`index_${this.depth}`); 236 const assignIndex = new AssignIndexNode(indexId); 237 238 let statements; 239 if (quantifier && !quantifier.singular && quantifier.required) { 240 statements = [ 241 t.labeledStatement( 242 loopId, 243 t.forStatement( 244 t.variableDeclaration('var', [ 245 t.variableDeclarator(iterId, t.numericLiteral(0)), 246 ]), 247 t.booleanLiteral(true), 248 t.updateExpression('++', iterId), 249 t.blockStatement([ 250 assignIndex.statement(), 251 ...this.childNode.statements(), 252 ]) 253 ) 254 ), 255 ]; 256 } else if (quantifier && !quantifier.singular) { 257 statements = [ 258 t.labeledStatement( 259 loopId, 260 t.whileStatement( 261 t.booleanLiteral(true), 262 t.blockStatement([ 263 assignIndex.statement(), 264 ...this.childNode.statements(), 265 ]) 266 ) 267 ), 268 ]; 269 } else if (quantifier && !quantifier.required) { 270 statements = [assignIndex.statement(), ...this.childNode.statements()]; 271 } else { 272 statements = this.childNode.statements(); 273 } 274 275 if (this.blockId && this.assignIndex && this.restoreIndex) { 276 statements = [ 277 t.labeledStatement( 278 this.blockId, 279 t.blockStatement( 280 [ 281 this.assignIndex.statement(), 282 ...statements, 283 this.restoreIndex.statement(), 284 this.abort.statement(), 285 ].filter(Boolean) 286 ) 287 ), 288 ].filter(Boolean); 289 } else if (this.assignIndex && this.restoreIndex) { 290 statements.unshift(this.assignIndex.statement()); 291 statements.push(this.restoreIndex.statement()); 292 } 293 294 return statements; 295 } 296} 297 298/** Generates a matcher of a sequence of sub-matchers for a single sequence */ 299class SequenceNode { 300 constructor(ast, depth, opts) { 301 this.ast = ast; 302 this.depth = depth || 0; 303 304 const indexId = t.identifier(`index_${depth}`); 305 const blockId = t.identifier(`block_${this.depth}`); 306 307 this.returnStatement = opts.returnStatement; 308 this.assignIndex = ast.alternation ? new AssignIndexNode(indexId) : null; 309 310 this.quantifiers = ast.sequence.map((childAst) => { 311 return new QuantifierNode(childAst, depth, { 312 ...opts, 313 restoreIndex: ast.alternation 314 ? new RestoreIndexNode(indexId) 315 : opts.restoreIndex, 316 abortCondition: ast.alternation ? null : opts.abortCondition, 317 abort: ast.alternation ? new AbortNode(blockId) : opts.abort, 318 }); 319 }); 320 } 321 322 statements() { 323 const blockId = t.identifier(`block_${this.depth}`); 324 const alternationId = t.identifier(`alternation_${this.depth}`); 325 const statements = this.quantifiers.reduce((block, node) => { 326 block.push(...node.statements()); 327 return block; 328 }, []); 329 330 if (!this.ast.alternation) { 331 return statements; 332 } 333 334 const abortNode = 335 this.depth === 0 ? this.returnStatement : t.breakStatement(alternationId); 336 337 return [ 338 t.labeledStatement( 339 blockId, 340 t.blockStatement([ 341 this.assignIndex && this.assignIndex.statement(), 342 ...statements, 343 abortNode, 344 ]) 345 ), 346 ]; 347 } 348} 349 350/** Generates matchers for sequences with (or without) alternations */ 351class AlternationNode { 352 constructor(ast, depth, opts) { 353 this.ast = ast; 354 this.depth = depth || 0; 355 this.sequences = []; 356 for (let current = ast; current; current = current.alternation) { 357 this.sequences.push(new SequenceNode(current, depth, opts)); 358 } 359 } 360 361 statements() { 362 if (this.sequences.length === 1) { 363 return this.sequences[0].statements(); 364 } 365 366 const statements = []; 367 for (let i = 0; i < this.sequences.length; i++) { 368 statements.push(...this.sequences[i].statements()); 369 } 370 371 if (this.depth === 0) { 372 return statements; 373 } 374 375 const alternationId = t.identifier(`alternation_${this.depth}`); 376 return [t.labeledStatement(alternationId, t.blockStatement(statements))]; 377 } 378} 379 380export class RootNode { 381 constructor(ast, nameNode, transformNode) { 382 const indexId = t.identifier('last_index'); 383 const node = t.callExpression(ids.tag, [ids.node, nameNode]); 384 385 this.returnStatement = t.returnStatement( 386 transformNode ? t.callExpression(transformNode, [node]) : node 387 ); 388 389 this.assignIndex = new AssignIndexNode(indexId); 390 this.node = new AlternationNode(ast, 0, { 391 returnStatement: this.returnStatement, 392 restoreIndex: new RestoreIndexNode(indexId, true), 393 restoreLength: null, 394 abortCondition: null, 395 abort: new AbortNode(), 396 capturing: true, 397 }); 398 } 399 400 statements() { 401 return [ 402 this.assignIndex.statement(), 403 t.variableDeclaration('var', [ 404 t.variableDeclarator(ids.match), 405 t.variableDeclarator(ids.node, t.arrayExpression()), 406 ]), 407 ...this.node.statements(), 408 this.returnStatement, 409 ]; 410 } 411}