Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ---------------------------------------------------------------------
- // Tree traversal with context data
- // ---------------------------------------------------------------------
- // This exercise shows how to traverse a tree with node-level context data
- // As example, we use the Esprima JavaScript parser in node.js
- // to find global usages and declarations of a JavaScript file
- // Tree traversal usually is solved with recursion - like here
- // The stack-based execution model automatically ensures that local data
- // will be cleared when a function is left
- // This is exactly what is needed for scope-specific context data in tree traversal
- // The worker function to be executed for each node has this signature:
- // func( node, nodeName, obj)
- // Here, node is the current node object with name nodeName.
- // obj is the context object attached to the node
- // The function func may return a new obj object as return value.
- // This will be used as "obj" parameter for all the child nodes of the current node
- // Also, it is possible to skip the traversal of a subtree
- // by raising the special exception _skipNode
- esprima = require( "esprima" );
- _skipNode = "Skip Node exception"; // special exception for skipping node
- function analyzeCode(code,func,obj) {
- var ast = esprima.parse(code);
- traverse(ast,"__root",func,obj);
- function traverse(node, nodeName, func, obj) {
- try {
- var _obj = func(node,nodeName,obj);
- Object.keys(node).forEach(function(nodeName){
- var child = node[nodeName];
- if (typeof child === 'object' && child !== null) {
- if (Array.isArray(child)) {
- child.forEach(function(node,i) {
- traverse(node, nodeName + "[" + i + "]",func, _obj);
- });
- } else {
- traverse(child, nodeName, func, _obj);
- }
- }
- });
- } catch (e) {
- if (e!=_skipNode) throw e;
- }
- }
- }
- function analyzeFile( fileName, func, obj, doAtEnd ) {
- fs.readFile( fileName, 'utf8', function( err, data ) {
- if (err) throw err;
- analyzeCode( data, func, obj );
- if (doAtEnd) doAtEnd( );
- });
- }
- function getSymbols( fileName, doAtEnd ) {
- var globals = { declared:{}, used:{} };
- if (!doAtEnd)
- doAtEnd = function(globals) { console.log( globals ) };
- analyzeFile( fileName,
- _getNames,
- globals,
- function() {
- doAtEnd(globals);
- });
- function _getNames(node,nodeName,obj) {
- if (node.type == "FunctionDeclaration") {
- obj.declared[node.id.name] = "f";
- return { declared:{}, used:{}, parentObj:obj } // with parentObj, we have a scope chain
- }
- if (node.type == "VariableDeclarator") {
- obj.declared[node.id.name] = "v";
- }
- if (nodeName == "key" || nodeName == "property") return obj;
- if (nodeName.match(/^params/) &&
- node.type == "Identifier") {
- obj.declared[node.name] = "p";
- }
- if (node.type == "Identifier") {
- setUsedSymbol(node.name,getScopeObjectForSymbol(node.name,obj));
- }
- return obj;
- // ----
- function setUsedSymbol(name,obj) {
- if (name in obj.used) obj.used[name]++;
- else obj.used[name] = 1;
- }
- function getScopeObjectForSymbol(name,obj) {
- while (obj) {
- if (name in obj.declared) return obj;
- obj = obj.parentObj;
- }
- return globals; // Not found - return global object
- }
- }
- }
- // If a naming convention prefixes local variables with l, the following filter finds
- // "local" symbol names used which aren't declared and hence are global erroneously
- function filterNames( symbols ) {
- console.log( Object.keys(symbols.used).filter( function(name) { return !! name.match(/^l/)}) );
- }
- getSymbols( "C:/Temp/vorbest.js") //, filterNames ); // <-- Place your javascript file here
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement