エンベデット受かってたよ!やたー。
プリンスオブペルシャ、2Dゲーで難易度高いとのことであえてPSP版を買ってみた。360版も買おうと思ったけど合わせて1万越えはちょっとね…。
半分くらいやったけど、まあまあかな。右に進むだけのアクションゲーなので気軽だがボリューム感は少なめ。難易度はそんなに高いとは思えないけど。操作でちょっといらいらするところがあるくらい。さっくり死ぬけど。
たしかに3Dのやつを2Dに上手く落としました的な感じではあるんだが、せっかくなのでクラシックなプリンスオブペルシャみたいに探索要素を入れても良かったんじゃないかな。おかね集めのために探索する要素はあるんだけど、探索としては弱いなぁ。
3Dのやつもそうなんだけど、基本的に仕掛けがその部屋単位で完結してるのが残念。あとはただのアクションゲーム。まあそう考えると2Dで問題ないかもと思えてくる。
あとPSP版ストーリー適当すぎるだろwwwwww
そんなわけでcitrusいじりはじめたんだけど、Treetopに比べてついこのあいだ出来たばっかりのようなのでいろいろと問題が。
treetopファイルとcitrusファイルはほぼ同じっぽいのでそのまま読み込ませたら、「または」の記述が/じゃなくて|になってた。citrusは正規表現リテラルが使えるからだね。
それは直したんだけどよくわからない所でエラーが。treetopなら何行目で○○と××が期待されてるのに無いよーというエラーを出してくれるんだけど、citrusだと何文字目のこんな文字列のあたりでエラー!としか言わない。意味わかんねーよ。
見てても何を間違ってるんだか全然わかんねーんだが、どうもendif_directiveのルールを使ってるところでエラーになっているようだ。
citrusの実装を見にいくと文法定義間違ってんじゃねーか!
rule :end_keyword, [ 'end', :space ]
こんな感じのendキーワード定義があるんだけど、:spaceには0以上の空白にマッチするルールが別に書いてある。ということは"endif_directive"の先頭もend_keywordにマッチするわけですね。
たぶんより正しそうなのはこう。
rule :end_keyword, [ 'end', notp(/[a-zA-Z0-9_]+/), :space ]
endの後ろに識別子の一部になる文字が続かない場合のみendになるようにした。もっといい書き方あるかもしれんけど今の俺ではこの程度。
これで通ったぜー。
しかしcitrusファイルをそのまま読み込むと、強制的にトップレベルにモジュール定義するし、トップレベルからしか各モジュールを検索してくれないのね。ひどい実装だ。
というかtreetopと違って、citrusファイルはおまけっぽい感じがするなぁ。基本的にはRubyでDSLっぽい書き方をするのが想定されてそうだ。
じゃあなおさらcitrusファイルからRubyコードへの変換器が欲しいじゃねーか。ということでcitrus/fileをコピペして作ってみた。作ってみたが読めるもんじゃねーソースが生成されたな…。
citrusファイル上で綺麗に見える書き方とRuby上で綺麗に見える書き方が違うからだなぁ。まあ変換器は作ったしとりあえずcitrusファイル使えばいいかー。
あとはやけにパースが遅い気がするのも気になるなぁ。パース時に:enable_memoをtrueにしてあげればキャッシュを使って早くするらしいんだけど*1、どうも効いてる気がしない。
と思ったら、遅いのはパースじゃなくてコード生成だったようだ。パースも遅そうな気がするけどそれはもうちょっと使ってみないとわからんか…。
*1 っつーか標準でキャッシュしろよ!
GLSLパーサ作るのにRaccを使わない理由。
いや何度か使おうと思ったんだけどね…。
最初はそういや最近PEGとかなんとか流行ってた気がするってのと、曖昧さが無いのは魅力的と思ってPEGを選んでみた。
Raccで書いてると*1ここはこれでちゃんとパースしてくれるのか?と不安になる所があるのとconflictが出た時にどうすりゃいいのかわかってないのでどうしようもない。PEGならこれが無いのかなーって。
で、Treetopを使ってみて、確かに書き方で不安になることは無かった。演算子の優先順位付けとか書き方が難しいけど慣れれば全く明確だ。
しかし。
GLSLとかの文法って大抵ドキュメントにBNFで書いてあるんだよね。Raccだとそれをパクってくればだいたい完成。 PEGではそれができない。だって書き方全然違うんだもの。
書き方が違うってのは痛くて、俺の作ったGLSLパーサを誰かがいじろうとしたら、まずPEGの書き方を覚えないといけないんだよね。そんなに難しくはないもののめんどいよね。
あとRaccは(ランタイム)標準添付で、別なライブラリを入れなくても使える。まあTreetopは大きくないしPure Rubyだから全然気にならんけど。
ということもあってやっぱRaccで書いちゃった方が楽かなーと思った時期が何度かありました。
でもやっぱりPEG、TreetopからCitrusに乗り換え中ではあるもののPEGのライブラリを選択。
なんでかというと、やっぱり書き方と実行ルールがシンプルなのはいいよね。Raccって書き方いつも忘れちゃう。なんかconflictとかないしね。
あとはTreetopにしてもCitrusにしても一旦書いた文法の拡張が楽。これはPEGよりライブラリの作りの話かもしれないんだけど。
TreetopもCitrusも出来たものはModuleになってて、他の文法からincludeできるようになってる。で、includeしてさらに規則を追加してあげれば手軽に文法拡張が可能!
これは非常に嬉しくて、たとえばGLSLに独自にCgFXとかHLSLのFXみたいなアノテーション追加してーなと思ったとする。Raccだと.yファイルを直接いじってコンパイルしてあげないといけない。文法のコピペが必要になるわけだ。
でもTreetopやCitrusだったら、元のGLSL文法はそのままで、追加分だけ自分で書いてGLSL文法をincludeしてあげれば完成!こっちのほうが楽チンですよね。気軽に拡張できるし、やる気になれば複数の拡張をいっしょに入れたりもできるかも。
Raccはたぶんそんなことできないし、他のLALRなパーサも見たけどできそうには無かった。少なくともコンパイル後の物を取り込むのは無理っぽい。
ということでやっぱりPEGで行くことに。Treetopで不満だった、文字列以外を入力にする(トークン列を入力にする)のが難しいってのはCitrusなら比較的簡単に作れそうなので大丈夫かな。
ただPEGは遅そうってのがあるんだよなぁ。まあ遅いのはそのうち速くなる可能性があるから今はいいか。遅いのは遅いのが問題になってから考える。
*1 LALR法のパーサっていうんだっけ
IronRubyで困るシリーズ。
IronRubyで継続を使おうとしたら無かった。
BulletMLをRubyで書いてしまおうとしてDSLっぽくなったんだけど、弾のスクリプトに途中で何フレーム待つとかいう機能があるんですよ。もちろん無いと困るんだけど。
で、DSLっぽい感じなので途中で切り上げる処理を自然に書きたくて、継続を使うことにした。 1.9ならFiberを使うところだろうが、IronRubyは1.8なのだ。
出来たので動かしてみたらcallccが無いとおっしゃる。おやおや。なぜかあると思い込んでいたんだが無いのか。
かといってFiberがあるわけもない。Enumeratorがあるじゃない!と思ったがEnumerator#nextはない。そりゃそうか。継続もFiberも無くてどうやってEnumerator#next実装するんだってな。
.NETにFiberっぽいクラスがあるかなぁと思ったが見当たらず。少なくともSilverlight4には無い。
これはこまったなもう…。仕方ないので以前作ったThreadを寝かせといて似たようなことをやるやつを使おう。
しかし動くのかなこれ…って動かねー!以前動くところまでは確認したつもりだったんだけど、動くわけねぇコードだな。 なんとなく直してみたが思ったとおりにはいかず沢山スレッド量産されて大変なことになってしまい今日は時間切れ。
まあちゃんと動いたとしてもスレッド100とかそれ以上に立ち上がっちゃうはずなんだけどさ。
うーん、IronRubyにFiber実装されんかなぁ。自分で実装するのはかなり難しそうだが。