Rhino上でCoffeeScriptのコードを実行するインタプリタもどきを作る

CoffeeScriptスクリプトを直接実行したい場合、CoffeeScriptコンパイラを動かしているJavaScript処理系で実行することが大半だ。

現状ではデフォルトのNode.js、Webブラウザ*1、PhantomJS*2Windows Script Host*3で実行できることは分かっている。しかしRhinoで(それもRhino単体で)実行する為の仕組みは見当たらないようだ。

そこで、思い立って気合と諦めで作ってみた。
RCSI - Rhino CoffeeScript Interpreter
コード的に大したことはしていない、気合一発ネタに近い代物である。機能は非常に限定されている(面倒なので諦めた、ともいう)。

具体的には、CoffeeScriptスクリプトファイルないし引数に指定したスクリプトの文字列をRhinoで実行する機能しかない。コンパイルしたいならNode.jsで十分だ(なので実装していない)。REPLぐらいは入れたかったけど、面倒なので止めた(花粉症が完治して毎年の悩みから解放されたら、考えなくもない)。

RCSIとは何か?

CoffeeScriptのコードをRhinoで直接実行するインタプリタCoffeeScriptコンパイラではない

長所

短所

  • 遅い。
  • エラーのハンドリングがまずい。
  • REPL未実装。

インストール

必要なもの:

  • Java Runtime Environment
  • js.jar(Rhinoに付属)
  • extras/coffee-script.js(CoffeeScriptに付属のブラウザ用?)
  1. RCSIのアーカイブ・ファイルをダウンロードし、解凍する。
  2. 解凍してできたフォルダ/ディレクトリの中(rhino-coffee-script.jsなどが置いてある)にjs.jarとcoffee-script.jsを置く。
  3. 必要なら上記のフォルダ/ディレクトリを環境変数PATHに設定する。

使い方

「rcsi -h」のメッセージを参照。

以下、実行例。

C:\tmp>rcsi -e "print i for i in [1..3]"
1
2
3

C:\tmp>rcsi -e "print i for i in arguments" foo bar baz
foo
bar
baz

C:\tmp>cat > zzzz.coffee
for i in arguments
  print i

C:\tmp>rcsi zzzz.coffee A B C
A
B
C

C:\soft\shell>_

ライセンス

zlibライセンス。

Tips

スクリプトはトップレベル直下の無名関数として実行される(はず)。

スクリプト内で他のCoffeeScriptのファイルをインクルードしたい場合に備えて、readCoffeeScriptという関数を用意している。この関数はCoffeeScriptで書かれたスクリプトファイルを読みこみ、コンパイルされたコード(JavaScript)の文字列を返す。

eval readCoffeeScript 'foo.coffee'
eval readCoffeeScript 'bar.coffee', charCode

上記のような使い方になるが、実は結構危険である。というのも、CoffeeScriptが生成するコードには自動的に追加された一時変数が含まれているが、この変数の名前が衝突する可能性がある。外部モジュールのロード的な使い方をしたいので、readCoffeeScriptで読み込むコードを無名関数で包み込むようなことはしていないのだ(スクリプト本体をトップレベル直下の無名関数として実行している点に留意すべし)。

本当は「loadCoffeeScript」のような名前のrequireやloadのような関数を追加したかったのだけど、関数の中からその1つ外の関数の内部変数を追加するなんて無理だと思うので、断念した。

動作確認済み環境

TODO

  • REPLよりも前に、もうちょっとマシなコマンドオプション解析を実装したい。現状は無茶苦茶適当すぎる。

*1:CoffeeScriptの処理系に付属するextras/coffee-script.jsを使用する。

*2:PhantomJS 1.9.0のソースを見る限り、プログラム内部にリソースとしてextras/coffee-script.jsを抱えていて、Webブラウザのコンテクストでそれを使用しているようだ。ちなみに内部に抱えているCoffeeScriptの処理系のバージョンは1.3.3のようだ。

*3:coffee-script-on-jscriptを使う。