exports.Scope = class Scope
Scope クラスは CoffeeScript 内の辞書的スコープを調整します。コードを生成するときに、ネストされた関数本体と同じ形状のスコープのツリーを作成します。各スコープは中で宣言された変数を知っていて、その変数を囲む親のスコープへの参照を持っています。この方法で、どの変数が新しく var
で宣言する必要があるかわかり、また、どの変数が外部のスコープと共有されているかを知ることができます。
exports.Scope = class Scope
そのチェーンをルックアップするための親と一緒にスコープを初期化し、さらに属する Block ノードへの参照、変数を宣言する場所である、さらに属する関数への参照、およびソースコードで参照していて、したがって、変数を生成するときに避ける必要のある変数のリストです。
constructor: (@parent, @expressions, @method, @referencedVars) ->
@variables = [{name: 'arguments', type: 'arguments'}]
@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'
変数がすでに宣言されているかどうかをチェックしますが、予約はせず、root スコープまで移動します。
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