exports.Scope = class Scope
Scope 类在 CoffeeScript 中管理词法作用域。在生成代码时,您会创建一个与嵌套函数体形状相同的范围树。每个范围都知道在其内声明的变量,并具有对其父封闭范围的引用。通过这种方式,我们知道哪些变量是新的,需要使用 var
声明,哪些变量与外部范围共享。
exports.Scope = class Scope
使用其父级初始化范围,以便向上查找链,以及对它所属的 Block 节点的引用,它应该在其中声明其变量,对它所属的函数的引用,以及源代码中引用的变量列表,因此在生成变量时应避免这些变量。还要跟踪应作为变量声明的一部分输出的注释。
constructor: (@parent, @expressions, @method, @referencedVars) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@comments = {}
@positions = {}
@utilities = {} unless @parent
@root
是给定文件的顶级 Scope 对象。
@root = @parent?.root ? this
添加一个新变量或覆盖现有变量。
add: (name, type, immediate) ->
return @parent.add name, type, immediate if @shared and not immediate
if Object::hasOwnProperty.call @positions, name
@variables[@positions[name]].type = type
else
@positions[name] = @variables.push({name, type}) - 1
当调用 super
时,我们需要找到当前方法的名称,以便知道如何调用父类的相同方法。如果从内部函数调用 super,这可能会变得很复杂。namedMethod
将向上遍历范围树,直到找到第一个具有填充名称的函数对象,或者到底部。
namedMethod: ->
return @method if @method?.name or !@parent
@parent.namedMethod()
在词法作用域中查找变量名称,如果它不存在,则声明它。
find: (name, type = 'var') ->
return yes if @check name
@add name, type
no
将变量名称保留为来自此范围的函数参数。内部引用不需要 var
。
parameter: (name) ->
return if @shared and @parent.check name, yes
@add name, 'param'
只需检查变量是否已声明,不保留,向上遍历到根范围。
check: (name) ->
!!(@type(name) or @parent?.check(name))
在给定索引处生成一个临时变量名称。
temporary: (name, index, single=false) ->
if single
startCode = name.charCodeAt(0)
endCode = 'z'.charCodeAt(0)
diff = endCode - startCode
newCode = startCode + index % (diff + 1)
letter = String.fromCharCode(newCode)
num = index // (diff + 1)
"#{letter}#{num or ''}"
else
"#{name}#{index or ''}"
获取变量的类型。
type: (name) ->
return v.type for v in @variables when v.name is name
null
如果我们需要存储中间结果,请找到编译器生成的变量的可用名称。_var
、_var2
等…
freeVariable: (name, options={}) ->
index = 0
loop
temp = @temporary name, index, options.single
break unless @check(temp) or temp in @root.referencedVars
index++
@add temp, 'var', yes if options.reserve ? true
temp
确保在此范围的顶部(或在请求的顶级范围)进行赋值。
assign: (name, value) ->
@add name, {value, assigned: yes}, yes
@hasAssignments = yes
此范围是否有任何声明的变量?
hasDeclarations: ->
!!@declaredVariables().length
返回在此范围中首次声明的变量列表。
declaredVariables: ->
(v.name for v in @variables when v.type is 'var').sort()
返回应该在此范围的顶部进行的赋值列表。
assignedVariables: ->
"#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned