exports.Scope = class Scope
スコープクラスは、CoffeeScript 内のレキシカルスコープを規制します。コードを生成すると、ネストされた関数本体と同じ形でスコープのツリーが作成されます。各スコープは、スコープ内で宣言された変数について認識し、その親である囲まれたスコープへの参照を持ちます。この方法で、どの変数が新しくvar
で宣言する必要があるか、どの変数が外部スコープと共有されているかを把握できます。
exports.Scope = class Scope
チェーンの検索のために親を使用してスコープを初期化し、変数を宣言する必要があるブロックノードへの参照、スコープが属する関数への参照、ソースコード内で参照されており、変数を生成するときに回避する必要がある変数のリストを使用します。また、変数宣言の一部として出力されるべきコメントも追跡します。
constructor: (@parent, @expressions, @method, @referencedVars) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@comments = {}
@positions = {}
@utilities = {} unless @parent
@root
は、特定のファイルのトップレベルのスコープオブジェクトです。
@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