Little Smallscript
物置を掃除してたらこんな本を見つけました。
Little Smalltalk入門 1989年6月21日 初版発行
父のものなんですが、持ち出してここ2,3日通勤中に読んでました。
Little SmalltalkってSmalltalk方言の一つで、Smalltalk(GUI環境付きのやつ)の雰囲気をキャラクタ端末で気軽に味わえるように、言語機能だけを抜き出したようなものらしいです。
要するにいわゆるスクリプト言語と立ち位置は同じ? ほとんどRubyです。(Rubyが後だけど)
本にのってるのはLittle Smalltalk version 1で、こんな感じに書きます。
"クラス定義" Class Person :Animal | myname answerStr | [ new: name myname <- name. answerStr <- 'hello' , myname | name myname print. ^ myname ] "メッセージ式" (Person new: 'ympbyc') name "レキシカルブロック" #(1 2 3 4) inject: 0 into: [:a :b| (a , b) print. a + b] "カスケード式" "最初のメッセージ式の結果をレシーバとしてメッセージを送り続ける" Person new: 'ympbyc'; name; name
なんか書いてて楽しいし、javascript(飯の種)と比べて短くなりそうなのでLittle SmalltalkからJavascriptへのトランスレータを作ることにしました。CoffeeScriptのSmalltalk版みたいな感じね。
https://github.com/ympbyc/LittleSmallscript
金曜日の夜に初めて今月曜日の朝5時なんだけど、とりあえず式は大体変換できるようになった。
READMEにもあるけど、
new LittleSmallscript("下のSmalltalkソース").expression();
で
[ Person <- [ :name | this at: 'name' put: name ]. Person at: 'prototype'; at: 'answer' put: [this at: 'name']. p <- Person new: 'Yendor'. p answer. p ] value
が
(function () { Person = function (name) { return (this).atPut("name", name); }; (function () { var _receiver = (Person).at("prototype"); _receiver.atPut('answer', function () { return (this).at("name"); }); return _receiver; })(); p = (Person).new("Yendor"); (p).answer(); return p; }).value()
になる。
javascriptだとコンストラクタ関数でオブジェクト作るから、あとは一時変数宣言できるようにすれば普通にOOPできちゃう。
で、ここまで作ってさあクラス定義のパーサ作るぞーと思ったところで、Little Smalltalkにバージョンがあることを知りました。
Little Smalltalk入門のはversion 1で、Web検索でよく出てくるのはversion 3、最新版はversion 5。
式はほとんど変わってないっぽいけど、クラスの作り方がバージョン間でかなり変わってる。
このリポジトリに全バージョンのテストコードとか例とかが入ってた。 https://github.com/crcx/littlesmalltalk
まとめた。 https://github.com/ympbyc/LittleSmallscript/blob/master/docs/versions.md
見た目的にはversion 1のが好きなんだけど完全に特殊形式で、version 4以降とかだと普通の式っぽくなってる。
varsion 4以降の、メソッド定義が1個1個バラバラにあるのがjavascriptでコンストラクタ関数のprototypeにメソッド作ってくのに似てるので、これを実装しましょう。最新版だしね。
パーサはPEGっていうのを見よう見まねで実装したものを使って作ってます。PEGめっちゃ楽しい。
一部。ブロックの中の式をパースする部分。
[:hoge| "このぶぶん"]
BlockParser.prototype.blockStatement = function () { var _this = this; return this.cacheDo("blockStatement", function () { var ret =""; _this.skipSpace(); //スペース読み捨て //式がドットで繋がったのがmany(いくつか,またはゼロ個)ある ret += _this.many(function () { var a; a = _this.expression(); //式 _this.skipSpace(); _this.chr("."); //ドット _this.skipSpace(); return a + "; "; }); _this.optional(_this.explicitReturn); //ブロックの最後の式の前には^(explicitReturn)を付けられる(optional) ret += " return " + _this.expression() + ";"; //最後の式 _this.skipSpace(); return ret; }); };
乞うご期待。