#parser.pegについて考えている。性能は至上命題なんだけど、特化しすぎるアーキテクチャにしちゃうと他と組み合わせるのが難しくなってしまう。どこでバランスを取るべきか。
#パーザがStreamを取って値(semantic value)と次の状態のStreamを返す、という構造自体は綺麗で、ビルディングブロックとしても申し分ない。パーザ単体での性能も悪くない。
#ただ、semantic valueを利用したい時にいちいちreceiveで受けるのは面倒で、そのために$doがあるんだけれど、これって本質的にはモナドのパターンなんだな。ここでコンフリクトが現れる。
#やってることがモナドなら、汎用的なモナドの仕組みを別に用意して組み合わせた方が応用範囲は広がる。しかし、現在のGaucheのアーキテクチャでは汎用的なモナドの仕組みで性能を出すのが難しい。粒度が大きい場合はさほど問題にならないんだけど、パーザの場合「一文字づつ」呼ばれる処理がたくさんあるんで、そういうtight loop内で例えばクロージャが生成されるようになっちゃうと苦しい。
#そういうわけで今のtrunkの状態では$doはクロージャ生成を避けるように(モナドのdoとは若干違うセマンティクスで)展開されるんだが、これは普通のモナドの書法とは違ってくる。似たようなものが別々のプリミティブで書かれ、しかもセマンティクスが微妙に違う、というのはやっぱり将来に禍根を残すような予感がする。
#可能性としては、(1) Gaucheのコンパイラを改良して、monadのdoセマンティクスでも余分なクロージャを極力生成しないようにするか、(2) peg自体にパーザコンビネータよりも抽象的なインタフェースを用意し、Gaucheで効率よく実行できる形に裏で変換してしまう、か。
#(1)についてはinterproceduralな解析が必要になる。プログラマにannotateして助けてもらえば楽になるけど、あまり美しくはない。
#(2)の方向は新たなDSLを導入することになるけど、ライブラリ側で最適化をかけられる機会が大幅に増えるので、性能面では有利なうえ、将来Gaucheのコンパイラが賢くなったら低レベルのパーザコンビネータと統合できるという可能性を残す。
#