├── LICENSE ├── README.md ├── ast-traverse.js ├── package.json └── tst ├── tst-ast.json └── tst.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Olov Lassus 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ast-traverse.js 2 | Simple but flexible AST traversal with pre and post visitors. 3 | Works in node and browsers. 4 | 5 | 6 | 7 | ## Usage 8 | ```javascript 9 | // ast is a Mozilla Parser API compatible structure 10 | // generated by Esprima or another parser 11 | var ast = require("esprima").parse("f(1, x) + 2"); 12 | 13 | var traverse = require("ast-traverse"); 14 | 15 | // print AST node types, pre-order (node first, then its children) 16 | traverse(ast, {pre: function(node, parent, prop, idx) { 17 | console.log(node.type + (parent ? " from parent " + parent.type + 18 | " via " + prop + (idx !== undefined ? "[" + idx + "]" : "") : "")); 19 | }}); 20 | console.log(); 21 | /* 22 | => 23 | Program 24 | ExpressionStatement from parent Program via body[0] 25 | BinaryExpression from parent ExpressionStatement via expression 26 | CallExpression from parent BinaryExpression via left 27 | Identifier from parent CallExpression via callee 28 | Literal from parent CallExpression via arguments[0] 29 | Identifier from parent CallExpression via arguments[1] 30 | Literal from parent BinaryExpression via right 31 | */ 32 | 33 | 34 | // you can also visit post-order, or both 35 | // all four arguments are provided to both visitors (left out unused below) 36 | var indent = 0; 37 | traverse(ast, { 38 | pre: function(node) { 39 | console.log(Array(indent + 1).join(" ") + node.type); 40 | indent += 4; 41 | }, 42 | post: function() { 43 | indent -= 4; 44 | } 45 | }); 46 | console.log(); 47 | /* 48 | => 49 | Program 50 | ExpressionStatement 51 | BinaryExpression 52 | CallExpression 53 | Identifier 54 | Literal 55 | Identifier 56 | Literal 57 | */ 58 | 59 | 60 | // return false from the pre-visitor to skip traversing its children 61 | // throw an exception to abort traversal 62 | 63 | 64 | // by default node property names beginning with $ are skipped 65 | // but you can supply your own skipProperty function instead 66 | traverse(ast, { 67 | pre: function(node) { 68 | console.log(node.type); 69 | }, 70 | skipProperty: function(prop, node) { 71 | return prop === "parent" || prop === "expression"; 72 | } 73 | }); 74 | /* 75 | => 76 | Program 77 | ExpressionStatement 78 | */ 79 | ``` 80 | 81 | 82 | 83 | ## Installation 84 | 85 | ### Node 86 | Install using npm 87 | 88 | npm install ast-traverse 89 | 90 | ```javascript 91 | var traverse = require("ast-traverse"); 92 | ``` 93 | 94 | ### Browser 95 | Clone the repo and include it in a script tag 96 | 97 | git clone https://github.com/olov/ast-traverse.git 98 | 99 | ```html 100 | 101 | ``` 102 | -------------------------------------------------------------------------------- /ast-traverse.js: -------------------------------------------------------------------------------- 1 | function traverse(root, options) { 2 | "use strict"; 3 | 4 | options = options || {}; 5 | var pre = options.pre; 6 | var post = options.post; 7 | var skipProperty = options.skipProperty; 8 | 9 | function visit(node, parent, prop, idx) { 10 | if (!node || typeof node.type !== "string") { 11 | return; 12 | } 13 | 14 | var res = undefined; 15 | if (pre) { 16 | res = pre(node, parent, prop, idx); 17 | } 18 | 19 | if (res !== false) { 20 | for (var prop in node) { 21 | if (skipProperty ? skipProperty(prop, node) : prop[0] === "$") { 22 | continue; 23 | } 24 | 25 | var child = node[prop]; 26 | 27 | if (Array.isArray(child)) { 28 | for (var i = 0; i < child.length; i++) { 29 | visit(child[i], node, prop, i); 30 | } 31 | } else { 32 | visit(child, node, prop); 33 | } 34 | } 35 | } 36 | 37 | if (post) { 38 | post(node, parent, prop, idx); 39 | } 40 | } 41 | 42 | visit(root, null); 43 | }; 44 | 45 | if (typeof module !== "undefined" && typeof module.exports !== "undefined") { 46 | module.exports = traverse; 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ast-traverse", 3 | "version": "0.1.1", 4 | "description": "simple but flexible AST traversal with pre and post visitors", 5 | "main": "ast-traverse.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/olov/ast-traverse.git" 9 | }, 10 | "keywords": [ 11 | "ast", 12 | "traverse", 13 | "traversal", 14 | "walk", 15 | "visit", 16 | "visitor", 17 | "esprima" 18 | ], 19 | "author": "Olov Lassus ", 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /tst/tst-ast.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Program", 3 | "body": [ 4 | { 5 | "type": "ExpressionStatement", 6 | "expression": { 7 | "type": "BinaryExpression", 8 | "operator": "+", 9 | "left": { 10 | "type": "CallExpression", 11 | "callee": { 12 | "type": "Identifier", 13 | "name": "f" 14 | }, 15 | "arguments": [ 16 | { 17 | "type": "Literal", 18 | "value": 1 19 | }, 20 | { 21 | "type": "Identifier", 22 | "name": "x" 23 | } 24 | ] 25 | }, 26 | "right": { 27 | "type": "Literal", 28 | "value": 2 29 | } 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /tst/tst.js: -------------------------------------------------------------------------------- 1 | // ast is a Mozilla Parser API compatible structure 2 | // generated by Esprima or another parser 3 | var ast = require("./tst-ast.json"); 4 | // or: var ast = require("esprima").parse("f(1, x) + 2"); 5 | 6 | var traverse = require("../ast-traverse"); 7 | 8 | // print AST node types, pre-order (node first, then its children) 9 | traverse(ast, {pre: function(node, parent, prop, idx) { 10 | console.log(node.type + (parent ? " from parent " + parent.type + 11 | " via " + prop + (idx !== undefined ? "[" + idx + "]" : "") : "")); 12 | }}); 13 | console.log(); 14 | /* 15 | => 16 | Program 17 | ExpressionStatement from parent Program via body[0] 18 | BinaryExpression from parent ExpressionStatement via expression 19 | CallExpression from parent BinaryExpression via left 20 | Identifier from parent CallExpression via callee 21 | Literal from parent CallExpression via arguments[0] 22 | Identifier from parent CallExpression via arguments[1] 23 | Literal from parent BinaryExpression via right 24 | */ 25 | 26 | 27 | // you can also visit post-order, or both 28 | // all four arguments are provided to both visitors (left out unused below) 29 | var indent = 0; 30 | traverse(ast, { 31 | pre: function(node) { 32 | console.log(Array(indent + 1).join(" ") + node.type); 33 | indent += 4; 34 | }, 35 | post: function() { 36 | indent -= 4; 37 | } 38 | }); 39 | console.log(); 40 | /* 41 | => 42 | Program 43 | ExpressionStatement 44 | BinaryExpression 45 | CallExpression 46 | Identifier 47 | Literal 48 | Identifier 49 | Literal 50 | */ 51 | 52 | 53 | // return false from the pre-visitor to skip traversing its children 54 | // throw an exception to abort traversal 55 | 56 | 57 | // by default node property names beginning with $ are skipped 58 | // but you can supply your own skipProperty function instead 59 | traverse(ast, { 60 | pre: function(node) { 61 | console.log(node.type); 62 | }, 63 | skipProperty: function(prop, node) { 64 | return prop === "parent" || prop === "expression"; 65 | } 66 | }); 67 | /* 68 | => 69 | Program 70 | ExpressionStatement 71 | */ 72 | --------------------------------------------------------------------------------