simple visitor API and code to figure out scope and references

This commit is contained in:
Mihai Bazon
2012-08-19 15:57:50 +03:00
parent 4488758d48
commit 6c35135ace
6 changed files with 474 additions and 102 deletions

88
lib/scope.js Normal file
View File

@@ -0,0 +1,88 @@
AST_Scope.DEFMETHOD("figure_out_scope", function(){
// step 1: handle definitions
var scope = null;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Scope) {
var save_scope = node.parent_scope = scope;
scope = node;
descend.call(node);
scope = save_scope;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
return;
}
if (node instanceof AST_SymbolDeclaration && !scope.parent_scope) {
node.global = true;
}
if (node instanceof AST_SymbolVar) {
scope.def_variable(node);
}
else if (node instanceof AST_SymbolLambda) {
scope.def_function(node);
}
else if (node instanceof AST_SymbolDefun) {
scope.parent_scope.def_function(node);
}
else if (node instanceof AST_Label) {
scope.def_label(node);
}
else if (node instanceof AST_SymbolCatch) {
// XXX: this is wrong according to ECMA-262 (12.4). the
// `catch` argument name should be visible only inside the
// catch block. For a quick fix AST_Catch should inherit
// from AST_Scope.
scope.def_variable(node);
}
else if (node instanceof AST_SymbolRef) {
node.scope = scope;
}
});
this.walk(tw);
// step 2: find back references and eval/with
var tw = new TreeWalker(function(node){
if (node instanceof AST_LabelRef) {
var sym = node.scope.find_label(node);
if (!sym) throw new Error("Undefined label " + node.name);
node.reference(sym);
}
else if (node instanceof AST_SymbolRef) {
var sym = node.scope.find_variable(node);
if (!sym) {
if (node.name == "eval") {
for (var s = scope; s; s = s.parent_scope)
s.uses_eval = true;
}
} else {
node.reference(sym);
}
}
});
this.walk(tw);
});
AST_Scope.DEFMETHOD("find_variable", function(name){
if (name instanceof AST_Symbol) name = name.name;
return this.variables[name] ||
(this.name && this.name.name == name && this.name) ||
(this.parent_scope && this.parent_scope.find_variable(name));
});
AST_Scope.DEFMETHOD("find_label", function(name){
if (name instanceof AST_Symbol) name = name.name;
return this.labels[name];
});
AST_Scope.DEFMETHOD("def_function", function(symbol){
this.def_variable(symbol);
this.functions[symbol.name] = symbol;
symbol.scope = this;
});
AST_Scope.DEFMETHOD("def_variable", function(symbol){
this.variables[symbol.name] = symbol;
delete this.functions[symbol.name];
symbol.scope = this;
});
AST_Scope.DEFMETHOD("def_label", function(symbol){
this.labels[symbol.name] = symbol;
symbol.scope = this;
});