scope: refactored

This commit is contained in:
satyr 2010-10-20 17:13:43 +09:00
parent 8d6b909b93
commit b0a4b7ab85
2 changed files with 35 additions and 38 deletions

View File

@ -27,29 +27,28 @@
Scope.root = null;
Scope.prototype.setVar = function(name, type) {
if (this.positions.hasOwnProperty(name)) {
return (this.variables[this.positions[name]].type = type);
this.variables[this.positions[name]].type = type;
} else {
this.positions[name] = this.variables.length;
return this.variables.push({
this.positions[name] = -1 + this.variables.push({
name: name,
type: type
});
}
return this;
};
Scope.prototype.startLevel = function() {
return this.garbage.push([]);
this.garbage.push([]);
return this;
};
Scope.prototype.endLevel = function() {
var _i, _len, _ref2, _result, garbage, vars;
vars = this.variables;
_result = [];
var _i, _len, _ref2, name;
for (_i = 0, _len = (_ref2 = this.garbage.pop()).length; _i < _len; _i++) {
garbage = _ref2[_i];
if (this.type(garbage) === 'var') {
_result.push(this.setVar(garbage, 'reuse'));
name = _ref2[_i];
if (this.type(name) === 'var') {
this.setVar(name, 'reuse');
}
}
return _result;
return this;
};
Scope.prototype.find = function(name, options) {
if (this.check(name, options)) {
@ -62,7 +61,7 @@
var _i, _len, _ref2, v;
for (_i = 0, _len = (_ref2 = this.variables).length; _i < _len; _i++) {
v = _ref2[_i];
if (fn(v.name, v.type)) {
if (fn(v)) {
return true;
}
}
@ -79,8 +78,8 @@
}
return !!(((_ref2 = this.parent) != null) ? _ref2.check(name) : undefined);
};
Scope.prototype.temporary = function(type, index) {
return type.length > 1 ? '_' + type + (index > 1 ? index : '') : '_' + (index + parseInt(type, 36)).toString(36).replace(/\d/g, 'a');
Scope.prototype.temporary = function(name, index) {
return name.length > 1 ? '_' + name + (index > 1 ? index : '') : '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a');
};
Scope.prototype.type = function(name) {
var _i, _len, _ref2, v;
@ -93,15 +92,13 @@
return null;
};
Scope.prototype.freeVariable = function(type) {
var index, temp;
var _ref2, index, temp;
index = 0;
while (this.check(temp = this.temporary(type, index)) && this.type(temp) !== 'reuse') {
index++;
}
this.setVar(temp, 'var');
if (this.garbage.length) {
last(this.garbage).push(temp);
}
(((_ref2 = last(this.garbage)) != null) ? _ref2.push(temp) : undefined);
return temp;
};
Scope.prototype.assign = function(name, value) {
@ -111,13 +108,14 @@
});
};
Scope.prototype.hasDeclarations = function(body) {
return body === this.expressions && this.any(function(k, val) {
return (val === 'var' || val === 'reuse');
return body === this.expressions && this.any(function(v) {
var _ref2;
return ((_ref2 = v.type) === 'var' || _ref2 === 'reuse');
});
};
Scope.prototype.hasAssignments = function(body) {
return body === this.expressions && this.any(function(k, val) {
return val.assigned;
return body === this.expressions && this.any(function(v) {
return v.type.assigned;
});
};
Scope.prototype.declaredVariables = function() {

View File

@ -21,7 +21,7 @@ exports.Scope = class Scope
@variables = [{name: 'arguments', type: 'arguments'}]
@positions = {}
if @parent
@garbage = @parent.garbage
{@garbage} = @parent
else
@garbage = []
Scope.root = this
@ -31,19 +31,19 @@ exports.Scope = class Scope
if @positions.hasOwnProperty name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.length
@variables.push {name, type}
@positions[name] = -1 + @variables.push {name, type}
this
# Create a new garbage level
startLevel: ->
@garbage.push []
this
# Return to the previous garbage level and erase referenced temporary
# variables in current level from scope.
endLevel: ->
vars = @variables
for garbage in @garbage.pop() when @type(garbage) is 'var'
@setVar garbage, 'reuse'
@setVar name, 'reuse' for name in @garbage.pop() when @type(name) is 'var'
this
# Look up a variable name in lexical scope, and declare it if it does not
# already exist.
@ -52,10 +52,9 @@ exports.Scope = class Scope
@setVar name, 'var'
false
# Test variables and return true the first time fn(v, k) returns true
# Test variables and return `true` the first time `fn(v)` returns `true`
any: (fn) ->
for v in @variables when fn v.name, v.type
return true
for v in @variables when fn v then return true
return false
# Reserve a variable name as originating from a function parameter for this
@ -71,11 +70,11 @@ exports.Scope = class Scope
!!@parent?.check name
# Generate a temporary variable name at the given index.
temporary: (type, index) ->
if type.length > 1
'_' + type + if index > 1 then index else ''
temporary: (name, index) ->
if name.length > 1
'_' + name + if index > 1 then index else ''
else
'_' + (index + parseInt type, 36).toString(36).replace /\d/g, 'a'
'_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'
# Gets the type of a variable.
type: (name) ->
@ -88,7 +87,7 @@ exports.Scope = class Scope
index = 0
index++ while @check(temp = @temporary type, index) and @type(temp) isnt 'reuse'
@setVar temp, 'var'
last(@garbage).push temp if @garbage.length
last(@garbage)?.push temp
temp
# Ensure that an assignment is made at the top of this scope
@ -99,12 +98,12 @@ exports.Scope = class Scope
# Does this scope reference any variables that need to be declared in the
# given function body?
hasDeclarations: (body) ->
body is @expressions and @any (k, val) -> val in ['var', 'reuse']
body is @expressions and @any (v) -> v.type in ['var', 'reuse']
# Does this scope reference any assignments that need to be declared at the
# top of the given function body?
hasAssignments: (body) ->
body is @expressions and @any (k, val) -> val.assigned
body is @expressions and @any (v) -> v.type.assigned
# Return the list of variables first declared in this scope.
declaredVariables: ->