Perl6のgrammarを試してみる

PCにRakudo Starを導入したので、Perl6に追加された機能の中で一番気になっていたgrammarを用いたパーサをためしに書いてみました。


作ったのは四則演算のみを行うシンプルなS式インタプリタです。
命令は+-*/の4つで、各命令は1個以上の引数を取り (+ (+ 1 2) 3) のようなネストした式も認めるものとします。


ソースはこれだけ。

grammar Sexp {
    token num { \d+ }
    token op { '+' | '-' | '*' | '/' }
    rule func { '('  + ')' }
    rule exp {  |  }
 
    rule TOP {
        ^  $
    }
}
 
class SexpActions {
    method num($/) { make eval $/.Str }
    method func($/){
        given $ {
            when '+' { make [+]($>>.ast) } # $[0].ast + $[1].ast + ..
            when '-' { make [-]($>>.ast) }
            when '*' { make [*]($>>.ast) }
            when '/' { make [/]($>>.ast) }
        }
    }
 
    method exp($/) {
        make $/.values[0].ast;
    }
 
    method TOP($/) { make $.ast; }
}

my $s = "(+ 1 2 3 (- 7 (* 2 3)))";
my $m = Sexp.parse($s,actions=>SexpActions);
say $m.ast; # 7

前半のSexpが字句解析・構文解析に当たる部分で、ここで抽象構文木まで作ってくれます。繰り返し要素とかはyaccとかだとわざわざconsセルのような形で再帰的に定義しないといけなくていつも面倒だなーと思うのですが、ルールについても正規表現風に+とかで表現出来るのが素敵ですね。


後半のSexpActionが実際の計算部分です。アクション内では本来構文木を取得出来る.astプロパティの値を、makeを使って自分の好きなものに置き換える事が出来ます。今回は計算するだけなので、計算結果をそのまま割り当てています。



実際書いてみるとかなりシンプルに記述出来て驚きました。同等のものをPerl5の正規表現で書くとなると恐らく結構複雑になるでしょうし、Perl6に追加された中でもかなりパワフルで素晴らしい機能だと思います。





今回のソースコード全文と実際の動作例はこちら
http://ideone.com/2R1IA

仕様についてはここのS05を参照しました:
http://perlcabal.org/syn/