1月分ですら仮エントリなのにどうするんだろう。
今回は毎年恒例? らしい Smalltalk ユーザーズグループのエイプリルフール企画、SmallTalk ((Tが大文字なことに注意(笑))) R41? のネタだし&仮実装。
この SmallTalk という処理系は昨年のエイプリルフールに作成されたもので、リンク先を見てもらうと分かるとおり抱腹絶倒の冗談拡張です。
んで、注目すべきは、こういう冗談拡張な言語を、言語の外に出ることなく(つまりソースコードをリコンパイルとかそういうことなく)できてしまう Smalltalk の柔軟性なんですな。
Smalltalk というのは、Lisp なんかも近いところがあるけど、非常に深いところが Smalltalk 自体で書かれています。
まあ私自身も勉強中なのでウソかもしれませんが、字句解析、構文解析、そしてそれをバイトコードに落とす部分まで Smalltalk 自身でかかれているのです。
のでやる気になればオレオレ文法を実装する、言語拡張が比較的容易にできる言語なのです。
……が、この解析系がけっこうハードコーディングバリバリで、lex/yacc みたいな道具になれたゆとりな人にはちーと敷居が高い。
SmaCC みたいなメタコンパイラライブラリを使えばもちょっと楽になれそうだけど……そういうのなしでやるにはちと時間が足りませんでしたね。
週末使って一日でハッカソンっぽくやるならイケたかもしれませんが(むろん、ペアプロでですよ)。
ネタ
色々出たけどメモとかとってないから忘れちゃった。SmallTalk R4.1 では With: 32個だったけど無限にしたら? あ、でも Smalltalk VM が引数32個しかサポートしてないからダメだ、とかいうのがあったかな。
ぼくが思いついたのはリスト内包表現による無限長配列とかだったけど、なんかありそうで結局言わなかった。
実装しよう、という話になったのは
- インナークラス
- あるクラスの中でのみ定義できて、外からは参照できないクラス
- for文
- そのまんま。
三組あったので三つ課題があったはずなんだけど、あれえっ、もう一つ出てこない。
我々は for 文をやることになりました。
for 文
そもそも「どの for 文をエミュレートするか」という議論からしたんだけど眠いので委細省略。Cのfor文をまずはSmalltalkっぽく作って、それをCっぽくかけるかどうかは後で考えよう、というアイディアでスタート。
まあ、これは一瞬ですわな。
まず Class に For ってクラスを作ります。Object 直下。
そうすると、こんな感じのクラスメソッドがありゃーいいんじゃないの? と思うわけです。
For init: [initializer] while: [condition] step: [stepBlock] do: [doBlock]
つことでそのまんまクラス定義にしてみます。実体はとりあえず self halt にでもしときましょう。
init: initializer while: conditionBlock step: stepBlock do: doBlock self halt.
んで、workspace で試しに実行。
| i | For init: [i := 15] while: [i > 2] step: [i := i - 3] do: [Transcript show: i printString; cr]
do に一生懸命値渡してますが結局実行されないから意味無し。
あ、あと C の For 文なので外部にカウンタ変数を定義しなきゃいけないのは仕様です(カウンタ名が Smalltalk っぽくない意味のない値なのも C を意識してます)。
まあ実行結果をはるのはタルいので止めますがちゃんと止まりました。メソッドの定義としてはオッケーと。
さて次は中身ですが、
- まず initialize ブロックを実行して
- doBlock と step の実行結果を condition が成立する間ぐるぐる回って
- まわり終わったら終わり
ですよねー。問題はオイラが「ブロックを実行する」方法をまだ知らんことです。
そういうときは Smalltalk に聞くべし。
適当なペインに [a :=1] とか書いて inspect it して、self でクラス調べて、クラス名選んで Alt-B でクラスブラウザ開けて、それっぽいメッセージを探す。
お、value なんてのがある。これだこれ。
二つのブロックの実行結果をまとめてぐるぐる回す、は、「ブロックをそれぞれ value したのをまたブロックで囲んで、それをぐるぐる回す」と読み替えられますね。
「……までぐるぐる回る」は Smalltalk のイディオムとして、大体 Boolean クラスのメソッドとして実現されてます (ifTrue: とかもそう)。ということで探すと whileTrue: ってのがある。よしこれだ。ということでできたのがこれ。
init: initializer while: conditionBlock step: stepBlock do: doBlock initializer value. condition whileTrue: [doBlock value. stepBlock value]
眠さ爆発なのでもう結果とか書きませんがちゃんと動きました。
問題はこれを C っぽく実装する……のは、Squeak の Scanner クラスとか Parser クラスの実装を見て諦めました。ぼくにはまだ無理です。
だめだ。もう無理。続きは後日!