jashkenas--coffeescript/src/scope.coffee

81 lines
2.7 KiB
CoffeeScript

# 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.
Scope: exports.Scope: (parent, expressions, method) ->
@parent: parent
@expressions: expressions
@method: method
@variables: {}
@temp_variable: if @parent then @parent.temp_variable else '__a'
this
# Look up a variable in lexical scope, or declare it if not found.
Scope::find: (name, remote) ->
found: @check name
return found if found or remote
@variables[name]: 'var'
found
# Define a local variable as originating from a parameter in current scope
# -- no var required.
Scope::parameter: (name) ->
@variables[name]: 'param'
# Just check to see if a variable has already been declared.
Scope::check: (name) ->
return true if @variables[name]
!!(@parent and @parent.check(name))
# You can reset a found variable on the immediate scope.
Scope::reset: (name) ->
delete @variables[name]
# Find an available, short, name for a compiler-generated variable.
Scope::free_variable: ->
(@temp_variable: succ(@temp_variable)) while @check @temp_variable
@variables[@temp_variable]: 'var'
@temp_variable
# Ensure that an assignment is made at the top of scope (or top-level
# scope, if requested).
Scope::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?
Scope::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?
Scope::has_assignments: (body) ->
body is @expressions and @assigned_variables().length
# Return the list of variables first declared in current scope.
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.
Scope::assigned_variables: ->
([key, val.value] for key, val of @variables when val.assigned).sort()
Scope::compiled_declarations: ->
@declared_variables().join(', ')
Scope::compiled_assignments: ->
(t[0] + ' = ' + t[1] for t in @assigned_variables()).join(', ')
# Helper functions:
# The next character alphabetically, to produce the following string.
succ: (str) ->
str.slice(0, str.length - 1) +
String.fromCharCode(str.charCodeAt(str.length - 1) + 1)