history

青木日記 RSS

<前月 | 最新 | 次月>

2004-09-02

BitChannel updates

徹夜していたら気が向いたので BitChannel の to do を簡単なやつからいくつか潰した。 BitChannelComment に書いてあったやつと、 rev 1.1.1.1 の件、それから CSS の @media=print、 あと大物としては revision kill か。 ま、詳しいことは BitChannelNews でも見てください。

バグレポートってどこにしてもらうのがいいのかねえ。 いまはテストのつもりで Wiki に置いてるけど、ML のほうが便利だろうか。 それとも Wiki のままで diff RSS を実装したほうが流行に乗れるかな。

(15:23)

ML

ML は設定するのがめんどくてさ。

ML つっても SML とか OCaml の ML ではなくて Mailing List のほうですよ。

(15:25)

ripper

なんか次から次へとバグレポートが来るんですけど。 こんなんじゃコミットどころじゃないっつーの。

そういえば現在の Ripper は ruby 1.9 専用っすよ。 ruby 1.8 で使うと落ちます。 両バージョン対応はめんどくさいからやりません。

(16:18)

日本Rubyの会

世間の波に置いていかれまくり。

るびま、というのは Rubyist Magazine ですか。 けっきょく無難なやつになったんだ。

ちなみに「るびま」って「ネギま!」みたいだなとか思うわたしはダメですか。 そうですか。

そーいや日本 Ruby の会に登録しようかなあ。 今年はたいしたことはできそうにないけど……。

(16:24)

RSS

最近なんでも RSS にしたがる人が多いけど、 CVS のコミットログも RSS になるんだろうか。

(16:46)


2004-09-03

X 死す

朝起きて、マシンを立ちあげて、Mozilla でアンテナの未読をバーっと開いたら X が即死。 どっかで SEGV したらしい。

(08:37)

無限fork

NetBSD の df に -h オプションがなかったので Ruby でラッパーを書こうかと思ったんだけど、 うっかり

out = `df -k`
...

とか書いて、それを ~/bin/df にインストールしてしまった。 で、df。

当然無限に自分を呼び続けることになってすごい勢いでリソースを消費していく。 ps してる間にもプロセスが増えていくので kill じゃ対処できないし、 killall ないし (あってもだめだったかも)、 もしかしてログアウトしたらねこそぎにできるかと思って exit してみたんだけど、ハードディスクの音が止まる気配もない。 ついでにメモリ不足で ssh もつながんないし、 シリアルコンソールはセットアップするの忘れてるし、 コンソールはもともとついてないし、で詰み。 しかたがないので電源切って落とした。 本当はどうするのがよかったんだろう。

(18:33)

BitChannel 0.3.1

何気に BitChannel 0.3.1 出ました。

(21:06)

つーことで

DEC Celebris GL-2 2622 を買いました。 Pentium II 333MHz を二個のせてパワーアップだ!

最近外向けサーバの負荷が上がってきてるので、 せめて dual にしてやろうかと。

(21:06)

わー

生姜焼きを作ろうと思ったら生姜がカビてる。

という一言を書くためだけに、 一度シャットダウンしたマシンを立ち上げるのってどうなのよ。

(01:16)

本日のツッコミ (全6件) [ツッコミを入れる]

anonyomous [怖くて試せませんが、
>~/bin/df
で上書きすれば間に合えば止まるかも。]

青木 [ああっ、そうか!
コマンドのほうを変えればいいんですね。
それは思いつかなかった……。]

なかだ [^Zでsuspendでは?]

青木 [そっか。それでもうまくいきました。
けっこういろいろあるなあ。]

Nayukist [今更ですが予めユーザーの最大プロセス数を制限しとくのがいいような。]

kjana [*BSD なら

Terminate the process group with pgid 117:
kill -- -117

なんて出来るようですが。

もしくは慌てず root のシェルを確保しておく。root のプロセス枠がいくつか確保されてるはずだから。]


2004-09-04

forkをbreak

何の意味があるのかよくわからないが、 Ruby の fork ブロックから break してみた。

% test-all-ruby -e 'fork { break }; Process.wait'
ruby-1.4.6:
/tmp/rbG3fnQt:1:in `wait': No child processes (Errno::ECHILD)
        from /tmp/rbG3fnQt:1
ruby-1.6.0:
-e:1:in `wait': No child processes (Errno::ECHILD)
        from -e:1
ruby-1.6.1:
-e:1:in `wait': No child processes (Errno::ECHILD)
        from -e:1
ruby-1.6.2:
-e:1:in `fork': unexpected break
ruby-1.6.3:
-e:1:in `fork': unexpected break
ruby-1.6.4:
-e:1:in `fork': unexpected break
ruby-1.6.5:
-e:1:in `fork': unexpected break
ruby-1.6.6:
-e:1:in `fork': unexpected break
ruby-1.6.7:
-e:1:in `fork': unexpected break
ruby-1.6.8:
-e:1:in `fork': unexpected break
ruby-1.8.0:
-e:1:in `fork': unexpected break
ruby-1.8.1:
-e:1:in `fork': unexpected break
ruby:
-e:1:in `fork': unexpected break

まあ、そんなもんかねえ。

じゃあ next はどうよ?

% test-all-ruby -e 'fork { next }; Process.wait'
ruby-1.4.6:
ruby-1.6.0:
ruby-1.6.1:
ruby-1.6.2:
ruby-1.6.3:
ruby-1.6.4:
ruby-1.6.5:
ruby-1.6.6:
ruby-1.6.7:
ruby-1.6.8:
ruby-1.8.0:
ruby-1.8.1:
ruby:

そりゃそうか。じゃあ redo ……

% test-all-ruby -e 'fork { redo }; Process.wait'

って、無限ループじゃん。

(23:16)


2004-09-05

たまには時事ネタ

DEATH NOTE 3 巻買ってきた。

……うわあ、なんかどんどん面白くなるな。 それにつけても思うのは、L は本当に入学したんだろうか……。

(02:00)

プロセス越し無限fork

うーむ、現場ではあせって対処不可能になった無限 fork ですが、 やっぱりいろいろと回避方法があるもんですねえ。 勉強になりました。 まとめると、まず予防策としては

  • プロセス数を制限しておく
  • コンソール確保 (rootアクセスができるようにしておく)

すでに発生している場合は

  • コマンドを mv するとかして、中断させる
  • ^Z でサスペンド (そののち kill)
  • kill -- -PID でプロセスグループごと一網打尽 (Linux の procps でも kill -PID でグループごと殺せるのね)

てなとこですか。 man に載ってる方法もあるんだから、 落ち着けばなんとかなったかもしれないな。 まずは冷静になるのが大切か。

(02:10)


2004-09-07

Referer SPAM

リファラ数が全部 21 で統一されてると思ったら、 Referer SPAM かよ。 奴らも URL に「/」を付ける技を覚えたようだ。

(17:04)

tDiary関連いろいろ

Referer SPAM 対策を考えるついでに disp_referrer のキャッシュ方法を考えた。 tDiary ではリファラは常に最新の日付にだけ蓄積されるので、 最新日より一日以上古いリファラは決して更新されない (と思う)。 つまり update.rb が日付をまたいだ時点で 前日分のリファラを処理して結果だけ保存すれば十分なはずだ。 (ちなみに最新日だけは覚悟を決めて毎回計算する。)

で、それと微妙に関連するんだけども、 tDiary のデータベースは日単位にしたらどうなんだろう。 例えばこんな感じのディレクトリ構造にする。

200401/
	01/
		property     …… Visible とか Format とか
		data         …… 日記
		comment/     …… ツッコミ
			01/
				property
				data
			02
			:
		referer      …… リファラ
	02/
	03/
	:

qmail のごとく、property をさらに細かくファイルに分けてもよい。 今の実装だと日付関係の計算をするためには一月まるごと読まないとならないけど、 ファイルが日単位になってればツリーのトラバースだけで済むのが利点だ。

……なんか実装したくなってきた (しないけど)

(18:07)

ruby-list

いくらなんでも重なりすぎ! (笑)

(19:27)

evalとconst_get

[ruby-list:40021] から始まる「クラス名→クラスオブジェクト」の話だけど、 ちょっと細かいことになるのでこっちに追記。

クラス名からクラスオブジェクトを得る方法として

eval(classname)
 
classname.split(/::/).inject(Object) {|c,name| c.const_get(name) }

の二つがある。 で、このうち eval は危険だとすぐわかるのでみんな注意する。

しかし、この場合に eval が危険なのは classname をリモートからかなり自由に指定できる場合である。 classname が定数なら「eval しているから危険」とは言えない。

そしてまた、引数が自由になるのであれば、 同じ状況で const_get を使ったところで安全とは言えない。 例えば classname に任意文字列を指定できるとすると、 (任意のクラス).new (など) が呼べることになる。 すぐには具体的な例が思いつかないんだが、 DL とか Win32API とか IO あたりを new すると それなりに危ないことができそうな予感がしないでもない。

ちなみに $SAFE=1 でも Object.const_get("IO".taint) は通るので $SAFE も当てにならない (シンボルは汚染されない)。

(23:00)

(追記) やっぱり new だけですごくやばいことができる例がないとイマイチ説得力がないな。 任意のコードの eval に比べると攻撃力が違いすぎ。

ま、そういうことで

AIX 確保。

(23:05)

本日のツッコミ (全3件) [ツッコミを入れる]

mput [> tDiary ではリファラは常に最新の日付にだけ蓄積される
逆かと。最新の日付に蓄積「されないように」する設定変更は可能ですが、permlink経由のリファラは常に特定の日付に記録されて、これを記録しないようにする手段はないはずです。

ちなみにその設定は設定画面の「リンク元->リンク元の記録制御」でやります。]

青木 [ええ、うそっ? うわー、本当だ。恥ずい……。
え、じゃあ、昔の日記の referer も隱れて増えてるのか。
なんかすごい数になってるリファラがありそうだけど。]

青木 [最高でも「みこみこ」の 1500 ちょいか。ちぇ。]


2004-09-09

tdiarysearch

頭からその存在をすっかり忘れていた tdiarysearch だが、 バグ報告が来てるな。

うーむ……。 category.rb が独自にやってる load_plugin が tdiarysearch のとバッティングしてるのかな。 とりあえず再現させるのが先決か。

(13:36)

Linux本

次の本を書いています。 またまた Ruby を裏切って Linux プログラミングの初心者本です。 まあでも、中で Ruby の話もしてるから裏切ってはいないか……。

できれば夏休み中に完成させたいんですが、どうなることやら。

(13:38)

かれこれ 3 時間くらい部屋のすみにでっかい蜂がとまって微動だにしない。 奴は何をやっているのだ。

(13:47)


2004-09-10

るびま

http://jp.rubyist.net/magazine/?0001

Rubyist Magazine (略して「るびま」) 第 1 号が出ましたね。 関係者の皆さまお疲れさまでした。

いやー

いやー

いやー……

巻頭言が面白すぎるんですけど! 「〜の発刊に際して」の時点でコーヒー吹いたよ!

Nora チュートリアルはフツーに参考になった。 ありがちなインストール記事がないけど、いいんだろうか。まあいいよな。 (そういえば、インストーラまわりをちゃんと追うと一本書けそうだ)

Ruby de GUI、今回は第一回だからしょうがないのだけど、 並列に並べるよりは思いきり偏った内容のほうが参考になりそうです。 そのへんは次回以降に期待してます。

あと、NaCl レポートできっちりサーバ室を撮ってきた笹田さん GJ! まつもとさんインタビューも今まではあんまりなかった感じだな、 というか日記の投票ネタを全部つっこんだね?

でもって「紅い隕石」さん…… 文章を読むと中の人が一瞬でわかるんですが (笑)

るびま (2)

第一号はなかなかよかったですねえ。 なんか書いてみたい気持ちもあるけど、 次に何が出てくるのかわからない状況に立つのも面白いな。 たまには傍観者になろうかな。

(15:31)

Ripperコミット予告

明後日 12 日に Ripper をコミットします。今度ばかりは本気です。 今日と明日でないのは明日が本の第一締切だから。 変なバグが報告されてたのでコミットを延期してたんですが、 問題が起きているのは Ruby 1.8.x だったということがわかったので とりあえず懸案はなくなりました。

なお、当然ですがコミット対象は HEAD のみです。 安定版に入れられるような状態ではありません。

(15:32)

ネットワーク切断

なんかすごくピンチっぽいのでしばらくイーサネットケーブルひっこぬきます。

(15:33)

本日のツッコミ (全4件) [ツッコミを入れる]

arton [徹夜明けの3分間クッキングきぼーん。]

ささだ [企業スパイと言われました。]

ささだ [ripperコミットって、parse.y にコミットってこと?]

青木 [そう。 > ripper
いま入れるとまずい?]


2004-09-12

断念

期待にこたえて秘密のネタをやろうかと思ったけど やっぱりやめときます……。 同じネタをやってもおもしろくないし。

(22:44)

Ripper入れるよ〜

Ripper を ruby 本体 (parse.y, ext/ripper) にコミットすべく作業中です。 12 日に作業を始めるのも、まあ 12 日のうちだよな。

何かまずそうならツッコミ入れてください。

(22:45)

/procでmv

procfs のエントリを変更しようとするとどうなるか実験してみた。

/proc/1526 % uname -srm
Linux 2.4.22 i686
/proc/1526 % ruby -e 'File.rename "fd", "_fd"'
-e:1:in `rename': No such file or directory - fd or _fd (Errno::ENOENT)
        from -e:1
/proc/1526 % ruby -e 'File.unlink "maps"'
-e:1:in `unlink': Permission denied - maps (Errno::EACCES)
        from -e:1
/proc/1526 % sudo ruby -e 'File.unlink "maps"'
-e:1:in `unlink': Permission denied - maps (Errno::EACCES)
        from -e:1

ということで、rename すると ENOENT、 unlink すると root であろうと EACCES になるようだ。 へー。

(22:46)

ripper (2)

とりあえず parse.y から入れてみる。

うぉぉ test/ruby/test_eval.rb で SEGV しやがった!

と思ったが ripper に関係なく元から SEGV していた。よかったよかった (マテ)

(00:33)

(追記) [ruby-dev:24228] のやつだな。

ripper (3)

lib/ripper.rb をコンパイル時に作るにはどうすればいいんだー

あー、どうしよう。 プラットフォームごとに違うものじゃないし、 開発者 (つまり俺) が作って ci することにしようかなあ。 ripper.rb が変わるのってイベントの互換性がなくなるときだし、 あんまり頻繁に変えていいものではないのは確かなんだよな。

(01:32)

ripper (4)

ext/ripper test/ripper コミット完了。

(02:54)

ripper (5)

  • make test OK.
  • test/ripper OK.
  • srcdir make OK.
  • objdir make OK.

(03:18)


2004-09-13

超アンテナ

見張ってるページの更新された部分だけを表示してくれる アンテナってのは結構あるけど、その更新された部分に 各サイトの CSS を適用して表示するってのはできないかなあ。

(10:14)

本日のツッコミ (全2件) [ツッコミを入れる]

zunda [ツリー状にパースしてから差分をとればHTMLの構造は保ったまま更新部分を抜きだしたりできるのでしょーか。
(と、また脊髄反射を。]

otsune [CSSの名前空間がかぶるので、ソースを書き換えて……]


2004-09-14

寝て起きた

……寝すぎた。

syslog に

Sep 14 17:14:43 harmony kernel: EXT2-fs error (device ide0(3,65)): ext2_read_inode: unable to read inode block - inode=6848513, block=13697032

なんてのが出てるのがとても気になるが、 ハードウェア損傷でなければよいことにする。

ripper (1)

ああ……。 はい入れましたでは済まないだろうとは思ってたけど まさかここまでいろいろ問題が出てくるとはなあ、 というか Borland make をいじめてやりたい。キュッと。

しかしどっちかというと気になるのは [ruby-core:03391] かなあ。

(18:18)

Linux本

第二締切が土曜なんだよね。 RHG 読書会行けるだろうか。

(18:19)

ripper (2) ビルド関係

えーとまずはわたなべさんの Borland make 向けパッチをとりこむか。 ルールを減らしてパイプを削除して……でいいかな。 俺専用ルールは Makefile.dev に分離しておこう。

ripper (3) on__ANY ...?

http://www.dm4lab.to/~usa/ruby/d/200409b.html#id20040914_P1

うささんのコード例を見ていて、 method_missing を追放しようと決意する。 スキャナ・パーサに関らずすべてのイベント発生を フックできる on__ANY を作ればいいだろうか。

……と思ったけど、 上記のコードなら on__scan のがよさそうだな。 全イベントフックは他に役立つ例を見付けてからにしよう。

ちなみにヒアドキュメントまわりは Ripper でも一番嫌なことを している部分なのでトークンをロストするくらいは日常茶飯事なのだ! (いばるな)

(19:39)

ripper (4) BSD make

[ruby-core:03391] は BSD make で発生する問題だったようだ。 NetBSD/Alpha でやったら再現した。 Borland make 用の変更で偶然 BSD make も通るようになった模様。

(22:09)

setup.rb

http://takahr.dhis.portside.net/cgi-bin/rwiki.cgi?cmd=view;name=%C6%FC%BB%EF

setup.rb のドキュメント? …… http://i.loveruby.net/ja/man/setup/ だけでは不足ってことでしょうか。

でも extconf.rb も入っているということは、 本当に知りたいのは「extconf.rb や setup.rb」じゃなくて 「Ruby で書いたアプリケーションやライブラリのパッケージの作りかた」 (Ruby 開発者向け文書?) なのかなあ。 もうちょっと意図が明確にならないとなんとも言えません。

(02:23)

本日のツッコミ (全2件) [ツッコミを入れる]

ささだ [読書会を執筆会にしよう.]

青木 [うわー凄いはかどそうー (棒読み)]


2004-09-16

なんでいつも原稿書いてるときなんだよ!

あは

HDD クラ――――――――――ッシュ!!

(14:58)

いや違った不良セクタだけか。 まあ、どっちにしても棺桶に片足つっこんでるか首までつかってるかの違いだよなあ。 片足だけのほうが復旧は楽だけどさ。

HDDトラブル (2)

超・運のいいことに昔使っていた /home 領域を空けたままにしてあったので、 とりあえずそっちに引越すことにする。

それにしてもえらい手狭になってしまった。 ~/src をほとんど捨てても空きが 2.6GB (使用率 74%) しかない。 しょうがないので明日にでも HDD を買ってこよう。

(20:55)


2004-09-17

HDD 買い換え失敗

HDD を買いに池袋まで行ったのに、 銀行のカードを忘れて買えんかった……。

コメントSPAM

大量のコメント SPAM が来てるので 一時的にコメント機能を殺してあります。 なんか防御方法を考えないとだめかな。 サーバの負荷も心配だ。

(21:00)

Ripper関係

ちょっと今日明日は余裕がないので ripper 関係は 日曜以降に落ち着いて対応します。すんません。

(21:11)

ドキュメントとサンプルも同様……というか、 このへんはせめてもうちょっと仕様が安定してからにしたいのね。 現在は仕様も実装もあまりに流動的なんで。

(22:12 追記)

RHG 読書会

明日は RHG 読書会ですが、やっぱ出られなそうです。 というか無理。明らかに無理。絶対無理。

(22:34)

ロマサガ1 リメイク

http://www.quiter.jp/news/saga/040915002261.html (つかれたより)
http://www.square-enix.co.jp/games/ps2/romasaga/

まじか――――――――――――――ッ!

え、うそだろ、本当にロマサガ 1 リメイク? マジで? うわーもうこれは買うよ PS2 ごと買うよ どんなに改悪されても買ってしまうよ! 聖剣伝説のリメイクはひどかったらしいが、 今度ばかりはほんとに頼んます。

あ、そうだ、ルピーシステムは遠慮なく消してください。

(23:46)

本日のツッコミ (全2件) [ツッコミを入れる]

青木 [つっこみテスト]

青木 [ツッコミテスト その 2]


2004-09-18

ツッコミ欄復活

コメント SPAM が収まったようなのでツッコミ機能を復活させた。 しかしすげえよ。機械的に全日付に POST してきてたもんな。

対策はどうしようかな。 あらかじめ "delete_me" っていう文字列を入れといたテキストボックスを置いて、 それを消さない限り登録しないというのはどうだろう。 いやチェックボックスのほうが簡単か。

→ かんたんに実装してみた。

(11:54)


2004-09-20

ripper (1)

とりあえず ripper のパッチだけとりこんじゃうかな。

rb_reserved_word は static だとずっと思い込んでた。 よく見たら extern なんだな。

よくよく考えると、 本体と Ripper のトークンはバイナリ的に同じじゃなきゃだめなんだから、 別々の yacc で処理するだけでも危険なんだ。 そういう意味では本体も bison 必須になったのは都合がいい。

lex_strterm の件は迷う。 せっかく本体が再入可能になったことだし、 yyparse の再帰で処理できないもんだろうか。

(06:11)

ripper (2)

パッチをあてたところ

ヒアドキュメントまわりで

lex_p がふしぎなおどりをおどっている!

MP を 28 吸い取られた!

(06:12)

ripper (3)

parser->buf の役割をやっと思い出した。 Ruby レベルではトークンが tokbuf に保存されるから 入力バッファを捨てられるけど、 Ripper では行をまたぐトークンがあるからまずいんだ。

あと、ヒアドキュメントのまわりで current_position を退避・復帰してないのもまずい。 これもやはり入力バッファから書き直したほうがよさそう。

やっぱり入力バッファは Ripper 専用に書き直すしかないな。

(06:37)

(14:28 追記) やっぱやめた。

ripper (4) #pos

がんばって current_position を退避・復帰するか Ripper#pos を捨てるかの選択を迫られた結果、 あっさり Ripper#pos を捨てることに大決定。 ま、lineno と column があるんだからなんとかなるでしょ。

(13:12)

ripper (5) 複数行の文字列類

次はこれか……。

~/c/ruby % cat -n t
     1  print(<<EOS)
     2  1
     3  2a#{}2b
     4  3
     5  EOS
~/c/ruby % ruby -rpp -rtokenizer -e 'pp Ripper.tokenize(ARGF.read)' t
[[1, 0, :on__ident, "print"],
 [1, 5, :on__lparen, "("],
 [1, 6, :on__heredoc_beg, "<<EOS"],
 [3, 0, :on__tstring_content, "1\n"],   ← 行番号ズレ
 [3, 0, :on__tstring_content, "2a"],
 [3, 2, :on__embexpr_beg, "#{"],
 [3, 4, :on__rbrace, "}"],
 [5, 0, :on__heredoc_content, "2b\n"],  ← 行番号ズレ
 [5, 0, :on__heredoc_content, "3\n"],   ← 行番号ズレ
 [5, 0, :on__heredoc_end, "EOS\n"],
 [1, 11, :on__rparen, ")"],
 [1, 12, :on__nl, "\n"]]

ヒアドキュメント本体のイベントが場合によって違う。 行番号もおかしい。文字列が複数行になっているときも同様。 さてこれを直すのはどうすれば……

意外に簡単だった。

~/c/ruby % cat -n t
     1  print(<<EOS)
     2  1
     3  2a#{}2b
     4  3
     5  EOS
~/c/ruby % ruby -rpp -rtokenizer -e 'pp Ripper.tokenize(ARGF.read)' t
[[1, 0, :on__ident, "print"],
 [1, 5, :on__lparen, "("],
 [1, 6, :on__heredoc_beg, "<<EOS"],
 [2, 0, :on__tstring_content, "1\n"],
 [3, 0, :on__tstring_content, "2a"],
 [3, 2, :on__embexpr_beg, "#{"],
 [3, 4, :on__rbrace, "}"],
 [3, 5, :on__tstring_content, "2b\n"],
 [4, 0, :on__tstring_content, "3\n"],
 [5, 0, :on__heredoc_end, "EOS\n"],
 [1, 11, :on__rparen, ")"],
 [1, 12, :on__nl, "\n"]]

(heredoc_content イベントを送るのはあきらめた。)

理想的には入力での並び順にイベントを発生させたいところだが、 ネストした場合を考えるとちょっと難しいな。 どのタイミングでフラッシュすればいいんだか。

これも思ったより簡単だった。

~/c/ruby % cat -n t
     1  print( <<A , <<B)
     2  1
     3  2a#{}2b
     4  3
     5  A
     6  1
     7  2
     8  B
     9  %w(a
    10  b )
~/c/ruby % ruby -rpp -rtokenizer -e 'pp Ripper.tokenize(ARGF.read)' t
[[1, 0, :on__ident, "print"],
 [1, 5, :on__lparen, "("],
 [1, 6, :on__sp, " "],
 [1, 7, :on__heredoc_beg, "<<A"],
 [1, 10, :on__sp, " "],
 [1, 11, :on__comma, ","],
 [1, 12, :on__sp, " "],
 [1, 13, :on__heredoc_beg, "<<B"],
 [1, 16, :on__rparen, ")"],
 [1, 17, :on__nl, "\n"],
 [2, 0, :on__tstring_content, "1\n"],
 [3, 0, :on__tstring_content, "2a"],
 [3, 2, :on__embexpr_beg, "#{"],
 [3, 4, :on__rbrace, "}"],
 [3, 5, :on__tstring_content, "2b\n"],
 [4, 0, :on__tstring_content, "3\n"],
 [5, 0, :on__heredoc_end, "A\n"],
 [6, 0, :on__tstring_content, "1\n"],
 [7, 0, :on__tstring_content, "2\n"],
 [8, 0, :on__heredoc_end, "B\n"],
 [9, 0, :on__qwords_beg, "%w("],
 [9, 3, :on__tstring_content, "a"],
 [9, 4, :on__words_sep, "\n"],
 [10, 0, :on__tstring_content, "b"],
 [10, 1, :on__words_sep, " )"],
 [10, 3, :on__nl, "\n"]]

(14:26)

ripper (6) yylval

しまったあああああああああ! スキャナで dispatch した値をパーサに伝えてない! うっわー、これは致命的。 値を渡すことを考えるとトークンを並べなおすなんて不可能だ。 パーサイベントも遅延させるか……だめだ……無理すぎる……。

いや待てよ、遅延スキャナイベントですらまずいな。 一回パーサに制御を戻さないことには yylval を拾ってくれない。 つまり無理矢理でも一つのトークンにまとめないとだめだ。

うわー……最悪だ……。 まさかここまでバカやってるとは思わなかった。

(15:47)

ripper (7) sexp

こんなものを作ってみたのだが……

% ruby -rripper/sexp -rpp -e 'pp Ripper.sexp("def m(a,b=1,*c,&block) end")'
[:program,
 [:stmts_add,
  [:stmts_new],
  [:def,
   [:@ident, "m", 1, 4],
   [:paren,
    [:params,
     [[:@ident, "a", 1, 6]],
     [[[:@ident, "b", 1, 8], [:@int, "1", 1, 10]]],
     [:restparam, [:@ident, "c", 1, 13]],
     [:@ident, "block", 1, 16]]],
   [:bodystmt, [:stmts_add, [:stmts_new], [:void_stmt]], nil, nil, nil]]]]

これって S-Expression なんですかね。

(17:11)

ripper (8)

今日だけで 13 回もコミットしてしまった……。 もうちょっと考えよう。

(18:28)

ripper (9) lib/**/*.rb

lib 以下のライブラリを片端からパースして再現できるかどうか試してみた。

まあ当然と言うかやっぱりと言うか、diff が出るね。

  • コメントまわりの空白がおかしい
  • __END__ 以降が捨てられてる

後者は気付いてたし、直すのも簡単。

前者は何だろうなあ。また yylex をぐるぐるまわらないといけないのか……。 でもさすがに疲れたし、原稿書きに戻りたいんで、続きはまたこんど。

しかし、これ以外に何も出ないというのは結構収穫だ。 SEGV もしなかったしな (といっても SEGV しやすいのはパースイベントで、 今回のテストはパースイベントを使っていないのであまり当てにはならない)。

(18:41)

ripper (10) usage

そーいや Ripper の使いかたって書いたことなかったんでちょっとだけ書いとく

基本

# けーしょーします
class MyParser < Ripper
  # イベントハンドラをていぎしてみます
  def on__comment(tok)
    print tok
  end
end
 
# きどうします
MyParser.new(ARGF).parse

いじょ

一歩進んでみる

  • イベントにはスキャナイベントとパーサイベントの二種類がある!
  • イベントを拾うメソッドがイベントハンドラ (event handler)
  • すべてのイベントハンドラは on__XXXX という名前
  • スキャナイベントは引数一個だ
  • パーサイベントの引数の数はイベントによる。Ripper::PARSER_EVENT_TABLE を見よ
  • あらゆるスキャナイベントをまとめて拾うことのできる on__scan がある。 on__scan だけは引数が二個で def on__scan(event,token) になっている。 event は :on__XXXX の形式で、token はそのまんま。
  • 全パーサイベントのリストが Ripper::PARSER_EVENTS に入ってる ("on__" は付いてない)
  • 全スキャナイベントのリストが Ripper::SCANNER_EVENTS に入ってる ("on__" は付いてない)
  • Ripper を継承すると ruby がスキャンするそのままの順番で スキャナイベントが起こるので、ヒアドキュメントの前後でややこしいことになる。 トークンが文字列通りの順番で欲しいときは Ripper::Tokenizer を継承しろ!
  • スキャナイベントハンドラでは行番号と桁が有効になる。 行番号は lineno()、桁は column()
  • パーサイベントハンドラでは行も桁も当てにならないのでトークンに情報を付けておくこと
  • 構文木を作るインターフェイスは作ってる人がいるのでちょっと待つといいことがある。 でもあの AST は……いやなんでもない

(19:23)

本日のツッコミ (全3件) [ツッコミを入れる]

oxy [はじめまして。AST作者の吉田(HN:oxy)と申します。
このプログラムは自分の目的を果たすためにとりあえず書いたもので、場当たり的な所が一杯あるのは自覚しているのですが、それでも公開すれば誰かの助けになるかもしれないと公開したものです。
ですから、もしご不満があれば直しますので、今回の日記の最後の行の真意を教えていただけないでしょうか。]

青木 [え? いや、たぶんそれは違うライブラリじゃないかと思います。
ぼくが言っているのは RubyForge のレポジトリに入っているやつでした。
ちなみに RubyForge のほうに関して思ったのは、「名前を合わせて
ほしかった」というただそれだけです。
わざわざほのめかすような書きかたをしてすみませんでした。]

青木 [↑「RubyForgeにある、Ripperのレポジトリに入っているやつ」
(lib/ripper/ast.rb) のことです。メンテしているのは
mark sparshatt で、オリジナルは別にあったはず。]


2004-09-21

HDD 10 月危機説

http://www.tokyo-nazo.net/%7etester/kako/best.html#20031015

10 月は HDD が一番壊れやすい月なんだってさ。へー。

(08:15)

ripper (11) 朝令

Ripper::Tokenizer はやめて Ripper::Filter というクラスを作った。

特徴

  • スキャナイベントのみ発生する
  • 入力文字列順にイベントが発生する
  • on__scan はない
  • 「その他すべてのイベント」を表す「on__default」がある
  • ハンドラの引数が一つ増えていて、アキュミュレータっぽく書ける

……まあようするに、 うささんとこの色付けスクリプトみたいのを作るために特化したクラスです。

require 'ripper/filter'
 
class ColorizeFilter < Ripper::Filter
  def on__default(event, tok, out)
    out << tok
  end
 
  def on__tstring_content(tok, out)
    out << %Q[<span class="string">#{tok}</span>]
  end
 
  def on__comment(tok, out)
    out << %Q[<span class="comment">#{tok}</span>]
  end
 
  # 以下略
end
 
result = ColorizeFilter.new(ARGF).parse('')

と、こんなふうに書ける! これはいいなあ。

こう考えてみると、実は on__scan っていらないんじゃないだろうか。

というか、on__ じゃなくて on_ でいいんじゃないだろうか (orz じゃないよ)。

(08:53)

ripper (12) 暮改

だんだん考えがまとまってきた。 Ruby プログラムをテキストとして扱いたい人たちには Ripper::Filter があればほぼ十分だろう。 あと必要なのは一発で構文木を作るインターフェイスと、 構文木自体を自分で作りたい人向けのインターフェイスだな。 で、前者は誰ぞが作ってくれたやつがあるので、それを持ってくればよい。 後者は Ripper クラスを直に使ってもらえばいいだろう。

もともと on__scan は Ripper::Filter みたいな用途のために付けたわけだが、 定数 SCANNER_EVENTS を付けたことで必要なくなったと考えていい。 on__scan は semantic value の扱いに関してもうまくないので邪魔だ。 やはり on__scan は捨てよう。

on__ のほうはちょっと迷うけど……。 うーん、どうしようかな。on_ でいいかなあ。 誰か決めてー!

(09:09)

ripper (13) on__

ちなみになんで on_ じゃなくて on__ かと言うと、 「Ripperは継承しないといけないし on_ だと他のモジュールのメソッドと重なりそうだよなー」 という程度の理由しかなかったりする。

(09:13)

setup.rb 3.3.0

どさくさに紛れて setup.rb をアップデートしてみた。 評判の悪い config setup install をついにあきらめて

$ ruby setup.rb

で全部やるようにした。 もちろん元の config setup install でもできるので 細かく制御したければそっちを使ってください。

ついでに

$ ruby setup.rb all --prefix=$HOME

なんてのもできるようにした。

(12:16)

ripper (14) AST

http://mono.kmc.gr.jp/~oxy/w/hiki.cgi?ast

そうか、「AST」というライブラリが別にあったんですね。失礼しました。 昨日のぼくの発言は一般名詞の「AST (Abstract Syntax Tree)」のことでした。 Ripper はしばらく RubyForge に入っていたんですが、 そこの lib/ripper にも ast.rb があるんです。

んでこいつの作りがいまいち好きになれない。 特にライブラリとしての名前付け規則に従ってないのがよろしくない。 ファイルが ripper/ast.rb でメインクラス名が Parse::Tree で その他に module Ruby が定義されていて、 一方実際に構文木を作るのは ripper/genast.rb になってる。 もうちょっと整理してほしいんだけどなあ。

(12:22)

本日のツッコミ (全4件) [ツッコミを入れる]

ささだ [ripper って,パース時の環境ってどう扱ってるんですか? ローカル変数とか.]

青木 [parse.y を FIXME で grep するとよいことがありそうです]

ささだ [こわくて checkout 出来ないのです(ぉ]

oxy [なるほど。ripperの中にast.rbが有ったんですね。昨日は早とちりをしてしまったみたいで、こちらこそすいませんでした。]


2004-09-22

ripper (15) 仕様大変更

キャプションの番号がどこまで行ったかよくわからなくなってきた。

Ripper の仕様を昨日言ったように変更しました。

  • on__scan 廃止
  • on__XXX → on_XXX

うーむ、lib/ripper/ast.rb って LGPL なのか。 これだと Ruby にはとりこめないな。

(15:38)

ripper (16) Ripperとは何か

そういえば「Ripperとは何か」について 書いたことがないような気がするので書いておく。 一言で。

Ripper は Ruby プログラムのパーサベアボーンキットだ!

え、ベアボーンキットって知らないなあ。 あれだよあれ、自作パソコンのケースと 電源とマザーボードだけセットにして売ってるやつ。 あとは自分で CPU とメモリと HDD を買って付けるとできあがるんだよ。

Ripper も同じ。 Ruby プログラムのパーサの一番面倒でどーでもいいところが ライブラリ化されていて、あとはおいしいとこだけ作ってやれば パーサができるわけですよ。

もちっとわかってる人向けにわかってる説明をすると、 まず lex と yacc、わかるね? んで Ruby のパーサを lex と yacc で書いたとして、 そのアクション以外を提供してくれるのが Ripper。 あとはアクションを (Rubyで) 書けばいい。

そんでは、Ruby のパーサとしては他にもいろいろあるのに Ripper を選ぶ理由は何だろうか。 それは、Ripper は本物の Ruby との文法の互換性がとても高いからだ。 なぜならばパーサの根幹部分を本物の Ruby と共有しているからである。

なにい、「共有しなくても互換性くらい保てるだろヴァーカ!」だと? ふぅ―――――――ん。 ま、やるのは俺じゃないしね……やるだけやってみればいいんじゃない? その……若い頃に無駄な努力をしてあがくのって大切だと思うし! あ、それから Ruby の文法はどんどん アップデートして (複雑になって) いくことを忘れずにね。

一方 Ripper の弱点は以下のようなところだろう。

  • 構文木を作ってくれるインターフェイスが (まだ) ない
  • 動作が (まだ) 不安定
  • 仕様が (まだ) 不安定
  • 後述するように、いまんとこ ruby CVS HEAD でしか使えない

ripper (17) Ripperでできること

御託を並べるのはこのへんにしよう。 具体的に、Ripper ではどんなものが作れるのだろうか。 ようするに特製パーサが作れるわけだから、 Ruby プログラムをテキスト的にいじるものはだいたい何でもできる。 例えば以下のような例が挙げられる。

  • ソースコードの動的色付け
  • ソースコードの静的色付け
  • Ruby 用 indent
  • Ruby 用 lint? (真面目にコード解析をやると大変なので表面的なものだけにしたほうがよさげ)
  • rtag
  • Ruby Refactoring Browser は Ripper の前バージョンを使ってるらしい
  • Ruby コンパイラのパーサ部分
  • irb みたいなの
  • rdoc みたいなの

ripper (18) Ripperを入手する

さて、解説を読んで Ripper を使いたくなったとしよう! どこからダウンロードできんだよーとっとと URL さらしやがれ、 と思うかもしれないが、そういうわけにはいかない。現在のところ、 Ripper を使ってみたかったら ruby の CVS HEAD を自分でインストールする必要がある。

[試験に出るポイント]

  • Ripper は個別には配布していない。
  • Ruby の CVS HEAD をチェックアウトしてコンパイルすると勝手にインストールされる。
  • Ruby 1.8 では (まだ) Ripper は使えない。

なぜ 1.8 がだめかと言うと、yacc の定義するトークン (を表す定数) が Ruby 本体のパーサと完璧に同じでないといけないからだ。 1.9 ではトークンが追加されているので 1.8 に合わせるのはけっこう大変なのである。 とは言え 1.8 の parse.y に独自に ripper extension を追加するよりはずっと簡単だ。

# なんか今日はテンション高いなー俺……

(17:13)


2004-09-23

ネ申

「ネ申」って二文字合わせて「神」のはずだけど、 どうしても「ネかみ」と読んでしまうんだよなあ。 なんでだろう?

(15:23)

Tru64/Alphaでruby SEGV (1)

うう、Alpha で ruby が落ちる……。 yyparse の下で落ちてるのが俺が原因ぽくてすげえ嫌だ。

(17:54)

Tru64/Alphaでruby SEGV (2)

CC=cc CFLAGS=-g だと落ちねえ。おのれ。

(17:55)

Tru64/AlphaでrubyがSEGV (3)

gcc -O2 は大丈夫なのがさらに気に食わん。 コンパイラのバグだったら嫌すぎ……。

(18:53)

Tru64/AlphaでrubyがSEGV (4)

ちょっといじるだけで落ちる場所が変わる。だめだこりゃ。 日和って parse.y だけ -g でコンパイルしておこう。

(19:24)


2004-09-26

HDD 交換

ようやく HDD を換えられた。 買ったのは Seagate の ST3120026A (ATA 120GB)。 二つあった HDD を一つにまとめられてスッキリした。

しかし、Windows だとどんなにでかい HDD でも 平気で一つのパーティションにするんだけど、 Linux だとなぜか細かく区切らないと気がすまないんだよな。

~ % df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/hda5             1.7G   88M  1.5G   6% /
none                   64M  4.0k   63M   1% /tmp
/dev/hda6              16G  2.7G   13G  17% /usr
/dev/hda7              18G  7.4G   10G  43% /home
/dev/hda8              64G  9.7G   51G  16% /d
/dev/hda9             9.2G  2.6G  6.1G  30% /var/backups
/dev/hda1              99M  3.5M   90M   4% /boot

げ、/home が小さすぎたか。

(01:51)

printfのアドレス

Solaris には pmap てコマンドがあって、 プロセスのメモリマップを表示してくれる。 これがいいなーと思ってたんだけど、 Linux の procps をアップデートしたら pmap が入っていた。 そこでさっそくいろいろ実行してみる。 ついでにいろいろなアドレスも表示しとく。

~ % ./src/show-vmmap
extern global   = 08049bf8
static global   = 08049bf4
function static = 08049bf0
function local  = bffff608
malloc mem      = 40140010
alloca mem      = bffff484
main()          = 080486d0
printf()        = 080485a0
dl sin()        = 40251360
1893:   ./src/show-vmmap
08048000      4K r-x--  /show-vmmap        ← printf()
08049000      4K rw---  /show-vmmap
40000000     76K r-x--  /ld-2.2.5.so
40013000      4K rw---  /ld-2.2.5.so
40014000      4K rw---    [ anon ]
4001b000      8K r-x--  /libdl-2.2.5.so
4001d000      4K rw---  /libdl-2.2.5.so
4001e000   1116K r-x--  /libc-2.2.5.so
40135000     24K rw---  /libc-2.2.5.so
4013b000   1048K rw---    [ anon ]
40248000    128K r-x--  /libm-2.2.5.so
40268000      4K rw---  /libm-2.2.5.so
bfffe000      8K rwx--    [ stack ]
 total     2432K

まあだいたいは明らかっていうかあたりまえなんだけど、 興味を引かれたのが printf のアドレス。 よく見ると libc ではなくて show-vmmap の テキスト領域にあることになっている。

それでは Linux/Alpha はどうかと言うと、

dpw600au:~ % ./show-vmmap
extern global   = 0000000120011278
static global   = 0000000120011274
function static = 0000000120011270
function local  = 000000011ffff5f0
malloc mem      = 00000200001d8018
alloca mem      = 000000011ffff5b8
main()          = 0000000120000960
printf()        = 00000200000bcb60
dl sin()        = 0000020000303e80
 
(pmapがなかったんで/proc/XXX/mapsから)
000000011fffe000-0000000120000000 rwxp
0000000120000000-0000000120002000 r-xp /home/aamine/c/linuxintro/src/show-vmmap
0000000120010000-0000000120012000 rwxp /home/aamine/c/linuxintro/src/show-vmmap
0000020000000000-000002000001e000 r-xp /lib/ld-2.2.5.so
000002000001e000-0000020000022000 rw-p
000002000002c000-000002000002e000 rwxp /lib/ld-2.2.5.so
000002000002e000-0000020000032000 r-xp /lib/libdl-2.2.5.so
0000020000032000-000002000003e000 ---p /lib/libdl-2.2.5.so
000002000003e000-0000020000042000 rwxp /lib/libdl-2.2.5.so
0000020000042000-00000200001b4000 r-xp /lib/libc-2.2.5.so   ← ここ
00000200001b4000-00000200001c2000 ---p /lib/libc-2.2.5.so
00000200001c2000-00000200001d2000 rwxp /lib/libc-2.2.5.so
00000200001d2000-00000200001d8000 rwxp
00000200001d8000-00000200002da000 rw-p
00000200002da000-0000020000364000 r-xp /lib/libm-2.2.5.so
0000020000364000-000002000036a000 ---p /lib/libm-2.2.5.so
000002000036a000-0000020000378000 rwxp /lib/libm-2.2.5.so

libc のテキスト領域にあるように見える。普通だ。 もしかして位置独立だとこうなるのかなー (Alpha はデフォルトで PIC だから) と思って i686 のほうを -fPIC でコンパイルしてみたんだけど、 変化はないようだ。

ふーん。何かスタブが入ってるのかねえ。 OK, ディスアセンブルだ。

% objdump -d show-vmmap
                     (略)
080484f0 <.plt>:
 80484f0:       ff 35 90 9b 04 08       pushl  0x8049b90
 80484f6:       ff 25 94 9b 04 08       jmp    *0x8049b94
 80484fc:       00 00                   add    %al,(%eax)
 80484fe:       00 00                   add    %al,(%eax)
 8048500:       ff 25 98 9b 04 08       jmp    *0x8049b98
 8048506:       68 00 00 00 00          push   $0x0
 804850b:       e9 e0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048510:       ff 25 9c 9b 04 08       jmp    *0x8049b9c
 8048516:       68 08 00 00 00          push   $0x8
 804851b:       e9 d0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048520:       ff 25 a0 9b 04 08       jmp    *0x8049ba0
 8048526:       68 10 00 00 00          push   $0x10
 804852b:       e9 c0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048530:       ff 25 a4 9b 04 08       jmp    *0x8049ba4
                     (略)
 8048596:       68 48 00 00 00          push   $0x48
 804859b:       e9 50 ff ff ff          jmp    80484f0 <_init+0x28>
 80485a0:       ff 25 c0 9b 04 08       jmp    *0x8049bc0    ← ここに飛ぶ
 80485a6:       68 50 00 00 00          push   $0x50
 80485ab:       e9 40 ff ff ff          jmp    80484f0 <_init+0x28>
 80485b0:       ff 25 c4 9b 04 08       jmp    *0x8049bc4
 80485b6:       68 58 00 00 00          push   $0x58

printf は 080485a0 なので、いきなり jmp 命令か。 えーと、

jmp    *0x8049bc0

って何だろ。あ、そうか、 「ポインタ 0x8049bc0 をデリファレンスしたアドレスに飛ぶ」だな。 0x8049bc0 は show-vmmap の BSS 領域だから、 ld.so がここに printf 本体のアドレスを置いてるんだろう。 OK, 自力で printf 本体をゲットするぜ。

こんな感じかな?

~ % cat x86resolve.c
#include <stdio.h>
#include <stdlib.h>
 
int
main(int argc, char *argv[])
{
    typedef int (*printf_t)(const char *fmt, ...);
    printf_t myprintf;
    char *p;
 
    p = (char*)printf;
    p += 2;   /* skip instruction */
    printf("BSS address     = %08lx\n", (unsigned long)(*(long*)p));
    myprintf = (printf_t)(*(long*)(*(long*)p));
    printf("printf() body   = %08lx\n", (unsigned long)myprintf);
    myprintf("test %s\n", "OK");
 
    exit(0);
}
~ % ./x86resolve
BSS address     = 08049648
printf() body   = 4006f8ec
test OK

おおっ。うまくいってしまった!

というか .plt って何の略やねん。 …… ld.so(1) によると Procedure Linkage Table らしい。 なるほどね。

このへんまで書いたところでずっと詳しいページを見付けた。

こうなると問題にすべきは 「なんで x86 では libc のテキスト領域を指していないか」 じゃなくて、 「なんで Alpha では libc のテキスト領域を指しているか」 のような気がするな。

……

そうか。 Alpha は最初からグローバルポインタテーブルを使ってるせいだ。 もともと間接参照してるんだからもう一段入れる必要はない。

(01:57)

a

なぜわたしは突然 Alpha アセンブラを書き始めているのでしょうか

        .file   1 "put.c"
        .set noat
        .set noreorder
.section        .rodata
$LC0:
        .ascii "Hello, World!\12\0"
.text
        .align 5
        .globl main
        .ent main
main:
        .mask 0x4000000,-16
        ldah    $29,9($27)
        lda     $29,4896($29)
$main..ng:
        lda $30,-16($30)
        lda $17,$LC0
        lda $16,1
        lda $18,15
        lda $0,4
        callsys
        stq $26,0($30)
        .prologue 1
        mov $31,$16
        lda $16,3
        lda $0,1
        ldq $26,0($30)
        nop
        lda $30,16($30)
        ret $31,($26),1
        .end main

だめだ落ちる……。 とりあえず gcc のインラインアセンブラから始めて _exit(2) は呼べるようになったんだけど、 write(2) を呼ぼうとした瞬間に落ちた。 gp (global pointer) にまともな値が入ってないから、 リテラル (BSS) を参照した瞬間に落ちるようだ。

くそう、BSS がだめならスタックに積んでくれるわ!

~ % cat put.s
        .set noreorder
.text
        .globl main
        .ent main
main:
        lda $16,10              ; a0 ← '\n'
        stb $16,0($30)          ; push a0
        lda $16,97              ; a0 ← 'a'
        stb $16,-1($30)         ; push a0
        subq $30,1,$17          ; a1 ← sp
        lda $16,1               ; a0 ← 1 (STDOUT_FILENO)
        lda $18,2               ; a2 ← 2
        lda $0,4                ; v0 ← 4 (NR_write)
        callsys
        lda $16,7               ; a0 ← 7
        lda $0,1                ; v0 ← 1 (NR_exit)
        callsys
        .end main
~ % gcc -static -nostdlib -Wl,--entry=main put.s -o put
~ % ./put
a
~ % echo $?
7

うごいた!

(02:59)


2004-09-27

HSPとか

HSP とかが goto メインなのはセーブ・ロードの実装が 簡単だからじゃないのかなあ。 吉里吉里 (のKAG) では if の中にラベルを書けない (セーブポイントにできない) っていう制限があるけど、 あれもつまりそういうことだろう。 ゲームプラットフォームとして、if/while を取るか セーブ機構を取るかという選択ならセーブ機構になるよね。

(00:43)

gotoとセーブ・ロード

ツッコミ欄は狭いのでこっちにします。

> 自分でセーブ機能を実装するってこと?
> システムがその時点のスナップショットを
> 取っておく機能を持つってのはあるような
> 気がしますから、あんまり関係ないような
> 気がします。

うーん、うまく説明できるかな。 例えばありがちなビジュアルノベルのスクリプトを例にすると、 次のように書いたとして (微妙に Ruby 風)、

a = true
if フラグチェック
    背景1表示
    立ち絵1表示
    テキスト1……
    b = true
    <<<セーブポイント1>>>
    背景2表示
    立ち絵2表示
    テキスト2……
end

セーブポイント 1 でセーブして別プロセスでロードしたときに、

  • a=true, b=true
  • 背景1、立ち絵1

の状態でセーブポイント 1 から始められる、というのが理想です。

しかし例えば Ruby で上記のように書いたとすると、 データは特定のオブジェクトのインスタンス変数で妥協すればなんとかなりますが、 if 文の途中にすっとんでいけません (callcc ではプロセスをまたげない)。

また KAG3 でも上記のような書きかたは禁止されており、 次のように書かないといけません (文法は違います)。

フラグ a = true
if フラグチェック
    背景1表示
    立ち絵1表示
    テキスト1……
    フラグ b = true
end
<<<セーブポイント1>>>
if フラグチェック
    背景2表示
    立ち絵2表示
    テキスト2……
end

これが「if の中にはラベルを書けない」という制限です。 その代わり、この制限にのっかっていれば goto ラベルを書いとくだけで セーブ・ロードを処理系が面倒見てくれます。 変数にも「勝手に保存される変数」と「ゲーム終了ごとに中身が消える変数」 があって、「勝手に保存される変数」はロードすると勝手に復帰します。

それでは、if 文 (やサブルーチン呼び出し) の中に セーブポイントを置けないもんでしょうか。 できないことはないでしょうが、関数呼び放題、if/while 使い放題の言語で 普通に continuation を保存してしまうとデータの融通が効きません。 例えばちょっとスクリプトが変わっても同じセーブデータを使いたいとか、 どこでセーブしたのか情報を取り出したいとか。 それだったらちょっとくらい制御構造を妥協してもいいんじゃないか、 と言いたいのです。

まとまらねえな。

(04:25)

本日のツッコミ (全2件) [ツッコミを入れる]

ささだ [自分でセーブ機能を実装するってこと? システムがその時点のスナップショットを取っておく機能を持つってのはあるような気がしますから、あんまり関係ないような気がします。

if の中でラベルを書けないっていうのは、よくわからないなぁ。なんか根本的な勘違いしてそうですけど。]

ささだ [注意しとくと、勘違いってのは、私がって意味ですね。

フラグとかのデータについては、あんまり関係ないですね。それはいいとして。背景データとか、その辺も、同じデータ構造の問題になりますね。

if文で駄目な理由がやっぱりわからないなぁ。例えば自分が処理系を作ったとして、どうしてそこで詰まるのかが思い浮かびません。保存しなきゃいけない状態は、トップレベルと変わらないような気がして。

もちろん、関数コールの途中とかだと、スタックの保存とか考えなくてはならなくて、面倒ってのは散々味わいました(え?)けど、if文の途中ってのが、ちと想像できません。

考えている、要求と制限で、色々と前提が変わっていて、話が合ってないだけな気もします。

ゲーム開発をわかりやすく(シンプルに)するための制限、として if文中でのラベルなしってのはあるかもしれないけれど。システムとして、そこに課題があるんだろうか。

なんか、私の知らない「状態」が存在したりするのかも。]


2004-09-28

gotoとセーブ・ロード (2)

ああそうかわかった、やっぱごちゃごちゃに書きはじめたのがまずかったんだ。

まず、「if の途中にラベルがある『から』ダメ」というのはぼくの勘違いです。 ちょっと自分で混乱して話を混ぜてました。 if の途中でセーブしたって復帰できねえじゃん、 というのは Ruby に関してだけです (もちろん、工夫しなきゃトップレベルでもだめだけど)。 最初は Ruby と HSP の比較で書いてたもんで。

あともう一つは、C みたいに if でスコープが積まれる仕様を 知らないうちに想定してたせいです。 if の中で b に代入してるのはそのへんを表現したかったから。

で、サブルーチンがあって if/while があってローカルスコープがあっても ちゃんと保存すれば完全に復帰「しようと思えばできる」というのはわかってます。 ソースコード変更に追従「しようと思えばできる」のもわかります。 言いたかったのは「できるか」じゃなくて、 そこらへんの事情を考えればいまどき goto バリバリな言語を作るのも 開発者のトレードオフの範囲だろう、ということです。

うーん、やっぱ吉里吉里の話を出したのはよくなかったな。 Ruby と HSP だけなら話は早かったんだ。

(14:07)

本業を思い出した

というか、goto について語りあう前に俺は原稿を書くべきです。

ああ、このままでは夏休みが終わってしまう。 まだ宿題のプリントが終わってないのに。

……俺はしょうがくせいか。

(15:47)

FlexWiki

http://sourceforge.net/projects/flexwiki/

MS 謹製 Wiki エンジンだってさ。 ダウンロードしてみると C# で書いてあることがわかる。 誰か動かしてる人いないかな〜

(23:29)

FlexWiki (2)

ソースコードがタブでインデントしてあるのを見たらなんとなく笑えた。

(00:15)

本日のツッコミ (全4件) [ツッコミを入れる]

shugo [monoで動かそうかと思ったんですけど、firefoxでイマイチらしいので萎えました。]

青木 [なんと……。こんなとこまで IE 縛りですか。やるな Microsoft。]

sakazu [こんなサイトを見つけました
http://www.flexwiki.jp/]

青木 [公式サイトができたんですね。
ありがとうございます。
# ざっと見た感じ、ごく普通のWikiに見えるな……]


2004-09-30

BitChannel関係

いつになったら BitChannel にまで手が回るのだろう。

それはそれとしてやっぱり ML 作ろうかなあ。

(20:19)

久しぶりにドキドキ

9/17 賞味期限のソーセージ

を食べ

た。な

んか

、冷汗が出るんだ

ど大丈夫なん

だろうか。

(01:46)

初黒星

うう……ちょっと吐いた……

(02:03)

Linux本

全然書けてないと思ってた hier の章が実は意外に埋まっててラッキー、 と思いつつも中間演習 (grepを作る) の章は全然終わってないし ましてや HTTP サーバの章はスカスカなのであり 絶対に今日の 24:00 には間に合わないっていうか すでに過ぎてるわけだから間に合うほうがおかしいっていうか、 そんな感じの気分です。

(02:08)

本日のツッコミ (全6件) [ツッコミを入れる]

*namu* [ソーセージ…参考になりました。
お大事に。]

通りすがれ [bitchannel を co しようとしたらエラーになってしまいます orz

cvs update: failed to create lock directory for `/src/bitchannel/test' (/src/bitchannel/test/#cvs.lock): Permission denied]

ささだ [どこを縦読みするんでしょうか。]

anonyomous [9をたん]

青木 [16文字目あたりかな]

青木 [あ、BitChannelのチェックアウトエラーだけはこっそり直しておきました。]


<前月 | 最新 | 次月>
2002|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|04|05|06|09|10|
2009|07|
2010|09|

Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved. LIRS