2015-01-22 15:21:02 +00:00
"use strict" ;
var INFER = '$' ;
var GIVEN = '#' ;
var EXPECTED _MAX _NODES _PER _NONOBFUSACATED _LINE = 25 ;
var MAX _RATIO _SHORT _NAMES = 0.45 ;
var NUM _NUMBERED _LOCALS = 5 ;
function isMinified ( toplevel , code , file ) {
var numLines = code . split ( /\r\n|\r|\n/ ) . length ;
var numStatements = 0 ;
var numNames = 0 ;
var numShortNames = 0 ;
var numNumberedNames = 0 ;
toplevel . walk ( new TreeWalker ( function ( node , descend ) {
numStatements ++ ;
if ( node instanceof AST _Symbol && ! ( node instanceof AST _This ) ) {
numNames ++ ;
if ( node . name . length <= 2 && node . name != "el" && node . name != "$" ) {
numShortNames ++ ;
}
if ( node . name . length >= 2 && node . name [ 0 ] == '_' ) {
var c2 = node . name [ 1 ] ;
if ( c2 >= '0' && c2 <= '9' ) ++ numNumberedNames ;
}
}
} ) ) ;
return ( EXPECTED _MAX _NODES _PER _NONOBFUSACATED _LINE * numLines <= numStatements ) ||
( numShortNames > numNames * MAX _RATIO _SHORT _NAMES ) ||
numNumberedNames == numNames ||
numNumberedNames >= NUM _NUMBERED _LOCALS ;
}
2015-01-26 01:31:07 +00:00
function replaceMangled ( code , file , infered _names ) {
2015-01-22 15:21:02 +00:00
var toplevel ;
try {
toplevel = parseFile ( code , file ) ;
2015-01-26 01:31:07 +00:00
} catch ( ex ) {
throw new Parse _Error ( ex ) ;
2015-01-22 15:21:02 +00:00
}
extendAst ( toplevel ) ;
2015-01-26 01:31:07 +00:00
//feature_outputter.string_map defines what id is assigned to each node in the final output
//therefore to assign same ids, we need to first populate string_map by running feature extraction
2015-01-22 15:21:02 +00:00
var feature _outputter = new FeatureJsonOutputter ( ) ;
generateAstFeatures ( toplevel , feature _outputter ) ;
generateFnamesFeatures ( toplevel , feature _outputter ) ;
2015-01-25 00:50:10 +00:00
generateFscopeConstraints ( toplevel , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
2015-01-26 01:31:07 +00:00
var stream ;
if ( typeof infered _names !== 'undefined' ) {
//replace variables with inferred names
stream = OutputStream ( {
beautify : true , replace _mangled : function ( node ) {
2015-02-19 00:10:45 +00:00
var label = nodeToProperty ( node ) . toString ( ) ;
2015-01-26 14:39:51 +00:00
if ( node . definition ( ) && feature _outputter . string _map . hasId ( label ) && feature _outputter . string _map . getId ( label ) in infered _names ) {
return infered _names [ feature _outputter . string _map . getId ( label ) ] ;
} else {
return node . name ;
}
//return node.definition() ? infered_names[feature_outputter.string_map.getId("$" + node.definition().id + "-" + node.name)] : node.name;
2015-01-26 01:31:07 +00:00
}
} ) ;
} else {
//replace variables with placeholders. Using in the online demo for interactive renaming.
stream = OutputStream ( {
beautify : true , replace _mangled : function ( node ) {
2015-02-19 00:10:45 +00:00
if ( node . definition ( ) && feature _outputter . string _map . hasId ( nodeToProperty ( node ) . toString ( ) ) ) {
return "local$$" + feature _outputter . string _map . getId ( nodeToProperty ( node ) . toString ( ) ) ;
2015-01-26 14:39:51 +00:00
} else {
return node . name ;
}
//return node.definition() ? "local$$" + feature_outputter.string_map.getId("$" + node.definition().id + "-" + node.name) : node.name;
2015-01-26 01:31:07 +00:00
}
} ) ;
}
2015-01-22 15:21:02 +00:00
toplevel . print ( stream ) ;
return stream . toString ( ) ;
}
2015-01-25 14:20:50 +00:00
function Minified _Error ( message ) {
2015-01-25 11:19:30 +00:00
this . message = message ;
}
2015-01-25 14:20:50 +00:00
function Parse _Error ( ex ) {
this . message = ex . toString ( ) ;
}
2015-01-22 15:21:02 +00:00
function extractFeatures ( code , file , print _ast , features , skip _minified ) {
var toplevel ;
2015-01-25 11:19:30 +00:00
2015-01-25 14:20:50 +00:00
try {
toplevel = parseFile ( code , file ) ;
} catch ( ex ) {
throw new Parse _Error ( ex ) ;
}
2015-01-22 15:21:02 +00:00
extendAst ( toplevel ) ;
if ( print _ast ) {
return printAst ( toplevel ) ;
}
if ( skip _minified && isMinified ( toplevel , code , file ) ) {
2015-01-25 14:20:50 +00:00
throw new Minified _Error ( "Skipping minified file" ) ;
2015-01-22 15:21:02 +00:00
}
var feature _outputter = new FeatureJsonOutputter ( ) ;
feature _outputter . openElem ( ) ;
feature _outputter . openArray ( "query" ) ;
2015-01-25 14:20:50 +00:00
2015-01-22 15:21:02 +00:00
if ( features . indexOf ( "ASTREL" ) != - 1 ) {
generateAstFeatures ( toplevel , feature _outputter ) ;
}
if ( features . indexOf ( "FNAMES" ) != - 1 ) {
generateFnamesFeatures ( toplevel , feature _outputter ) ;
}
2015-01-24 23:11:19 +00:00
if ( features . indexOf ( "FSCOPE" ) != - 1 ) {
generateFscopeConstraints ( toplevel , feature _outputter ) ;
}
2015-01-22 15:21:02 +00:00
feature _outputter . closeArray ( ) ;
feature _outputter . dumpSymbols ( ) ;
feature _outputter . closeElem ( ) ;
return feature _outputter . output ;
}
/* -----[ functions ]----- */
2015-02-08 12:40:36 +00:00
function Property ( must _infer , name , annotation ) {
this . must _infer = must _infer ;
this . name = name ;
this . annotation = annotation ;
}
Property . prototype . toString = function ( ) {
return ( this . must _infer ? INFER : GIVEN ) + this . name ;
}
function nodeToProperty ( node , parent ) {
2015-01-25 14:20:50 +00:00
if ( node == null ) return null ;
2015-01-22 15:21:02 +00:00
if ( node instanceof AST _Symbol ) {
if ( node instanceof AST _This ) {
2015-02-08 12:40:36 +00:00
//return GIVEN + node.name;
return new Property ( false , node . name , "" ) ;
2015-01-22 15:21:02 +00:00
}
// AST_Symbol::unmangleable() returns true if this symbol cannot be renamed (it's either global, undeclared, or defined in scope where eval or with are in use.
if ( node . unmangleable ( ) ) {
2015-02-08 12:40:36 +00:00
//return GIVEN + node.name;
return new Property ( false , node . name , "" ) ;
2015-01-22 15:21:02 +00:00
}
2015-02-08 12:40:36 +00:00
//return INFER + node.definition().id + "-" + node.name;
return new Property ( true , node . definition ( ) . id + "-" + node . name , "" ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Constant ) {
2015-02-08 12:40:36 +00:00
//var name = GIVEN + String(node.value).slice(0,64);
//name.annotation = "!" + nodeType(node) + "!";
//return name;
return new Property ( false , String ( node . value ) . slice ( 0 , 64 ) , nodeType ( node ) ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Sub ) {
//x[1], x -> expression, 1 -> property
2015-02-08 12:40:36 +00:00
if ( nodeToProperty ( node . expression , node ) == null ) {
return null ;
}
var prop = nodeToProperty ( node . expression , node ) ;
prop . annotation += "[]" ;
return prop ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _PropAccess ) {
2015-02-08 12:40:36 +00:00
//return GIVEN + node.property;
return new Property ( false , node . property , "" ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Defun ) {
//function foo(...) { ... }
2015-02-08 12:40:36 +00:00
return nodeToProperty ( node . name , node ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _VarDef ) {
// var x = function () { ... }
2015-02-08 12:40:36 +00:00
return nodeToProperty ( node . name , node ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Assign ) {
//x = function () { ... }
2015-02-08 12:40:36 +00:00
return nodeToProperty ( node . left , node ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _ObjectProperty ) {
// { "x" : function () { ... } }
2015-02-08 12:40:36 +00:00
//return GIVEN + node.key;
return new Property ( false , node . key , "" ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Call ) {
//x.foo( function () { ... } )
//foo( function () { ... } )
2015-02-08 12:40:36 +00:00
return nodeToProperty ( node . expression , node ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Lambda ) {
if ( node . parent instanceof AST _Call ) {
//'node.parent.expression != node' as lambda can call itself
2015-01-25 14:20:50 +00:00
if ( node . parent . expression == node ) {
return null ;
}
2015-02-08 12:40:36 +00:00
if ( nodeToProperty ( node . parent . expression , node ) == null ) {
return null ;
}
//var name = nodeToProperty(node.parent.expression, node);
//name.annotation = "(" + node.child_id + ")";
//return name;
var prop = nodeToProperty ( node . parent . expression , node ) ;
prop . annotation += "(" + node . child _id + ")" ;
return prop ;
2015-01-25 14:20:50 +00:00
}
if ( node . parent != parent ) {
2015-02-08 12:40:36 +00:00
return nodeToProperty ( node . parent , node ) ;
2015-01-25 14:20:50 +00:00
}
2015-01-22 15:21:02 +00:00
}
return null ;
}
function nodeType ( node ) {
if ( node instanceof AST _Binary || node instanceof AST _Unary ) {
return node . _ _proto _ _ . TYPE + node . operator ;
} else if ( node instanceof AST _Boolean ) {
return "Bool" ;
} else if ( node instanceof AST _Atom && ! ( node instanceof AST _Constant ) ) {
//atoms are special constant values as Nan, Undefined, Infinity,..
return "Atom" ;
}
return node . _ _proto _ _ . TYPE ;
}
function pathToStringFw ( path , start ) {
var res = "" ;
for ( var i = start ; i < path . length - 1 ; i ++ ) {
res += nodeType ( path [ i ] ) ;
res += "[" + path [ i + 1 ] . child _id + "]" ;
}
return res ;
}
function pathToStringBw ( path , start ) {
2015-02-19 00:10:45 +00:00
var res = "[" + path [ path . length - 1 ] . child _id + "]" ;
2015-01-22 15:21:02 +00:00
for ( var i = path . length - 2 ; i >= start ; i -- ) {
res += nodeType ( path [ i ] ) ;
2015-02-19 00:10:45 +00:00
res += "[" + path [ i ] . child _id + "]" ;
2015-01-22 15:21:02 +00:00
}
return res ;
}
function printAst ( toplevel ) {
var output = "" ;
var walker = new TreeWalker ( function ( node ) {
2015-01-26 14:39:51 +00:00
output += string _template ( ' node{id} [label="{label}"];\n' , {
2015-01-22 15:21:02 +00:00
id : node . id ,
label : nodeType ( node )
} ) ;
if ( walker . parent ( ) != null ) {
2015-01-26 14:39:51 +00:00
output += string _template ( ' node{id1} -> node{id2} [weight=1];\n' , {
2015-01-22 15:21:02 +00:00
id1 : walker . parent ( ) . id ,
id2 : node . id
} ) ;
}
} ) ;
output += "digraph AST {\n" ;
toplevel . walk ( walker ) ;
output += "}\n" ;
return output ;
}
function generateAstFeatures ( toplevel , feature _outputter ) {
var walker = new TreeWalker ( function ( node ) {
2015-02-08 12:40:36 +00:00
// console.log(nodeType(node) + " - " + nodeToProperty(node));
2015-01-22 15:21:02 +00:00
var paths = this . node _finder . find ( node ) ;
for ( var i = 0 ; i < paths . length ; i ++ ) {
var path1 = paths [ i ] ;
var node1 = path1 [ path1 . length - 1 ] ;
for ( var j = i + 1 ; j < paths . length ; j ++ ) {
var common _prefix _len = 0 ;
var path2 = paths [ j ] ;
var node2 = path2 [ path2 . length - 1 ] ;
//determine common prefix to be skipped
while ( common _prefix _len < path1 . length && common _prefix _len < path2 . length
&& path1 [ common _prefix _len ] === path2 [ common _prefix _len ] ) {
common _prefix _len ++ ;
}
if ( common _prefix _len == 0 ) {
throw "common prefix not greater than 0!" ;
}
feature _outputter . addFeature (
2015-02-08 12:40:36 +00:00
nodeToProperty ( node1 ) ,
nodeToProperty ( node2 ) ,
2015-01-24 23:11:59 +00:00
//pathToStringBw(path1, common_prefix_len) + ":" + nodeType(path1[common_prefix_len - 1]) + ":" + pathToStringFw(path2, common_prefix_len)
( path2 . length != common _prefix _len )
? pathToStringBw ( path1 , common _prefix _len ) + ":" + pathToStringFw ( path2 , common _prefix _len - 1 )
: pathToStringBw ( path2 , common _prefix _len ) + ":" + pathToStringFw ( path1 , common _prefix _len - 1 )
2015-01-22 15:21:02 +00:00
) ;
2015-01-24 23:11:59 +00:00
2015-01-22 15:21:02 +00:00
}
}
} ) ;
walker . node _finder = new NodePathFinder ( 3 , function ( node ) {
return ( node instanceof AST _Symbol || node instanceof AST _Constant || node instanceof AST _PropAccess ) ;
} ) ;
toplevel . walk ( walker ) ;
}
function addFeatures ( lhss , lhs _label , rhs , rhs _label , feature _outputter ) {
var prefix = "" ;
for ( var i = lhss . length - 1 ; i >= 0 ; i -- ) {
prefix += lhs _label ;
feature _outputter . addFeature ( lhss [ i ] , rhs , prefix + rhs _label ) ;
}
}
2015-01-24 23:11:19 +00:00
function addScopeConstraints ( node , toplevel , feature _outputter ) {
feature _outputter . beginScope ( ) ;
2015-02-08 12:40:36 +00:00
var name = nodeToProperty ( node ) ;
2015-01-24 23:11:19 +00:00
if ( name != null )
feature _outputter . addToScope ( name ) ;
for ( var i = 0 ; i < node . enclosed . length ; i ++ ) {
2015-02-08 12:40:36 +00:00
feature _outputter . addToScope ( nodeToProperty ( node . enclosed [ i ] . orig [ 0 ] ) ) ;
2015-01-24 23:11:19 +00:00
}
node . variables . each ( function ( symbol ) {
2015-02-08 12:40:36 +00:00
feature _outputter . addToScope ( nodeToProperty ( symbol . orig [ 0 ] ) ) ;
2015-01-24 23:11:19 +00:00
} ) ;
toplevel . globals . each ( function ( symbol ) {
2015-02-08 12:40:36 +00:00
feature _outputter . addToScope ( nodeToProperty ( symbol . orig [ 0 ] ) ) ;
2015-01-24 23:11:19 +00:00
} ) ;
feature _outputter . endScope ( ) ;
}
function generateFscopeConstraints ( toplevel , feature _outputter ) {
addScopeConstraints ( toplevel , toplevel , feature _outputter ) ;
toplevel . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _Defun || node instanceof AST _Lambda ) {
addScopeConstraints ( node , toplevel , feature _outputter ) ;
}
} ) ) ;
}
2015-01-22 15:21:02 +00:00
function generateFnamesFeatures ( toplevel , feature _outputter ) {
var outer _funcs = [ ] ;
toplevel . walk ( new TreeWalker ( function ( node , descend ) {
2015-02-08 12:40:36 +00:00
if ( ( node instanceof AST _Defun || node instanceof AST _Lambda ) && nodeToProperty ( node ) != null ) {
var name = nodeToProperty ( node ) ;
2015-01-22 15:21:02 +00:00
for ( var i = 0 ; i < node . argnames . length ; i ++ ) {
2015-02-08 12:40:36 +00:00
addFeatures ( [ name ] , "FN" , nodeToProperty ( node . argnames [ i ] ) , "PAR" , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
}
outer _funcs . push ( name ) ;
descend ( ) ; //traverse childs
outer _funcs . pop ( ) ;
return true ; //do not traverse childs again
}
if ( node instanceof AST _New ) {
2015-02-08 12:40:36 +00:00
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node ) , "NEW" , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Call ) {
2015-02-08 12:40:36 +00:00
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node ) , "CALL" , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Constant ) {
2015-02-08 12:40:36 +00:00
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node ) , nodeType ( node ) . toUpperCase ( ) , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _VarDef ) {
2015-02-08 12:40:36 +00:00
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node . name ) , "DECL" , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
} else if ( node instanceof AST _Dot && ! ( node . parent instanceof AST _Call ) ) {
2015-02-08 12:40:36 +00:00
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node ) , "PROP" , feature _outputter ) ;
} else if ( node instanceof AST _Return && nodeToProperty ( node . value ) != null ) {
addFeatures ( outer _funcs , "FN" , nodeToProperty ( node . value ) , "RETURN" , feature _outputter ) ;
2015-01-22 15:21:02 +00:00
}
} ) ) ;
}
/* -----[ NodePathFinder ]----- */
function NodePathFinder ( max _depth , filter ) {
this . max _depth = max _depth ;
this . paths = [ ] ;
this . filter = filter ;
}
NodePathFinder . prototype = new TreeWalker ( function ( node , descend ) {
if ( this . stack . length > this . max _depth || node instanceof AST _Defun ) {
return true ;
}
//enforce in-order traversal
//otherwise we get for "x.foo()" feature foo - x instead of x - foo as x is a parent of foo in the AST
descend ( ) ;
if ( this . filter ( node ) ) {
this . paths . push ( this . stack . slice ( 0 ) ) ;
}
return true ;
} ) ;
NodePathFinder . prototype . find = function ( node ) {
this . root = node ;
this . paths = [ ] ;
node . walk ( this ) ;
return this . paths ;
} ;
/* ---[ JsonOutputter ]--- */
function FeatureJsonOutputter ( ) {
2015-01-26 14:39:51 +00:00
this . string _map = new StringMap ( ) ;
2015-01-22 15:21:02 +00:00
this . first _element = true ;
this . output = "" ;
this . depth = 0 ;
this . pairs = { } ;
2015-01-24 23:11:19 +00:00
this . cur _scope = { } ;
2015-01-25 00:50:10 +00:00
this . has _features = false ;
2015-01-22 15:21:02 +00:00
}
FeatureJsonOutputter . prototype . indent = function ( ) {
var res = "" ;
for ( var i = 0 ; i < this . depth ; i ++ ) {
res += " " ;
}
return res ;
} ;
FeatureJsonOutputter . prototype . openElem = function ( ) {
if ( ! this . first _element ) {
this . output += "," ;
}
this . output += "\n" + this . indent ( ) + "{" ;
this . first _element = true ;
this . depth ++ ;
} ;
FeatureJsonOutputter . prototype . closeElem = function ( ) {
this . depth -- ;
this . output += "}" ;
this . first _element = false ;
} ;
FeatureJsonOutputter . prototype . openArray = function ( name ) {
if ( ! this . first _element ) {
this . output += "," ;
}
this . output += "\n" + this . indent ( ) + "\"" + name + "\":[" ;
this . first _element = true ;
this . depth ++ ;
} ;
FeatureJsonOutputter . prototype . closeArray = function ( ) {
this . depth -- ;
this . output += "\n" + this . indent ( ) + "]" ;
this . first _element = false ;
} ;
FeatureJsonOutputter . prototype . visitFeature = function ( a _id , b _id , name ) {
if ( ! ( a _id in this . pairs ) ) {
this . pairs [ a _id ] = [ ] ;
}
var visited = this . pairs [ a _id ] ;
if ( visited . indexOf ( b _id + "-" + name ) >= 0 ) {
return true ;
}
visited . push ( b _id + "-" + name ) ;
return false ;
} ;
FeatureJsonOutputter . prototype . addFeature = function ( a , b , name ) {
if ( a == null || b == null ) {
return ;
}
//do not add features between two fixed nodes
2015-02-08 12:40:36 +00:00
//if (a[0] == GIVEN && b[0] == GIVEN) {
if ( ! a . must _infer && ! b . must _infer ) {
2015-01-22 15:21:02 +00:00
return ;
}
2015-02-08 12:40:36 +00:00
if ( a . annotation != "" ) {
name = a . annotation + "-" + name ;
}
if ( b . annotation != "" ) {
name = name + "-" + b . annotation ;
}
var a _id = this . string _map . getId ( a . toString ( ) ) ;
var b _id = this . string _map . getId ( b . toString ( ) ) ;
2015-01-22 15:21:02 +00:00
if ( a _id == b _id || this . visitFeature ( a _id , b _id , name ) ) {
return ;
}
2015-01-25 00:50:10 +00:00
this . has _features = true ;
2015-01-22 15:21:02 +00:00
this . openElem ( ) ;
this . output += '"a": ' + a _id + "," ;
this . output += '\t"b": ' + b _id + "," ;
this . output += '\t"f2": "' + name + '"' ;
this . closeElem ( ) ;
} ;
FeatureJsonOutputter . prototype . addSymbol = function ( key ) {
this . openElem ( ) ;
this . output += '"v": ' + this . string _map . getId ( key ) + "," ;
if ( key [ 0 ] == INFER ) {
//${id}-{name}
this . output += '\t"inf": "' + escapeString ( key . split ( "-" ) [ 1 ] ) + '"' ;
} else {
//#{name}
this . output += '\t"giv": "' + escapeString ( key . slice ( 1 ) ) + '"' ;
}
this . closeElem ( ) ;
} ;
FeatureJsonOutputter . prototype . dumpSymbols = function ( ) {
2015-01-25 00:50:10 +00:00
if ( ! this . has _features ) {
this . openArray ( "assign" ) ;
this . closeArray ( ) ;
return ;
}
2015-01-22 15:21:02 +00:00
this . openArray ( "assign" ) ;
// var keys = Object.keys( this.string_map.map );
var keys = this . string _map . keys ;
for ( var i = 0 , length = keys . length ; i < length ; i ++ ) {
this . addSymbol ( keys [ i ] ) ;
}
this . closeArray ( ) ;
} ;
2015-01-24 23:11:19 +00:00
FeatureJsonOutputter . prototype . beginScope = function ( ) {
this . cur _scope = { } ;
} ;
FeatureJsonOutputter . prototype . addToScope = function ( a ) {
var a _id = this . string _map . getId ( a ) ;
this . cur _scope [ a _id ] = true ;
} ;
FeatureJsonOutputter . prototype . endScope = function ( ) {
//{"cn":"!=","n":[14,366,370,372,108,40,356]}
2015-01-25 00:50:10 +00:00
if ( ! this . has _features ) {
return ;
}
2015-01-24 23:11:19 +00:00
var keys = Object . keys ( this . cur _scope ) ;
if ( keys . length <= 1 ) {
return ;
}
this . openElem ( ) ;
this . output += '"cn":"!=", "n":[' ;
this . output += keys [ 0 ] ;
for ( var i = 1 , length = keys . length ; i < length ; i ++ ) {
this . output += ',' ;
this . output += keys [ i ] ;
}
this . output += "]" ;
this . closeElem ( ) ;
} ;
2015-01-22 15:21:02 +00:00
/* -----[ StringMap ]----- */
2015-01-26 14:39:51 +00:00
function StringMap ( ) {
2015-01-22 15:21:02 +00:00
this . map = { } ;
this . current _id = 0 ;
this . keys = [ ] ;
}
2015-01-26 14:39:51 +00:00
StringMap . prototype . hasId = function ( input ) {
if ( input == null ) {
throw new Error ( "error null" ) ;
}
//we add a special character in from to allow for keys such as "toString"
2015-02-08 12:40:36 +00:00
var escaped _input = "#" + input . toString ( ) ;
2015-01-26 14:39:51 +00:00
return escaped _input in this . map ;
} ;
2015-01-22 15:21:02 +00:00
StringMap . prototype . getId = function ( input ) {
if ( input == null ) {
throw new Error ( "error null" ) ;
}
2015-02-08 12:40:36 +00:00
input = input . toString ( ) ;
2015-01-22 15:21:02 +00:00
//we add a special character in from to allow for keys such as "toString"
var escaped _input = "#" + input ;
if ( ! ( escaped _input in this . map ) ) {
this . map [ escaped _input ] = this . current _id ;
//keep ordered map of keys for iterating later
this . keys . push ( input ) ;
this . current _id ++ ;
}
return this . map [ escaped _input ] ;
} ;
/* ------------------------ */
2015-01-25 14:20:50 +00:00
function escapeString ( input ) {
2015-01-25 15:37:14 +00:00
try {
return encodeURIComponent ( input ) ;
} catch ( ex ) {
throw new Parse _Error ( " unable to encode '" + input . replace ( /^!(String|Number|RegExp|Atom|Bool')!/ , '' ) + "'" ) ;
}
2015-01-22 15:21:02 +00:00
}
2015-01-29 11:29:11 +00:00
function unescapeString ( input ) {
return decodeURIComponent ( input ) ;
}
2015-01-22 15:21:02 +00:00
function parseFile ( code , file ) {
var toplevel = parse ( code , {
filename : file
} ) ;
toplevel . figure _out _scope ( ) ;
return toplevel ;
}
function extendAst ( root ) {
var current _id = 0 ;
var walker = new TreeWalker ( function ( node ) {
if ( ! node . hasOwnProperty ( "id" ) ) {
node . id = current _id ;
current _id += 1 ;
}
if ( ! node . hasOwnProperty ( "parent" ) ) {
node . parent = walker . parent ( ) ;
}
node . num _childs = 0 ;
node . child _id = 0 ;
if ( walker . parent ( ) !== undefined ) {
node . child _id = walker . parent ( ) . num _childs ;
walker . parent ( ) . num _childs ++ ;
}
if ( node instanceof AST _Symbol ) {
if ( node . definition ( ) != null ) {
node . definition ( ) . id = current _id ;
current _id ++ ;
}
}
} ) ;
root . walk ( walker ) ;
}