無名サブルーチンの変数のキャプチャの構文解析 - SPVM開発日記

無名サブルーチンの変数のキャプチャの構文解析が可能かどうか、考えている。Java構文解析でこれを行えているので、解析方法は存在するはず。

変数のキャプチャというのは、無名サブルーチンの中から、外側の変数が見える機能のことをいいます。

my $x = 1;

my $anon_sub = sub : int ($self : self) {
  print($x);
};
$anon_sub->();

これをどう実現するのかというと、次のシンタックスシュガーにすればよい。

my $x = 1;

my $anon_sub = package {
  has x : int;
  sub : int ($self : self) {
    print($self->{x});
  }
};
$anon_sub->{x} = 1;
$anon_sub->();

外部変数の参照として見えているのだけれど、実質は、フィールドアクセスのシンタックスシュガーとなる。

問題は、どうやって構文解析するか

これは、構文解析の中でも非常に難しい。素直に解析できないパターンになっている。

無名サブルーチンの中だけで行えるようにする

まずは、話を簡単にするために、外部変数のキャプチャは、無名サブルーチンの中でだけ行えるようにする。

限定することで、余分なことを考えないで済むようにする。

キャプチャされる可能性のある変数を、無名サブルーチンに登録する

まず、キャプチャされる可能性のある変数、この例の場合は$xを、無名サブルーチンに登録する。

SPVM_LIST_push(anon_sub->capture_enable_vars, var);

無名サブルーチンが定義された位置で、見ることのできるすべての変数を登録する。この変数は、変数宣言の情報を持っており、型情報も持っている。

無名サブルーチンで、キャプチャしている情報を取得

無名サブルーチンを構文解析して、変数宣言されていないで、利用されている変数を探す。

その変数が、キャプチャリストの中にあれば、キャプチャされた外部変数だとして、登録する。

if (anon_sub->capture_enable_varsに含まれていれば) {
  SPVM_LIST_push(anon_sub->capture_vars, var);
}
フィールド宣言を追加

キャプチャされる変数は、フィールド情報として追加する。

キャプチャする変数があった場合は、生成されたオブジェクトに設定するように構文木を変更

キャプチャする変数があった場合は、生成されたオブジェクトに設定するように構文木を変更。

$anon_sub->{x} = 1;

の処理を、自動でできるように構文木に追加する。

この流れで、いけるのだろうか。それは、実際に実装してみないとわからない。

懸念点は、少なくとも、構文木解析のループが、二つ増えること。フィールドが決定できるのが、構文解析の後になること。