• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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