福澤テクノロジー

おっさんプログラマーが、主に技術的な話をするブログ。

mruby に %W %w %s リテラルを追加

mruby に %W %w %s の3種類のリテラルを追加し、pull request しました。
この時、文字列の解析ルーチンに無駄があったのでリファクタリングしつつ、新リテラルにも対応させるようにしました。
特に Here document 周りに無駄があったので、 parse_string() に吸収合併させた所、バイナリサイズも減りました。
自分の環境でのサイズ変化は以下の通り。

  • mirb 600208 → 591488
  • mrbc 517588 → 513180
  • mruby 600164 → 591452

同時に %I %i リテラルも簡単に作れたのですが、こちらはまだJIS規格になってない(ということはISO規格にもなってない?)ので pull request はしてません。
git://github.com/FUKUZAWA-Tadashi/mruby.git の iI ブランチにコミットしてあります。


追記(2013-03-19):
%W %w %s リテラルは、本家にマージされました。
%I %i リテラルは、pull request していいよとの事だったので、pull request 出しました。

追記(2013-03-19):
%I %i リテラルも、本家にマージされました。

Lazy Here-document (仮) をmrubyに実装してみた

Hayat言語の特殊機能 sayCommand - 福澤テクノロジー にて、sayCommand機能を紹介しましたが、言語仕様としては不恰好で一般的とは言えません。
そこで、他の言語にも採用してもらえそうな仕様を考えました。
仮に、Lazy Here-document と名付けてみました。式展開をその場で評価しないというような意味合いで。

Rubyに組み込んだと想定して書いてみます。
<<< で開始しHere documentと同じ様に記述するのですが、値は文字列ではなく配列になります。
式展開は実行されず、Procオブジェクトとなります。

a = <<<EOH
aaa#{ 1 + 2 }bbb
ccc
EOH

a.map{ |x| x.class }
# => [String, Proc, String, String]

a.map{ |x| x.class == Proc ?  x.call().to_s : x }.join('-')
# => "aaa-3-bbb\n-ccc\n"

これであれば、配列リテラルの一種として一般化出来て、sayCommandのような余計なメソッドを定義する必要もありません。


これを、mrubyに実装してみました! (これをしたかったが為に、mrubyにHere documentを実装したのでした。)
https://github.com/FUKUZAWA-Tadashi/mruby.git
heredocブランチにコミットしてあります。

Rubyの仕様から外れているので、 pull request はしていません。
mrbgem に出来ればいいんだけど、parse.y や codegen.c を変更しているので、出来ない。(と思う)

現状の課題
  • Lazy Here-document と呼ぶ?
  • 開始は <<< でいいのか?
  • <<<'EOH' でも式展開をしてしまっている
  • <<<- で終端文字列がインデント出来るようになっていない

Hayat言語の特殊機能 sayCommand

スクリプト言語Hayatは、ゲーム機組み込み用に設計された言語なので、ゲーム用の特殊機能があったりします。
その1つ、sayCommandを紹介します。

こんなスクリプトを書くことができます。

  アインシュタイン「人生は{青}退屈すれば長く{白}、
           {赤}充実すれば短い{白}。」

これはコンパイル時に以下のように展開されます。

  アインシュタイン.sayCommandStart(1,2)
  アインシュタイン.sayCommand("人生は")
  アインシュタイン.sayCommand(青)
  アインシュタイン.sayCommand("退屈すれば長く")
  アインシュタイン.sayCommand(白)
  アインシュタイン.sayCommand("、\n")
  アインシュタイン.sayCommand(赤)
  アインシュタイン.sayCommand("充実すれば短い")
  アインシュタイン.sayCommand(白)
  アインシュタイン.sayCommand("。")
  アインシュタイン.sayCommandEnd()

"" から ""までの間の文字列と式展開が、sayCommandメソッドに送られます。
sayCommandStart の引数は、スクリプト内でのsayCommandの通し番号と、行数です。
他の言語では、文字列中の式展開やHeredocument中の式展開は、その場で計算され、文字列に変換されて全て結合され、
1つの文字列となってしまいます。
HayatのsayCommand機能では、式展開はsayCommandメソッドの引数として渡されるようになっています。
ゲームでは、文字を表示するときに1文字ずつ徐々に表示させて、しゃべっているような演出をする事が多々あります。
また、文字毎に色を変更したり、フォントサイズを変更したり、表示速度を変えたりする事がよくあります。
他の言語の式展開のように1つの文字列にされてしまうと、こういった制御が面倒なのです。
sayCommandであれば、3つのメソッドを定義すれば好きな様にコントロールする事が可能です。

  enum { 白, 青, 赤 }   // 0,1,2
  class Person
    def initialize(name) { @name = name }
    def sayCommandStart(serialNo, numLines) {
      myGamePrintName(@name)
    }
    def sayCommand(x) {
      if (x.getClass() == Int)
        Font.setColor(x)
      else
        myGamePrintString(x)
    }
    def sayCommandEnd() {}
  end
  アインシュタイン = new Person("アインシュタイン")


Rubyでは式展開は #{ } ですが、Hayatでは { } です。
複数行に渡る場合、行頭と行末の空白文字は削除されます。なので好きな所で好きなだけインデント可能です。全角スペースも空白文字です。
敢えて行頭などに空白を入れたい場合は、空白文字をエスケープすればOKです。

この機能は、ノベル系のゲームを作る時に大変役立ちました。プログラマでないスクリプターにもわかりやすく書けます。
また、ゲーム中のイベントでゲーム内のキャラに喋らせる演出をする時も役に立ちました。
ゲーム用のスクリプト言語には、この機能があるととても便利です。Hayat以外の言語でも実装して欲しい所です。
他の言語を採用した場合は、スクリプトとは別の文章記述ファイルに、各ゲーム独自のルールで文章を書き、
別のツールでコンバートするといった面倒なことになりがちです。

便利ではありますが、メソッドを3つ定義する必要があるとか、言語仕様としてスマートでない所もあります。
それを解決しつつ、ゲーム専用ではなく一般化する方法を考えたのですが、それは次のエントリにて。

ゲーム機用スクリプト言語

昔は、ゲーム機に乗せるスクリプトは自作するものだった。

僕も10年前のプロジェクトでスクリプト言語を作った。

ゲームに特化した言語で、データ型も4種類しかなかったが、プログラマでなくても

イベントや演出を作ることができ、スクリプト言語の持つパワーを感じた。

もっとうまく設計された言語を使えば、ゲームの生産性向上に大きく寄与するはずだ。

ゲーム内ではなくツール用としてはperlrubyを使っていたが、こういった強力な

言語がゲーム内でも使えるようになれば、開発スピードも上がりそうだ。

 

残念ながら会社が傾いて多くの社員がリストラにあい、僕も会社を離れることになって

この時作った言語を発展させる事にはならなかった。

 

転職先の会社で、新たにスクリプト言語を作ることになった。

会社の業務として作るのだが、作った言語の著作権を自分のものとする契約を結んだ。

(かなり大きな代償と引き換えだった。)

 

こうして作ったのが、Hayat言語だ。MITライセンスで公開している。

https://github.com/FUKUZAWA-Tadashi/Hayat.git

PSPNintendo DS 上で動作させた実績があり、製品として出荷されたものにも含まれている。

トレーディングカードゲームのような複雑なシステムを Hayat上で構築してちゃんと

動いていたし、開発していて結構さくさく作れた。HayatでなくC++で全部作っていたら

ものすごく大変だっただろう。

 

しかし、現在ではHayatは使わずにlua,squirrel,mrubyなどの他の言語を選択する方が良いだろう。

Hayatは一人で作っていたため改良のスピードが遅く、言語仕様としてはちょっと

面白いものになったと思うのだが、動作速度の点でかなり見劣りがする。

オープンソースにするのが遅すぎた。

元々rubyライクなものにする目標で設計したので、mrubyが出てきた現在となっては

Hayatを選択するメリットは無くはないものの、少ない。

 

結論は、ゲーム機用スクリプト言語としては今後は mruby を採用するといいんじゃないかな、という事。

 Nintendo や SCE が、開発ツールの中に最初から組み込んでくれるのがベストだと思う。

なんなら僕が組み込みの仕事をやりたいんだけど、どうですか Nintendoさん、SCEさん。

 

mrubyにHeredocument機能を追加

mrubyに Here document の機能が無かったので、作ってみて pull request したら

早速採用されました。

夜にpull requestしたら、翌朝には2件のバグフィックスも含めてマージされてた。早い!

 

mruby は https://github.com/mruby/mruby でメンテされています。

 

ブログ開始

不特定多数に情報発信する手段を、ツイッター以外に持っていなかったので、

ブログを開設してみました。

技術的な話を主にしていくつもりです。