1// AST walker module for Mozilla Parser API compatible trees 2 3// A simple walk is one where you simply specify callbacks to be 4// called on specific nodes. The last two arguments are optional. A 5// simple use would be 6// 7// walk.simple(myTree, { 8// Expression: function(node) { ... } 9// }); 10// 11// to do something with all expressions. All Parser API node types 12// can be used to identify node types, as well as Expression and 13// Statement, which denote categories of nodes. 14// 15// The base argument can be used to pass a custom (recursive) 16// walker, and state can be used to give this walked an initial 17// state. 18 19function simple(node, visitors, baseVisitor, state, override) { 20 if (!baseVisitor) { baseVisitor = base 21 ; }(function c(node, st, override) { 22 var type = override || node.type, found = visitors[type]; 23 baseVisitor[type](node, st, c); 24 if (found) { found(node, st); } 25 })(node, state, override); 26} 27 28// An ancestor walk keeps an array of ancestor nodes (including the 29// current node) and passes them to the callback as third parameter 30// (and also as state parameter when no other state is present). 31function ancestor(node, visitors, baseVisitor, state, override) { 32 var ancestors = []; 33 if (!baseVisitor) { baseVisitor = base 34 ; }(function c(node, st, override) { 35 var type = override || node.type, found = visitors[type]; 36 var isNew = node !== ancestors[ancestors.length - 1]; 37 if (isNew) { ancestors.push(node); } 38 baseVisitor[type](node, st, c); 39 if (found) { found(node, st || ancestors, ancestors); } 40 if (isNew) { ancestors.pop(); } 41 })(node, state, override); 42} 43 44// A recursive walk is one where your functions override the default 45// walkers. They can modify and replace the state parameter that's 46// threaded through the walk, and can opt how and whether to walk 47// their child nodes (by calling their third argument on these 48// nodes). 49function recursive(node, state, funcs, baseVisitor, override) { 50 var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor 51 ;(function c(node, st, override) { 52 visitor[override || node.type](node, st, c); 53 })(node, state, override); 54} 55 56function makeTest(test) { 57 if (typeof test === "string") 58 { return function (type) { return type === test; } } 59 else if (!test) 60 { return function () { return true; } } 61 else 62 { return test } 63} 64 65var Found = function Found(node, state) { this.node = node; this.state = state; }; 66 67// A full walk triggers the callback on each node 68function full(node, callback, baseVisitor, state, override) { 69 if (!baseVisitor) { baseVisitor = base; } 70 var last 71 ;(function c(node, st, override) { 72 var type = override || node.type; 73 baseVisitor[type](node, st, c); 74 if (last !== node) { 75 callback(node, st, type); 76 last = node; 77 } 78 })(node, state, override); 79} 80 81// An fullAncestor walk is like an ancestor walk, but triggers 82// the callback on each node 83function fullAncestor(node, callback, baseVisitor, state) { 84 if (!baseVisitor) { baseVisitor = base; } 85 var ancestors = [], last 86 ;(function c(node, st, override) { 87 var type = override || node.type; 88 var isNew = node !== ancestors[ancestors.length - 1]; 89 if (isNew) { ancestors.push(node); } 90 baseVisitor[type](node, st, c); 91 if (last !== node) { 92 callback(node, st || ancestors, ancestors, type); 93 last = node; 94 } 95 if (isNew) { ancestors.pop(); } 96 })(node, state); 97} 98 99// Find a node with a given start, end, and type (all are optional, 100// null can be used as wildcard). Returns a {node, state} object, or 101// undefined when it doesn't find a matching node. 102function findNodeAt(node, start, end, test, baseVisitor, state) { 103 if (!baseVisitor) { baseVisitor = base; } 104 test = makeTest(test); 105 try { 106 (function c(node, st, override) { 107 var type = override || node.type; 108 if ((start == null || node.start <= start) && 109 (end == null || node.end >= end)) 110 { baseVisitor[type](node, st, c); } 111 if ((start == null || node.start === start) && 112 (end == null || node.end === end) && 113 test(type, node)) 114 { throw new Found(node, st) } 115 })(node, state); 116 } catch (e) { 117 if (e instanceof Found) { return e } 118 throw e 119 } 120} 121 122// Find the innermost node of a given type that contains the given 123// position. Interface similar to findNodeAt. 124function findNodeAround(node, pos, test, baseVisitor, state) { 125 test = makeTest(test); 126 if (!baseVisitor) { baseVisitor = base; } 127 try { 128 (function c(node, st, override) { 129 var type = override || node.type; 130 if (node.start > pos || node.end < pos) { return } 131 baseVisitor[type](node, st, c); 132 if (test(type, node)) { throw new Found(node, st) } 133 })(node, state); 134 } catch (e) { 135 if (e instanceof Found) { return e } 136 throw e 137 } 138} 139 140// Find the outermost matching node after a given position. 141function findNodeAfter(node, pos, test, baseVisitor, state) { 142 test = makeTest(test); 143 if (!baseVisitor) { baseVisitor = base; } 144 try { 145 (function c(node, st, override) { 146 if (node.end < pos) { return } 147 var type = override || node.type; 148 if (node.start >= pos && test(type, node)) { throw new Found(node, st) } 149 baseVisitor[type](node, st, c); 150 })(node, state); 151 } catch (e) { 152 if (e instanceof Found) { return e } 153 throw e 154 } 155} 156 157// Find the outermost matching node before a given position. 158function findNodeBefore(node, pos, test, baseVisitor, state) { 159 test = makeTest(test); 160 if (!baseVisitor) { baseVisitor = base; } 161 var max 162 ;(function c(node, st, override) { 163 if (node.start > pos) { return } 164 var type = override || node.type; 165 if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) 166 { max = new Found(node, st); } 167 baseVisitor[type](node, st, c); 168 })(node, state); 169 return max 170} 171 172// Used to create a custom walker. Will fill in all missing node 173// type properties with the defaults. 174function make(funcs, baseVisitor) { 175 var visitor = Object.create(baseVisitor || base); 176 for (var type in funcs) { visitor[type] = funcs[type]; } 177 return visitor 178} 179 180function skipThrough(node, st, c) { c(node, st); } 181function ignore(_node, _st, _c) {} 182 183// Node walkers. 184 185var base = {}; 186 187base.Program = base.BlockStatement = function (node, st, c) { 188 for (var i = 0, list = node.body; i < list.length; i += 1) 189 { 190 var stmt = list[i]; 191 192 c(stmt, st, "Statement"); 193 } 194}; 195base.Statement = skipThrough; 196base.EmptyStatement = ignore; 197base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression = 198 function (node, st, c) { return c(node.expression, st, "Expression"); }; 199base.IfStatement = function (node, st, c) { 200 c(node.test, st, "Expression"); 201 c(node.consequent, st, "Statement"); 202 if (node.alternate) { c(node.alternate, st, "Statement"); } 203}; 204base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); }; 205base.BreakStatement = base.ContinueStatement = ignore; 206base.WithStatement = function (node, st, c) { 207 c(node.object, st, "Expression"); 208 c(node.body, st, "Statement"); 209}; 210base.SwitchStatement = function (node, st, c) { 211 c(node.discriminant, st, "Expression"); 212 for (var i$1 = 0, list$1 = node.cases; i$1 < list$1.length; i$1 += 1) { 213 var cs = list$1[i$1]; 214 215 if (cs.test) { c(cs.test, st, "Expression"); } 216 for (var i = 0, list = cs.consequent; i < list.length; i += 1) 217 { 218 var cons = list[i]; 219 220 c(cons, st, "Statement"); 221 } 222 } 223}; 224base.SwitchCase = function (node, st, c) { 225 if (node.test) { c(node.test, st, "Expression"); } 226 for (var i = 0, list = node.consequent; i < list.length; i += 1) 227 { 228 var cons = list[i]; 229 230 c(cons, st, "Statement"); 231 } 232}; 233base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) { 234 if (node.argument) { c(node.argument, st, "Expression"); } 235}; 236base.ThrowStatement = base.SpreadElement = 237 function (node, st, c) { return c(node.argument, st, "Expression"); }; 238base.TryStatement = function (node, st, c) { 239 c(node.block, st, "Statement"); 240 if (node.handler) { c(node.handler, st); } 241 if (node.finalizer) { c(node.finalizer, st, "Statement"); } 242}; 243base.CatchClause = function (node, st, c) { 244 if (node.param) { c(node.param, st, "Pattern"); } 245 c(node.body, st, "Statement"); 246}; 247base.WhileStatement = base.DoWhileStatement = function (node, st, c) { 248 c(node.test, st, "Expression"); 249 c(node.body, st, "Statement"); 250}; 251base.ForStatement = function (node, st, c) { 252 if (node.init) { c(node.init, st, "ForInit"); } 253 if (node.test) { c(node.test, st, "Expression"); } 254 if (node.update) { c(node.update, st, "Expression"); } 255 c(node.body, st, "Statement"); 256}; 257base.ForInStatement = base.ForOfStatement = function (node, st, c) { 258 c(node.left, st, "ForInit"); 259 c(node.right, st, "Expression"); 260 c(node.body, st, "Statement"); 261}; 262base.ForInit = function (node, st, c) { 263 if (node.type === "VariableDeclaration") { c(node, st); } 264 else { c(node, st, "Expression"); } 265}; 266base.DebuggerStatement = ignore; 267 268base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); }; 269base.VariableDeclaration = function (node, st, c) { 270 for (var i = 0, list = node.declarations; i < list.length; i += 1) 271 { 272 var decl = list[i]; 273 274 c(decl, st); 275 } 276}; 277base.VariableDeclarator = function (node, st, c) { 278 c(node.id, st, "Pattern"); 279 if (node.init) { c(node.init, st, "Expression"); } 280}; 281 282base.Function = function (node, st, c) { 283 if (node.id) { c(node.id, st, "Pattern"); } 284 for (var i = 0, list = node.params; i < list.length; i += 1) 285 { 286 var param = list[i]; 287 288 c(param, st, "Pattern"); 289 } 290 c(node.body, st, node.expression ? "Expression" : "Statement"); 291}; 292 293base.Pattern = function (node, st, c) { 294 if (node.type === "Identifier") 295 { c(node, st, "VariablePattern"); } 296 else if (node.type === "MemberExpression") 297 { c(node, st, "MemberPattern"); } 298 else 299 { c(node, st); } 300}; 301base.VariablePattern = ignore; 302base.MemberPattern = skipThrough; 303base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); }; 304base.ArrayPattern = function (node, st, c) { 305 for (var i = 0, list = node.elements; i < list.length; i += 1) { 306 var elt = list[i]; 307 308 if (elt) { c(elt, st, "Pattern"); } 309 } 310}; 311base.ObjectPattern = function (node, st, c) { 312 for (var i = 0, list = node.properties; i < list.length; i += 1) { 313 var prop = list[i]; 314 315 if (prop.type === "Property") { 316 if (prop.computed) { c(prop.key, st, "Expression"); } 317 c(prop.value, st, "Pattern"); 318 } else if (prop.type === "RestElement") { 319 c(prop.argument, st, "Pattern"); 320 } 321 } 322}; 323 324base.Expression = skipThrough; 325base.ThisExpression = base.Super = base.MetaProperty = ignore; 326base.ArrayExpression = function (node, st, c) { 327 for (var i = 0, list = node.elements; i < list.length; i += 1) { 328 var elt = list[i]; 329 330 if (elt) { c(elt, st, "Expression"); } 331 } 332}; 333base.ObjectExpression = function (node, st, c) { 334 for (var i = 0, list = node.properties; i < list.length; i += 1) 335 { 336 var prop = list[i]; 337 338 c(prop, st); 339 } 340}; 341base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration; 342base.SequenceExpression = function (node, st, c) { 343 for (var i = 0, list = node.expressions; i < list.length; i += 1) 344 { 345 var expr = list[i]; 346 347 c(expr, st, "Expression"); 348 } 349}; 350base.TemplateLiteral = function (node, st, c) { 351 for (var i = 0, list = node.quasis; i < list.length; i += 1) 352 { 353 var quasi = list[i]; 354 355 c(quasi, st); 356 } 357 358 for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1) 359 { 360 var expr = list$1[i$1]; 361 362 c(expr, st, "Expression"); 363 } 364}; 365base.TemplateElement = ignore; 366base.UnaryExpression = base.UpdateExpression = function (node, st, c) { 367 c(node.argument, st, "Expression"); 368}; 369base.BinaryExpression = base.LogicalExpression = function (node, st, c) { 370 c(node.left, st, "Expression"); 371 c(node.right, st, "Expression"); 372}; 373base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) { 374 c(node.left, st, "Pattern"); 375 c(node.right, st, "Expression"); 376}; 377base.ConditionalExpression = function (node, st, c) { 378 c(node.test, st, "Expression"); 379 c(node.consequent, st, "Expression"); 380 c(node.alternate, st, "Expression"); 381}; 382base.NewExpression = base.CallExpression = function (node, st, c) { 383 c(node.callee, st, "Expression"); 384 if (node.arguments) 385 { for (var i = 0, list = node.arguments; i < list.length; i += 1) 386 { 387 var arg = list[i]; 388 389 c(arg, st, "Expression"); 390 } } 391}; 392base.MemberExpression = function (node, st, c) { 393 c(node.object, st, "Expression"); 394 if (node.computed) { c(node.property, st, "Expression"); } 395}; 396base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) { 397 if (node.declaration) 398 { c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); } 399 if (node.source) { c(node.source, st, "Expression"); } 400}; 401base.ExportAllDeclaration = function (node, st, c) { 402 if (node.exported) 403 { c(node.exported, st); } 404 c(node.source, st, "Expression"); 405}; 406base.ImportDeclaration = function (node, st, c) { 407 for (var i = 0, list = node.specifiers; i < list.length; i += 1) 408 { 409 var spec = list[i]; 410 411 c(spec, st); 412 } 413 c(node.source, st, "Expression"); 414}; 415base.ImportExpression = function (node, st, c) { 416 c(node.source, st, "Expression"); 417}; 418base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore; 419 420base.TaggedTemplateExpression = function (node, st, c) { 421 c(node.tag, st, "Expression"); 422 c(node.quasi, st, "Expression"); 423}; 424base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); }; 425base.Class = function (node, st, c) { 426 if (node.id) { c(node.id, st, "Pattern"); } 427 if (node.superClass) { c(node.superClass, st, "Expression"); } 428 c(node.body, st); 429}; 430base.ClassBody = function (node, st, c) { 431 for (var i = 0, list = node.body; i < list.length; i += 1) 432 { 433 var elt = list[i]; 434 435 c(elt, st); 436 } 437}; 438base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) { 439 if (node.computed) { c(node.key, st, "Expression"); } 440 if (node.value) { c(node.value, st, "Expression"); } 441}; 442 443export { ancestor, base, findNodeAfter, findNodeAround, findNodeAt, findNodeBefore, full, fullAncestor, make, recursive, simple }; 444