# 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)