77 lines
2.7 KiB
CoffeeScript
77 lines
2.7 KiB
CoffeeScript
this.exports: this unless process?
|
|
|
|
# Scope objects form a tree corresponding to the shape of the function
|
|
# definitions present in the script. They provide lexical scope, to determine
|
|
# whether a variable has been seen before or if it needs to be declared.
|
|
#
|
|
# Initialize a scope with its parent, for lookups up the chain,
|
|
# as well as the Expressions body where it should declare its variables,
|
|
# and the function that it wraps.
|
|
exports.Scope: class Scope
|
|
|
|
constructor: (parent, expressions, method) ->
|
|
[@parent, @expressions, @method]: [parent, expressions, method]
|
|
@variables: {}
|
|
@temp_var: if @parent then @parent.temp_var else '_a'
|
|
|
|
# Look up a variable in lexical scope, or declare it if not found.
|
|
find: (name) ->
|
|
return true if @check name
|
|
@variables[name]: 'var'
|
|
false
|
|
|
|
# Define a local variable as originating from a parameter in current scope
|
|
# -- no var required.
|
|
parameter: (name) ->
|
|
@variables[name]: 'param'
|
|
|
|
# Just check to see if a variable has already been declared.
|
|
check: (name) ->
|
|
return true if @variables[name]
|
|
!!(@parent and @parent.check(name))
|
|
|
|
# You can reset a found variable on the immediate scope.
|
|
reset: (name) ->
|
|
delete @variables[name]
|
|
|
|
# Find an available, short, name for a compiler-generated variable.
|
|
free_variable: ->
|
|
while @check @temp_var
|
|
ordinal: 1 + parseInt @temp_var.substr(1), 36
|
|
@temp_var: '_' + ordinal.toString(36).replace(/\d/g, 'a')
|
|
@variables[@temp_var]: 'var'
|
|
@temp_var
|
|
|
|
# Ensure that an assignment is made at the top of scope (or top-level
|
|
# scope, if requested).
|
|
assign: (name, value, top_level) ->
|
|
return @parent.assign(name, value, top_level) if top_level and @parent
|
|
@variables[name]: {value: value, assigned: true}
|
|
|
|
# Does this scope reference any variables that need to be declared in the
|
|
# given function body?
|
|
has_declarations: (body) ->
|
|
body is @expressions and @declared_variables().length
|
|
|
|
# Does this scope reference any assignments that need to be declared at the
|
|
# top of the given function body?
|
|
has_assignments: (body) ->
|
|
body is @expressions and @assigned_variables().length
|
|
|
|
# Return the list of variables first declared in current scope.
|
|
declared_variables: ->
|
|
(key for key, val of @variables when val is 'var').sort()
|
|
|
|
# Return the list of variables that are supposed to be assigned at the top
|
|
# of scope.
|
|
assigned_variables: ->
|
|
"$key = ${val.value}" for key, val of @variables when val.assigned
|
|
|
|
# Compile the string representing all of the declared variables for this scope.
|
|
compiled_declarations: ->
|
|
@declared_variables().join ', '
|
|
|
|
# Compile the string performing all of the variable assignments for this scope.
|
|
compiled_assignments: ->
|
|
@assigned_variables().join ', '
|