JS/CC のサンプルファイルが少しみにくかったので、ちょっと整形してみました。まずは文法定義ファイル(calc.grammar)です。LALR パーザを使った方ならば、だいたい理解できると思います。サンプルの文法定義ファイルにやたらに使われていたTABを除去し、構文解析木のドライバルーチンを除去しました。
! ‘ |t’;
‘(‘
‘)’
‘[0-9]+’ INT [* %match = parseInt(%match); *];
< '+' '-';
< '*' '/';
##
p: e [* print( %1 ); *];
e:
| e '+' e [* %% = %1 + %3; *]
| e '-' e [* %% = %1 – %3; *]
| e '*' e [* %% = %1 * %3; *]
| e '/' e [* %% = %1 / %3; *]
| '-' e &'*' [* %% = %2 * -1; *]
| '(' e ')' [* %% = %2; *]
| INT;
[*
var calc_parser = {
parse: __##PREFIX##parse,
set_with_trace: function () { ##PREFIX##_dbg_withtrace = true; },
set_with_parse_tree: function () { ##PREFIX##_dbg_withparsetree = true; },
set_with_step_by_step: function () { ##PREFIX##_dbg_withstepbystep = true; }
};
*]
構文解析器は文法定義のあとに宣言された calc_parser オブジェクトの parse メソッドとしています。''##PREFIX'' は JS/CC が適宜、置き換えるようです。ドライバルーチンを除去したのは、JS/CC による書き換えに依存しない部分は通常のJavaScriptのコードとして独立させたかったからです。
これを以下のようにjsccで処理して、構文解析器(calc.js)を作成します。
jscc -o calc.js calc.grammar
以下がドライバルーチン (main.js) です。
(function (input) {
var error_offsets = new Array();
var error_lookaheads = new Array();
calc_parser.parse(input, error_offsets, error_lookaheads);
error_offsets.forEach(function (i) {
print("Parse error near "" +
str.substr(error_offsets[i]) +
"", expecting "" +
error_lookaheads[i].join() +
""");
});
})(arguments[0]);
まだ納得がいかないのは、字句定義の部分のセミコロンの使い方です。セミコロンの有無に意味があるのかなぁと思いつつ、今夜はこれまでにしようと思います。
元々のドライバルーチンより若干、すっきりとさせましたが、特に説明を要する点はないでしょう。これを以下のように実行することができます。
$ tracemonkey -f calc.js main.js '5*(2-7)/4'
-6.25
$ squirrelfish calc.js main.js — '5*(2-7)/4'
-6.25
明日は先日、Objective Caml で書いた IMP を JS/CC + JavaScript で書いてみようと思います。