Node.js v0.10.x系のREPLで f)( のように括弧が逆でも関数が呼べてしまう

Node.js v0.10.x系のREPLでは次のように関数呼び出しの括弧の順序が逆でもキチンと関数が呼べてしまう。

> function f() {return 1;}
undefined
> f)( // 明らかにおかしいが、Node.jsのREPLでは動作する
1

もちろん上記のコードを.jsに保存して実行させても動作しない。単にNode.jsのREPLの仕様としてそうなっているだけのようだ。

プログラミング言語処理系のREPL(Read-Eval-Print loop)というのは案外複雑に出来ている。単にスクリプトをEvalするだけではなく、色々なケースを考慮しなければいけなくて、例えばfor文やif文、関数定義は複数の行にまたがって書かれるので、即Evalをするのではなくユーザの入力を待つ必要がある。

> function f() {

この関数定義は明らかに入力途中なので、node.jsでは次のようにプロンプトを出している。

> function f() {
... // 次の入力を促すプロンプト

ではどのようにしてユーザの入力が複数行になるのかを判定すればよいのか。大抵の場合、とりあえずEvalをしてみて上手くいくかどうかで判定すればよい。
実際Node.jsのREPLもそうしている。

ただし、この場合も入力が式(Expression)か文(Statement)かを判定しなければいけない。なぜなら、式の場合はEvalの結果として値が返るが文の場合は値が返らないなど、処理が異なるからだ。

{a: 1} // オブジェクトリテラル
{var a = 1;} // ブロック構文

ではどうやってこの違いを判定するか?
入力を構文解析した結果の抽象構文木の構造を見れば良いのだけれども、いかんせんNode.jsのREPLはJavaScriptで書かれているため、Node.jsで使われているJavaScript処理系であるV8の生成する抽象構文木の構造を見るのは骨が折れる作業になってしまう。

そのため、この例でもNode.jsでは「Eval出来るかどうかで判定する」というアプローチを使っている。
具体的には以下のように括弧でくくることで、Evalが可能かどうかで判定を行っている。

({a: 1}) // オブジェクトリテラルは式なので、valid
({var a = 1;}) // 明らかにinvalid

このため、最初に示したコードが以下のように変形され、validな入力として扱われてしまっていた。

> function f() {return 1;}
undefined
> (f)() // 普通の関数呼び出し
1

その点LispのREPLはあれこれ考える必要もなくシンプルに実装出来るので素晴らしいと思う。

(loop (print (eval (read))))

ちなみにこの仕様、バグと言っても差し支えないレベルなのでnode.js v0.11.x系では無くなってしまっているようだ。
'{}'で括られたインプットのみ'()'で括ってEvalを行い、式か文かを判定している。
javascript - Why does calling a function in the Node.js REPL with )( work? - Stack Overflow