静的型付き言語にする


前回、いったん終了して、静的型付き言語を作り直し…と言ったのですが、買った本を読んでいた感じですと、作り直しではなく手直しで行けそうでしたので、これまで作ったものを改造して作ってみようと思います。

本を読んでいたところ、片方の本ではコードを Java に吐き出して…ということをやっていました。結局、自分で処理系を書くより、そっちのほうがよほど実用的という感じがしています。そのため、私も最終的には C# と JavaScript (できれば C++ にも吐き出したいけど GC 作らなきゃ)に書き出すことを想定して作っていこうと思います。

文法定義

新たに、typename を導入しました。加えて、関数定義や変数宣言に型名の指定を必要としました。最終的に C# に書き出すことを考えて、型名ではジェネリクスの指定もできるようにしてあります。

本当は、C 風に

型名 変数名
の形で変数宣言できるようにしたかったのですが、現在のパーサでは、型名なのか変数名なのかが 1 トークンからでは判断できません。そのため、たとえば、クラスの定義で
int foo ~
と書いても、それが変数宣言なのか、関数定義なのかが判断できません。2 つ 3 つ先まで読めるように改造するのも手でしたが、面倒だったので、変数宣言には var を使うようにしました。
var int x = 0;
とケッタイな構文になりましたが、型名は前に書くのに慣れてしまっているため、仕方ないです(だから、TypeScript があまり好きになれない…)。ただし、for 文の中のカウンタ等を宣言する際にも毎回 2 つ書くのも面倒なため、型推論を行う auto 宣言も追加しました。といっても面倒なので、auto の場合は初期化を必須にして、その型をそのまま採用という方針にします。

<> でジェネリクスを定義しているのですが、呼び出しの際に、関数用のジェネリクスや、スタティック関数のための型名アクセスなどはできません(そもそも、まだスタティック関数作ってませんが)。というのも、variable のポストフィクスに <> をつけたいのですが、これをやってしまうと、演算子の < なのか、<> と続くのかが判断できないためです。パーサの問題です。この辺を今の LL 法のパーサでやりたい場合は、先にクラス名の定義だけをパースして、型名部分だけは字句解析のときにトークンとして型名として認識して…みたいなことをやれば対応できそうですが、私が書くプログラムではその辺は必要になりそうにないため、未対応にしています。

文法を修正後、構文木作成の部分を修正し、とりあえず型情報は無視して最低限実行はできるようにしました。

C# へ変換

型情報がついたので、ここらで一度、C# への変換を作ってみました。できたものはこちらにあります。

型はついたので、変換は簡単かと思ったのですが、C# にはグローバル変数も関数もないため、ちょっとひと工夫必要でした。SsGlobal という static クラスの static メンバに変換し、それを呼び出すようなコードにしました。ただ、これはこれで大変で、識別子がグローバルを参照しているのか、クラスのメンバを参照してるのかは、結局構文木をたどって、スコープを見て行くことになりました。

変換したコードは、以下のサポートクラスと一緒にし、変換後の SsGlobal.__ss_main メソッドを呼び出せば、無事に実行できました。

まとめ

処理系の実装は一旦休憩して、C# への変換を作りました。次は、JavaScript への変換も作ってみようと思います。おそらく、こちらの方が簡単だと思います。コード変換ができたら、CodeMirror を使って、簡単なエディタを作ってみようと思います。