ゲームを作るための言語を作っている。glslをJSにトランスパイルするglsl-transpilerがすでにあって、それをベースにすれば容易に作れそうな気がしたが、そもそも言語開発というものをしたことがないので、ソースコードを読んでもなかなか理解できず、先に進まない。
このトランスパイラは内部でglsl-parserを使ってASTを作っている。でこのparserはベースとしてはpratt parserのJS実装版(Top Down Operator Precedence)がベースとなっている。
pratt parserというのは再帰下降型パーサーの一種で、いわゆる言語構文のためのメタ言語(BNF・ENBF・PEGなど)でパーサーをジェネレートするのではなくて、手書きで一からパーサーを書くときに有効な手法である。ライブラリというよりはこう書けば楽に手書きパーサーが書けて、かつイレギュラーな言語仕様にも容易に対応させることができるという手法をまとめたものだ。もともとのTDOP Pratt の論文はパーサーの実装例はLISPで書かれていたが、それをダグラス・クロックフォードさんがJSに移植して、ステートメント(文)やスコープに対応した。その記事がTop Down Operator Precedenceである。
で結局いろいろ調べた結果、Top Down Operator Precedenceのサンプルコードをベースに作っていくことにした。WASMバイナリにするのはbinaryen.jsを使うことにした。
Top Down Operator Precedenceのサンプルコードはgithubにあるので、このレポジトリをforkして以下のレポジトリをまず作成し、コードを追加していくことにした。
https://github.com/sfpgmr/sgl2
コンパイラというのは大きくは以下の3つのコンポーネントでできている。
- ソースコードを、キーワード・演算子(トークン)に分割するトークナイザ
- トークンを解析してをAST(抽象構文木)を生成するパーサ
- ASTからオブジェクトコードを吐き出すコードジェネレータ
Top Down Operator Precedenceのサンプルコードは1.と2.のサンプルコードであり、3.までは実装されていない。またあくまでサンプルコードなので、当然のことながらトークナイザやパーサーには手を入れて実用レベルに引き上げる必要がある。またTop Down Operator Precedenceのサンプルコードがパースできるのは簡略化したJSコードだけなので、オレオレ言語の仕様に合わせた改造も必要である。
このコードに手を入れる前にTop Down Operator Precedenceを読むか、もしくは「ビューティフル・コード」を読んで内容を理解する必要がある。私は「ビューティフル・コード」の古本を買って読んだ。
パーサーのコードはparse.jsであるが、内容を読むとこれで本当にパーサーとして機能するのか?というぐらいに簡素である。キモなのはexpression()
関数である。これによって演算子の優先順位を考慮した構文木が作れるのである。
私が作ろうとしている言語は強い静的型付け言語である。つまり変数宣言はJSのようにvar
,let
,const
ではなく、変数型で明確に宣言する。なので宣言文については解析が若干複雑になる。また型エイリアスやユーザー定義型(構造体やクラス)などで新しい型を宣言できるようにするための機能も追加しなくてはならない。
いきなりは難しいので、強い静的型付け言語であるCを参考にし、コンパイルして動くものをまず作りそれを改良しながらオレオレ言語の仕様に近づけて行くことする。
で、ソースコードのパースは
- 文
- 式文
- 宣言文(初期化式含む)
- 型定義
に分けて行うことにした。最初に手を付けたのは宣言文で、今は文・式文もあわせて実装している途中である。
実装はそこそこ難航している。なにせ実験段階であるes6 modulesを使って実装しているので、デバッグ機能が不十分である。node 10.0にするとさらに不自由になりデバッガで変数の内容を表示できなくなってしまった。しょうがないので以下の検証ページを作ってデバッグはChromeのDev Toolsを使って行うことにしようと考えている。