JS/CC のサンプル

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


Spam Protection by WP-SpamFree