■
まあそれはいい。今日、あれから一時間くらい考えて大体決まった。
1パス目で構文解析。このときに、ループと関数に大きめのフレーム情報をのっけとく。んで、ネストした関数からそのフレームの変数の中の変数から参照があったら、そのフレームに印をつけとく。
// 大きめのフレーム情報をframe_baseという名前にしておく。 function nanika() // 関数にframe_baseいっこ { var x; while ( 1 ) { // ループにframe_baseいっこ var y; for ( ;; ) { // このループにもframe_baseいっこ var z; function nestfunc() { return () { z+x; } } } } }
この場合だと、xとzを参照するので、
function nanika() // この関数はクロージャ。 { var x; while ( 1 ) { // これはただのループ var y; for ( ;; ) { // このループは毎回フレーム生成 var z; function nestfunc() { return () { z+x; } } } } }
ここまで1パス。
次のパスでローカル変数番号付けとコード生成。2パスでいける。後埋めするのはジャンプアドレスだけ。
変数の番号付けで注意しないといけないのは、
function nanika() { { var x; } { var y; } }
この場合は、xとyは同じ変数にしてもいいけど、
function nanika() { { var x; function () { return x; } } { var y; } }
この場合は、xとyは同じにしてはいけない。これは、適当にビットマップ持たせといて、必要になったらマークしていけば大丈夫。多分。
さらにやるとしたら、ローカル変数の生存期間ぐらい調べるとか
{ var x,y; x = ...; ... ; // ここより下ではx使わない y = ...; ... }
こうなってたら、xとyは同じにしてもいい。これをパス増やさずにそれなりに簡単に実現することって可能?できるならやってもいいと思うが。あとはネスト関数入りループの中からネスト関数で使われない変数を外に出してコピーする量減らすとか。うにゃうにゃとか、うにゃうにゃとか。(書くのが面倒になってきたらしい)
そこらへんは、まあいい。多分、今考えてる分でメモリ割り当て量はともかく、割り当て回数は大体削れるはずだ。他のスクリプト言語と比較しても負けることは無い、と思う。