同人ゲーム 制作ガイドブック 基礎編という本が株式会社メディア・テック出版より発売されるそうです。
2006/08/31発売で(ただしもう書店に並んでいるそうですが)、8/27時点ではメディア・テック出版のページからのリンク先が間違っています。9/1の時点でリンクは直ってました
「基礎編・応用編」の分冊になっており、応用編は9月中に発売されるそうです。
「吉里吉里KAGで乙女ゲーム・BLゲームを作成する」というサブタイトルです。
RisseはTJS2に比べて強力な車輪が追加されていますが、そのうちのいくつかが動作するようになりました。
まずはECMAスクリプト風のクロージャです。
var test = function () {
var n = 0;
return function () { n++; return n; };
};
var closure = test(); // 関数オブジェクトが帰る
closure(); // n++ が実行されて 1 が帰る
closure(); // n++ が実行されて 2 が帰る
return closure(); // n++ が実行されて 3 が帰る
function test内にあるローカル変数nを 入れ子になった関数内で操作して、その入れ子の関数を返しています。ローカル変数はすでに入れ子の関数に結びついているため、function testのスコープ外であっても、その入れ子の関数からは参照できます(TJS2ではこれができませんでした)。
次はRuby風のブロック
var repeat_4 = function () block
{
// ブロックを4回実行する
for(var i = 1; i <= 4; i++) block(i);
};
var n = 0;
repeat_4() { |v| n += v; };
return n; // => 10
関数呼び出しの後に「ブロック」をつけて呼び出し、呼び出された関数からそれをコールバックすることができます。関数呼び出しの際のブロックの記法はRubyに酷似していますが、関数宣言時の記法やブロックの呼び出しはちょっと違います。ちなみに以下のようにも記述できますが、この例では全く同じ動作をします。ただし、生成されるコードと意味合いはちょっと変わります(どう変わるのかはいずれ)。
var repeat_4 = function () block
{
// ブロックを4回実行する
for(var i = 1; i <= 4; i++) block(i);
};
var n = 0;
repeat_4() function(v) { n += v; };
return n; // => 10
ちなみに
repeat_4() { |v| n += v; };
の最後の;(セミコロン)は省略できません。ifなどの次のブレースで囲まれた複合文の「後」のセミコロンは省略できるのですが、上記のrepeat_4() { |v| n += v; }は「式」なのでその後にセミコロンが必要です。セミコロンが要る場合と要らない場合とあっていやんな感じで、いろいろ省略できるような記法なども考えたのですが、どうにもこの文法体系上では解決のしようがない気がします。ここら辺を見てみるとRubyの文法は良くできてるナァと思います。
ちなみにECMAスクリプト(JavaScript)の「セミコロンの挿入」は嫌いです (閉じブレースの「前」のセミコロンは省略可能にはするかも)。
あと、たぶん(まだ実装してませんが)
for(var member in object) { ... }
は以下の文と等価になります。
object.each() { |member| ... };
Risseでは A garbage collector for C and C++ (通称 Boehm GC) 使ってますが、とても便利。
長所
短所
今のところ長所の方が短所を大きく上回るので便利なのです。とくにdeleteしなくてよい(明示的にしなくても勝手にそのうちdeleteされる)というのは、Risseのように複雑なデータ構造を多用する場面ではとくに便利です。Risseではまだデストラクタを書いたことがないです。
とりあえず簡単なスクリプトは実行できるようです。
test.rs
var sum = 0; for(var i = 0; i < 10; i++) sum += i; return sum;
が
$ ../../build_output/bin/rissetest.exe test.rs
========== AST ==========
Context (TopLevel)
node0:VarDecl (var)
item0:VarDeclPair (sum)
initializer:Factor (Constant 0)
node1:For
initializer:VarDecl (var)
item0:VarDeclPair (i)
initializer:Factor (Constant 0)
condition:Binary (Lesser)
child0:Id ("i")
child1:Factor (Constant 10)
iterator:Unary (PostInc)
child:Id ("i")
body:ExprStmt
expression:Binary (AddAssign)
child0:Id ("sum")
child1:Id ("i")
node2:Return
expression:Id ("sum")
========== SSA (root) ==========
*entry_1
// Use start: _tmp@2
[0] _tmp@2 = AssignConstant() // _tmp@2 = constant 0
// Use start: sum#3@5
[1] sum#3@5 = _tmp@2
// Use end: _tmp@2
// Use start: _tmp@6
[2] _tmp@6 = AssignConstant() // _tmp@6 = constant 0
// Use start: i#7@9
[3] i#7@9 = _tmp@6
// Use end: _tmp@6
// Use start: sum#3@31
[4] sum#3@31 = sum#3@5
// Use end: sum#3@5
// Use start: i#7@11
[5] i#7@11 = i#7@9
// Use end: i#7@9
[6] goto *for_cond_10
// LiveOut: sum#3@31, i#7@11, i#7@9, sum#3@5
*for_cond_10 // pred: *entry_1, *for_iter_23
// LiveIn: sum#3@32, i#7@29, i#7@9, sum#3@5
// Use start: _tmp@12
[7] _tmp@12 = i#7@11
// Use start: _tmp@13
[8] _tmp@13 = AssignConstant() // _tmp@13 = constant 10
// Use start: _tmp@14
[9] _tmp@14 = _tmp@12.Lesser(_tmp@13)
// Use end: _tmp@12, _tmp@13
// Use start: sum#3@18
[10] sum#3@18 = sum#3@31
// Use start: i#7@16
[11] i#7@16 = i#7@11
// Use start: sum#3@33
[12] sum#3@33 = sum#3@31
[13] if _tmp@14 then *for_body_15 else *for_exit_30
// Use end: _tmp@14
// LiveOut: sum#3@33, sum#3@31, sum#3@18, i#7@16, i#7@11
*for_body_15 // pred: *for_cond_10
// LiveIn: sum#3@31, i#7@11
// Use start: _tmp@17
[14] _tmp@17 = i#7@16
// Use start: _tmp@19
[15] _tmp@19 = sum#3@18
// Use end: sum#3@18
// Use start: _tmp@20
[16] _tmp@20 = _tmp@19.Add(_tmp@17)
// Use end: _tmp@19, _tmp@17
// Use start: sum#3@22
[17] sum#3@22 = _tmp@20
// Use end: _tmp@20
// Use start: sum#3@32
[18] sum#3@32 = sum#3@22
// Use end: sum#3@22
// Use start: i#7@25
[19] i#7@25 = i#7@16
// Use end: i#7@16
[20] goto *for_iter_23
// LiveOut: sum#3@32, i#7@25, sum#3@22, i#7@16
*for_exit_30 // pred: *for_cond_10
// LiveIn: sum#3@31
// Use start: _tmp@34
[21] _tmp@34 = sum#3@33
// Use end: sum#3@33
[22] _tmp@34.Return()
// Use end: _tmp@34
*for_iter_23 // pred: *for_body_15
// LiveIn: sum#3@22, i#7@16
// Use start: _tmp@24
[23] _tmp@24 = AssignConstant() // _tmp@24 = constant 1
// Use start: _tmp@26
[24] _tmp@26 = i#7@25
// Use end: i#7@25
// Use start: _tmp@27
[25] _tmp@27 = _tmp@26.Add(_tmp@24)
// Use end: _tmp@26, _tmp@24
// Use start: i#7@29
[26] i#7@29 = _tmp@27
// Use end: _tmp@27
[27] sum#3@31 = sum#3@32
// Use end: sum#3@31, sum#3@32
[28] i#7@11 = i#7@29
// Use end: i#7@11, i#7@29
[29] goto *for_cond_10
// LiveOut: sum#3@32, sum#3@31, i#7@29, i#7@11
========== VM (root) ==========
00000 const %0, *0 // *0=0
00003 cp %1, %0
00006 const %0, *0 // *0=0
00009 cp %2, %0
00012 cp %0, %1
00015 cp %1, %2
00018 jump 00020
00020 cp %2, %1
00023 const %3, *1 // *1=10
00026 lt %4, %2, %3
00030 cp %3, %0
00033 cp %2, %1
00036 cp %5, %0
00039 branch %4, 00043, 00064
00043 cp %4, %2
00046 cp %6, %3
00049 add %3, %6, %4
00053 cp %4, %3
00056 cp %3, %4
00059 cp %4, %2
00062 jump 00069
00064 cp %2, %5
00067 ret %2
00069 const %2, *2 // *2=1
00072 cp %5, %4
00075 add %4, %5, %2
00079 cp %2, %4
00082 cp %0, %3
00085 cp %1, %2
00088 jump 00020
========== Result ==========
45
(ここでSSA (root)などとして表示されてる内容は、SSA形式を加工後のフェーズの内容が表示されているもので、これはSSA形式ではありません)
すごい量の無駄なレジスタ間コピー(cp)ですね。まぁここらへんは後で。
あとは関数宣言やtry-catchやクロージャなどを仕上げてからオブジェクト指向システムの実装に移ろうかと思ってます。