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 で書いてみようと思います。


コメントなし
この投稿記事に対するコメントのフィード
トラック・バックのリンク: http://ken-wakita.net/research/ja/2010/02/jscc-sample-grammar/trackback/