メイン

Javascript アーカイブ

2007年11月22日

JSRuby - Javascript による Ruby(っぽい)実装

OreScript時代の幕開け - yukobaの日記
http://d.hatena.ne.jp/yukoba/20071108/p1

巷で OreScript ブームが巻き起こる中、某所ではさらに局地的に Javascript での言語実装ブーム。

××さんが ******* を試作したり、西尾さんが python4js を試作したりして、うーむこんなおもしろそうなお祭りは看過できんでわないか。Ruby 成分の多い中谷としてはここはやっぱ Javascript で Ruby かっ!?

そういえばだれか Javascript でパーサ書いてはったよなあ。それを使わせてもらえば Ruby の構文木をさっくり得られて、ちょこちょこっとインタプリタ書いたら、FizzBuzz くらいなら動くんじゃないの? という思いついてしまったからには、もう作ってみるしか。


というわけで言った者勝ち(笑)の JSRuby をこしらえてみました。

JSRuby テストページ

リンク先のページで簡単な演算や FizzBuzz の実行テストを行えます。一応 IE6, Firefox2, Opera9, Safari3β で動作確認はしてみました。Safari2 では RegExp.rightContext がサポートされていないそうなので動きません(amachang サンクス)。すいません。

コードは <script type="text/ruby"> で埋め込んであるのを拾って textarea に出しているので、編集して実行することもできるようにしてあります。

んが、正直今のところ「 Ruby で書かれた FizzBuzz を実行できる、Ruby っぽい何か」なので、普通の Ruby スクリプトを入れてもまだまず動きません(苦笑)。


実装範囲は

・演算子 +, -, *, /, ==, ..

・演算の優先順位は有効

・数値、文字列リテラル

・代入

・組み込み関数 puts

・if ~ then ~ elsif ~ else ~ end

・配列に対する each メソッド

以上! って本当に FizzBuzz しか見てません……

とはいえ一応代入も if もちゃんと値を返すので、a=b=4 とか puts if x % 2 == 0 then "even" else "odd" end という書き方はちゃんとできます。


実装済みの範囲でも怪しいところはちらほら。特に前者はやばめ。

・パーサの時点で識別子をメソッドか変数か決定してしまっている(ミスった)

・Ruby の型を扱っていない(整数の割り算が実数になる、 .. 演算子が(javascriptの)配列を返す、など)


あと残念ながら、パーサは当初考えていた Gin ではなく、自前で再帰下降パーサを手書きしてます(隠れ左再帰を見つけられず、too much recursion を解消できなかった……)。


まあ半分ネタとはいえせっかく作ったので、上に書いた識別子の問題を解消し、ソースを分割、テストコードを付け、CodeRepos にアップさせてもらえれば、みんなでよってたかってつついてくれて、あれーいつの間にかできてる! とか妄想中w

フル実装は夢としても、どう書く?org の Rubyコードの7割がたが動く、くらいならかなり現実的な気もしますです。


この状態で使ってみようという人はいないと思いますが、簡単な使い方。

jsruby.js を組み込んで、

var parser = new RubyEngine.Parser();
var ruby = new RubyEngine.Interpreter();
ruby.run(parser.parse("puts 2*3+4*5; 9*9*9"))

とやれば指定の ruby コードを実行します。

ruby.run() の返値は最終の式の評価なので、この場合 729 が返ってきます。

また、標準出力の値は ruby.stdout から取り出せます(この場合 "26\n" が入っている)。

標準出力への書き出しは ruby.writeStdout() を上書きすることで変更できます。例えば ruby.run() の前に

ruby.writeStdout = function(st){ alert(st); }

と入れれば、都度ダイアログで表示されます。

2007年11月29日

JSirb - Javascript で Ruby コンソール

JSRubyCodeRepos に登録すべく、「いじってもらう前にここだけはちゃんとしとかんとなー」というところをせっせと作り込んだり、機能テストをちまちま書いたりしています。
これがまあ結構忍耐のいる作業なもので、ちょっと飽きちゃったりすることも。
というわけで気晴らしにさっくり JSirb なんて作ってみました。

JSirb - JSRuby console

名前の通り、Webページに埋め込める JSRuby のコンソール。サンプル( (1..10).each…… ) の入っているテキストボックスで Enter を押すと実行されます。
ただし機能的には前回と変わっていないので、まだまだほとんどの Ruby の機能は使えません(すんません~)。
まあまだあまり作り込んでいないので、出力やエラーがあまあまなのはご勘弁。

2007年11月30日

JSRuby を CodeRepos に commit しました

とりあえず FizzBuzz が動くだけだった Javascript によるRuby 実装な JSRuby ですが、「全然できあがってなくても、まず公開」という教えもあることですし、とっとと CodeRepos に登録してしまおう~

と思っていたのですが、メソッドの実装まわりや、Ruby のオブジェクトの概念の導入など、構成に大きく影響を与えそうな上にまっさきにやらないといけないことくらいは片付けないとなあ。さすがに全体が1ファイルだと、せっかく手を入れようと思ってくれた人がいても衝突しまくりだし。最低限のテストセットも用意しておいた方が話も早いだろうしなあ。

とごにょごにょ作業していたのですが、ようやくそこらへんが片付いたので、CodeRepos に commit させてもらいました。

http://coderepos.org/share/wiki/JSRuby

ライセンスは JavaScript-XPath にならって MIT ライセンスとしています。

機能的なサポート範囲は先日の「 FizzBuzz が動くだけの Ruby っぽいなにか」から大きく変わってはいませんが、数値や文字列が「 Ruby のオブジェクト」として扱われるようになり、それらのメソッドも実装できるようになっています(サンプルとして Integer#chr と String#reverse は実装済み)。これで「 FizzBuzz が動くだけの Ruby のサブセット」に昇格したんじゃないかと(笑)。
またいろいろ細かいバグも取れてます。

TODO はまだまだいっぱいあって、
・エラー処理
・未実装の演算
・未実装の組み込み関数
・未実装の組み込みオブジェクト&それらのメソッド
・スコープ
・メソッド定義
・モジュール定義
・クラス定義
・……

とまあ道のりは長いのですが、とりあえず「どう書く.org に組み込んでもらって、Ruby のコードならブラウザ上でそのまま動作」というのを目標にしようかなあなどなど夢見ています。

よろしければガンガン commit してやってください。

2007年12月05日

jjsruby - JSRuby for Rhino (笑)

id:amachang わーい \(^o^)/ JS で書ける IRC ボットライブラリできたよー

(Rhino) たのしー \(^o^)/

というのに当てられて、タイトルですでにネタバレしているとおり、JSRuby を Rhino で試せるようにしてみました(笑)。
といっても、id:tokuhirom さんが commit してくれた repl.js をちょこっといじっただけですが(笑)。


(1) CodeRepos から JSRuby をぱしっと取ってくる。
(2) Java(5 以上?)や Rhino もいれておく
(3) jsruby の trunk の下で下記実行 ( js.jar のパスは適宜指定 )

java -cp ./js.jar org.mozilla.javascript.tools.shell.Main  tools/jjsruby.js

これで対話的に Rhino 上で javascript ruby のコードを実行できます( Rhino で Javascript が実行できるのは当たり前ですね、とほほ……)。

一番苦労したのは Rhino から標準入出力をどうやって読み書きするか(笑)。



追記:Rhino で標準入出力


せっかくなのでメモ。
Rhino では print() が組み込まれていて、標準出力に文字列を出力することが出来ますが、自動的に改行されてしまうため、改行を付けたくない文字列出力(プロンプトとか)には使えません。
また、SpiderMonkey には readline() が組み込まれていて標準入力から文字列が取れますが、Rhino にはそれっぽいのは無さそうです(見つけられなかっただけで、もしかしたらあるのか?)。

Rhino からは Java のクラスが使えるのでそっちを経由して標準入出力を取りたいところ。
java.lang.System に in と out というフィールドがあり、これらがそれぞれ stdin / stdout に対応しているのでこれを取れればOK。
でも java.lang.System.in と書くと "missing name after . operator" と怒られてしまう。正解は java.lang.System["in"] 。

というわけで jjsruby では以下のように使いました。

importPackage(java.io);
var stdin = new BufferedReader(new InputStreamReader(java.lang.System["in"]));
var stdout = new OutputStreamWriter(java.lang.System["out"]);

(中略)

stdout.write("> ");stdout.flush(); // プロンプト表示
while (line = stdin.readLine()) { // 一行入力
var nodetree = parser.parse(line)
print(ruby.exec(nodetree));
stdout.write("> ");stdout.flush();
}

stdin は readLine() が使いたかったので BufferedReader でくるんでます。
stdout は短い文字列を出力しただけだと表示されないので、すぐ flush() しています。

About Javascript

ブログ「nakatani @ cybozu labs」のカテゴリ「Javascript」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

次のカテゴリはPathtraqです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。