walk.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // AST walker module for Mozilla Parser API compatible trees
  2. (function(mod) {
  3. if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
  4. if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
  5. mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
  6. })(function(exports) {
  7. "use strict";
  8. // A simple walk is one where you simply specify callbacks to be
  9. // called on specific nodes. The last two arguments are optional. A
  10. // simple use would be
  11. //
  12. // walk.simple(myTree, {
  13. // Expression: function(node) { ... }
  14. // });
  15. //
  16. // to do something with all expressions. All Parser API node types
  17. // can be used to identify node types, as well as Expression,
  18. // Statement, and ScopeBody, which denote categories of nodes.
  19. //
  20. // The base argument can be used to pass a custom (recursive)
  21. // walker, and state can be used to give this walked an initial
  22. // state.
  23. exports.simple = function(node, visitors, base, state) {
  24. if (!base) base = exports.base;
  25. function c(node, st, override) {
  26. var type = override || node.type, found = visitors[type];
  27. base[type](node, st, c);
  28. if (found) found(node, st);
  29. }
  30. c(node, state);
  31. };
  32. // An ancestor walk builds up an array of ancestor nodes (including
  33. // the current node) and passes them to the callback as the state parameter.
  34. exports.ancestor = function(node, visitors, base, state) {
  35. if (!base) base = exports.base;
  36. if (!state) state = [];
  37. function c(node, st, override) {
  38. var type = override || node.type, found = visitors[type];
  39. if (node != st[st.length - 1]) {
  40. st = st.slice();
  41. st.push(node);
  42. }
  43. base[type](node, st, c);
  44. if (found) found(node, st);
  45. }
  46. c(node, state);
  47. };
  48. // A recursive walk is one where your functions override the default
  49. // walkers. They can modify and replace the state parameter that's
  50. // threaded through the walk, and can opt how and whether to walk
  51. // their child nodes (by calling their third argument on these
  52. // nodes).
  53. exports.recursive = function(node, state, funcs, base) {
  54. var visitor = funcs ? exports.make(funcs, base) : base;
  55. function c(node, st, override) {
  56. visitor[override || node.type](node, st, c);
  57. }
  58. c(node, state);
  59. };
  60. function makeTest(test) {
  61. if (typeof test == "string")
  62. return function(type) { return type == test; };
  63. else if (!test)
  64. return function() { return true; };
  65. else
  66. return test;
  67. }
  68. function Found(node, state) { this.node = node; this.state = state; }
  69. // Find a node with a given start, end, and type (all are optional,
  70. // null can be used as wildcard). Returns a {node, state} object, or
  71. // undefined when it doesn't find a matching node.
  72. exports.findNodeAt = function(node, start, end, test, base, state) {
  73. test = makeTest(test);
  74. try {
  75. if (!base) base = exports.base;
  76. var c = function(node, st, override) {
  77. var type = override || node.type;
  78. if ((start == null || node.start <= start) &&
  79. (end == null || node.end >= end))
  80. base[type](node, st, c);
  81. if (test(type, node) &&
  82. (start == null || node.start == start) &&
  83. (end == null || node.end == end))
  84. throw new Found(node, st);
  85. };
  86. c(node, state);
  87. } catch (e) {
  88. if (e instanceof Found) return e;
  89. throw e;
  90. }
  91. };
  92. // Find the innermost node of a given type that contains the given
  93. // position. Interface similar to findNodeAt.
  94. exports.findNodeAround = function(node, pos, test, base, state) {
  95. test = makeTest(test);
  96. try {
  97. if (!base) base = exports.base;
  98. var c = function(node, st, override) {
  99. var type = override || node.type;
  100. if (node.start > pos || node.end < pos) return;
  101. base[type](node, st, c);
  102. if (test(type, node)) throw new Found(node, st);
  103. };
  104. c(node, state);
  105. } catch (e) {
  106. if (e instanceof Found) return e;
  107. throw e;
  108. }
  109. };
  110. // Find the outermost matching node after a given position.
  111. exports.findNodeAfter = function(node, pos, test, base, state) {
  112. test = makeTest(test);
  113. try {
  114. if (!base) base = exports.base;
  115. var c = function(node, st, override) {
  116. if (node.end < pos) return;
  117. var type = override || node.type;
  118. if (node.start >= pos && test(type, node)) throw new Found(node, st);
  119. base[type](node, st, c);
  120. };
  121. c(node, state);
  122. } catch (e) {
  123. if (e instanceof Found) return e;
  124. throw e;
  125. }
  126. };
  127. // Find the outermost matching node before a given position.
  128. exports.findNodeBefore = function(node, pos, test, base, state) {
  129. test = makeTest(test);
  130. if (!base) base = exports.base;
  131. var max;
  132. var c = function(node, st, override) {
  133. if (node.start > pos) return;
  134. var type = override || node.type;
  135. if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
  136. max = new Found(node, st);
  137. base[type](node, st, c);
  138. };
  139. c(node, state);
  140. return max;
  141. };
  142. // Used to create a custom walker. Will fill in all missing node
  143. // type properties with the defaults.
  144. exports.make = function(funcs, base) {
  145. if (!base) base = exports.base;
  146. var visitor = {};
  147. for (var type in base) visitor[type] = base[type];
  148. for (var type in funcs) visitor[type] = funcs[type];
  149. return visitor;
  150. };
  151. function skipThrough(node, st, c) { c(node, st); }
  152. function ignore(_node, _st, _c) {}
  153. // Node walkers.
  154. var base = exports.base = {};
  155. base.Program = base.BlockStatement = function(node, st, c) {
  156. for (var i = 0; i < node.body.length; ++i)
  157. c(node.body[i], st, "Statement");
  158. };
  159. base.Statement = skipThrough;
  160. base.EmptyStatement = ignore;
  161. base.ExpressionStatement = function(node, st, c) {
  162. c(node.expression, st, "Expression");
  163. };
  164. base.IfStatement = function(node, st, c) {
  165. c(node.test, st, "Expression");
  166. c(node.consequent, st, "Statement");
  167. if (node.alternate) c(node.alternate, st, "Statement");
  168. };
  169. base.LabeledStatement = function(node, st, c) {
  170. c(node.body, st, "Statement");
  171. };
  172. base.BreakStatement = base.ContinueStatement = ignore;
  173. base.WithStatement = function(node, st, c) {
  174. c(node.object, st, "Expression");
  175. c(node.body, st, "Statement");
  176. };
  177. base.SwitchStatement = function(node, st, c) {
  178. c(node.discriminant, st, "Expression");
  179. for (var i = 0; i < node.cases.length; ++i) {
  180. var cs = node.cases[i];
  181. if (cs.test) c(cs.test, st, "Expression");
  182. for (var j = 0; j < cs.consequent.length; ++j)
  183. c(cs.consequent[j], st, "Statement");
  184. }
  185. };
  186. base.ReturnStatement = function(node, st, c) {
  187. if (node.argument) c(node.argument, st, "Expression");
  188. };
  189. base.ThrowStatement = function(node, st, c) {
  190. c(node.argument, st, "Expression");
  191. };
  192. base.TryStatement = function(node, st, c) {
  193. c(node.block, st, "Statement");
  194. if (node.handler) c(node.handler.body, st, "ScopeBody");
  195. if (node.finalizer) c(node.finalizer, st, "Statement");
  196. };
  197. base.WhileStatement = function(node, st, c) {
  198. c(node.test, st, "Expression");
  199. c(node.body, st, "Statement");
  200. };
  201. base.DoWhileStatement = base.WhileStatement;
  202. base.ForStatement = function(node, st, c) {
  203. if (node.init) c(node.init, st, "ForInit");
  204. if (node.test) c(node.test, st, "Expression");
  205. if (node.update) c(node.update, st, "Expression");
  206. c(node.body, st, "Statement");
  207. };
  208. base.ForInStatement = function(node, st, c) {
  209. c(node.left, st, "ForInit");
  210. c(node.right, st, "Expression");
  211. c(node.body, st, "Statement");
  212. };
  213. base.ForInit = function(node, st, c) {
  214. if (node.type == "VariableDeclaration") c(node, st);
  215. else c(node, st, "Expression");
  216. };
  217. base.DebuggerStatement = ignore;
  218. base.FunctionDeclaration = function(node, st, c) {
  219. c(node, st, "Function");
  220. };
  221. base.VariableDeclaration = function(node, st, c) {
  222. for (var i = 0; i < node.declarations.length; ++i) {
  223. var decl = node.declarations[i];
  224. if (decl.init) c(decl.init, st, "Expression");
  225. }
  226. };
  227. base.Function = function(node, st, c) {
  228. c(node.body, st, "ScopeBody");
  229. };
  230. base.ScopeBody = function(node, st, c) {
  231. c(node, st, "Statement");
  232. };
  233. base.Expression = skipThrough;
  234. base.ThisExpression = ignore;
  235. base.ArrayExpression = function(node, st, c) {
  236. for (var i = 0; i < node.elements.length; ++i) {
  237. var elt = node.elements[i];
  238. if (elt) c(elt, st, "Expression");
  239. }
  240. };
  241. base.ObjectExpression = function(node, st, c) {
  242. for (var i = 0; i < node.properties.length; ++i)
  243. c(node.properties[i].value, st, "Expression");
  244. };
  245. base.FunctionExpression = base.FunctionDeclaration;
  246. base.SequenceExpression = function(node, st, c) {
  247. for (var i = 0; i < node.expressions.length; ++i)
  248. c(node.expressions[i], st, "Expression");
  249. };
  250. base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
  251. c(node.argument, st, "Expression");
  252. };
  253. base.BinaryExpression = base.AssignmentExpression = base.LogicalExpression = function(node, st, c) {
  254. c(node.left, st, "Expression");
  255. c(node.right, st, "Expression");
  256. };
  257. base.ConditionalExpression = function(node, st, c) {
  258. c(node.test, st, "Expression");
  259. c(node.consequent, st, "Expression");
  260. c(node.alternate, st, "Expression");
  261. };
  262. base.NewExpression = base.CallExpression = function(node, st, c) {
  263. c(node.callee, st, "Expression");
  264. if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
  265. c(node.arguments[i], st, "Expression");
  266. };
  267. base.MemberExpression = function(node, st, c) {
  268. c(node.object, st, "Expression");
  269. if (node.computed) c(node.property, st, "Expression");
  270. };
  271. base.Identifier = base.Literal = ignore;
  272. // A custom walker that keeps track of the scope chain and the
  273. // variables defined in it.
  274. function makeScope(prev, isCatch) {
  275. return {vars: Object.create(null), prev: prev, isCatch: isCatch};
  276. }
  277. function normalScope(scope) {
  278. while (scope.isCatch) scope = scope.prev;
  279. return scope;
  280. }
  281. exports.scopeVisitor = exports.make({
  282. Function: function(node, scope, c) {
  283. var inner = makeScope(scope);
  284. for (var i = 0; i < node.params.length; ++i)
  285. inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
  286. if (node.id) {
  287. var decl = node.type == "FunctionDeclaration";
  288. (decl ? normalScope(scope) : inner).vars[node.id.name] =
  289. {type: decl ? "function" : "function name", node: node.id};
  290. }
  291. c(node.body, inner, "ScopeBody");
  292. },
  293. TryStatement: function(node, scope, c) {
  294. c(node.block, scope, "Statement");
  295. if (node.handler) {
  296. var inner = makeScope(scope, true);
  297. inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
  298. c(node.handler.body, inner, "ScopeBody");
  299. }
  300. if (node.finalizer) c(node.finalizer, scope, "Statement");
  301. },
  302. VariableDeclaration: function(node, scope, c) {
  303. var target = normalScope(scope);
  304. for (var i = 0; i < node.declarations.length; ++i) {
  305. var decl = node.declarations[i];
  306. target.vars[decl.id.name] = {type: "var", node: decl.id};
  307. if (decl.init) c(decl.init, scope, "Expression");
  308. }
  309. }
  310. });
  311. });