パーサの次

ytqwerty2008-06-07

ACATSのテストを通せるパーサができたので、次を考えます。目標はYT独自拡張ばりばりのAda2005コンパイラgccに煩わされること無く好き勝手に弄れるやつ。32/64bit、Windows/Mac/BSDあたりに全対応できたらいいなあぐらいの夢想で。どう考えても力尽きますのでとりあえず32bit Windowsがターゲットです。
パーサの次にやることとしては、意味解析及び中間ファイルフォーマットへの変換です。.oのようなものは問題外です。中間ファイルがあれば元ソース要らないぐらいじゃないと。コンパイラがmakeツール兼ねるようになったとき依存関係辿るのにいちいち全ソースをパースして回るのも頭悪いですし。Delphiの.dcuやOCamlの.cmiみたいな形式を持てない限り、まともなモジュール機能のあるコンパイラとは呼びたくないです。
QueenのときはD言語でしたのでやたら苦労した記憶があるのですが、ふふふ、今回は開発言語をコンパイラ書くのに向いていると定評のあるOCamlにしたのですよ。開発言語とターゲット言語が異なる最大のメリットとして、セルフコンパイルは想定しなくてOKすなわち中間ファイル形式にはOCamlのMarshalを堂々と使うことができます。
Marshalを使うデメリットとして、他のモジュールを参照するときは、直接ポインタ(OCamlだと値との区別がないですが)で参照せずに、テーブル作ってインデックスの形で参照を持っておく必要がありますね。じゃないと参照先モジュールまで一緒に保存してしまいます。中間ファイルはソースコード毎に作りたいので。
あと、白昼夢だけは大きくグローバル最適化したいので、中間ファイルの段階では機械語にはせずに3番地文とかSSA形式とかで。
以上の基本方針で、Adaの中間ファイルとしてはどのようなものが必要か考えます。
ソースファイルから別のソースファイルを参照する機能は、こんなとこでしょうか。

  • with
  • 子package
  • separate
  • specとbodyを別ファイルに置く(.ads/.adb)

作ってあるパーサは、ACATSのテストをそのまま通すため、複数の翻訳単位をひとつのソースコードに詰め込むことができます。Adaではgenericのインスタンス化で2行だけのソースコードを量産したりしますので、ACATSのために限らず純粋にできると嬉しいです。加えて、separateと、.ads/.adb分離機能(私としてはあまり好きではないのですが、既存のAdaコンパイラ向けに書いたソースを曲がりなりにも使うことを考えたら必須)を考えたら、翻訳単位とソースコードが多対多で対応するカオスな状況に。
withに書かれた名前から対応するソースコードを見つけられることは必須ですので、ひとつのソースファイルが複数の翻訳単位を公開するのは若干無理があります。それこそコンパイル時に言語内の名前とソースファイルとの対応をどこかに保存するのでない限り。(←ネタ盗用ごめんなさい)
ということで、複数の翻訳単位をひとつのソースコードに詰め込んだ場合も、他のソースコードから参照できるのは、最後の一個だけとします。後はソースファイル内private。
ということは、同名の翻訳単位を捌けないといけないことになります。
それから、.ads/.adb分離と、separateは……ま、これは、一括でコンパイルして、中間ファイルとしては一個でいいんじゃないかなあ……includeみたいなものですよと日和る。
これで、中間ファイル:公開翻訳単位:ソースファイルが1:1:nと、まあまあシンプルにできるのではないでしょうか。
あと気をつける必要がありそうなのは……limited withしたら循環参照できる。循環参照しているソースコードは常に同時にコンパイルしないといけないなんてのも辛いので……幸い、limited viewでは、型名の識別子と子packageだけが見えるという制限があるので……いやしかし、こんなケースも?

limited with B;
package A is
   type P is access B.D.T;
end;
with C;
package B is
   package D renames C;
end;
package D is
   type T is null record;
end;

こんなケースも。

limited with F;
package E is
   package G renames F;
end;
limited with E;
package F is
   package H renames E;
   package I renames E.G;
end;

D言語どうやってるのでしょうね。
ええと、limited with先は毎回パースして……limited with先のwith先もパースして……それらはlimited viewに対応したlimited中間表現にする。limited中間表現は適当にlimited意味解析される。
ええと……ひとつのソースをコンパイルする流れとしては、最初にASTをlimited中間表現(識別子未解決)にする、with先の完全中間表現を読む無ければ先にコンパイルする、limited with先をlimited中間表現(識別子未解決)にする、全てのlimited中間表現(識別子未解決)をlimited中間表現(識別子解決済み)にする、ターゲットのソースを完全中間表現にする……でいいのでしょうか?
limited中間表現はファイルには残さないとして、いや残してもいいのか。あとlimited中間表現ではaccess TしたときTによってaccess Tの内部表現も変わるので、型の種類とpragma、for...useぐらいは見ないといけない。
中間ファイルを考えることはビルドルールを考えることなんだなあと改めて。