2008/06/18からのアクセス回数 39660
ANTLRは、yacc, lexと同じコンパイラー・コンパイラーです。
ANTRLを使うことで、
を容易に作成することができます。
一時期よりも話題にならなかったコンパイラー技術もGWTがjavaからjavascriptへの変換を 使ったことにより、その価値が見直されているのではないかと思います。
特にANTRLは、
を生成するため、数テーブル、関数テーブル、構文チェック、コンパイラー、インタプリタが 再利用できる点が優れています。
ANTWorksは、ANTLRの文法を作成、チェックするためのワークベンチです。
ができます。
ANTLRWorksは、以下のURLからマシン環境に合ったファイルをダウンローしてください。
http://www.antlr.org/works/index.html
私は、MacOSXを使用しているので、ANTRLWOrks 1.1.7のBundole for Mac OS Xをダウンロードしました。
ANTLRWorksの使い方は、http://www.antlr.org/works/help/tutorial/calculator.html を参照してください。
画面の表示例を以下に示します。
Cコンパイラ設計(yacc・lexの応用) で紹介した yaccではLALR(1)を使って構文解析を行うのに対し、ANTLRはLL(*)を使って構文解析を行います。
ANTRLの特徴は、
が挙げられます。
yacc, lexと比較すると
の違いがあります。
以下では、Cコンパイラ設計(yacc・lexの応用)/第1章 言語の定義の例題の文法
expression : expression '+' product | expression '-' product | product product : product '*' factor | product '/' factor | factor factor : IDENTIFIER | CONSTANT | '(' expressoin ')'
をどのようにANTLRで記述するかを紹介します。
上記のルールをANTLRWorksで入力すると
のように波下線のある expression, productで Rule "expression" is left-recursive のエラーメッセージ が表示されます。
左再帰(left-recursive)を避けるために、文法を以下のように変更します。
expression : product ('+' product)* | product ('-' product)* ; product : factor ('*' factor)* | factor ('/' factor)* ;
ここで*は、カッコで括った部分が0回以上繰り返されることを意味します。
これでエラーは無くなりましたが、expression, productで8個の警告が出力されます。 そこで、
expression : product ('+' product | '-' product)* ; product : factor ('*' factor | '/' factor)* ;
に変更します。
文法が正しいかどうかexpressionのSyntax Diagramを見てみましょう。
これは、productが1個あり、その後に+または-に続きproductが繰り返されることを表現しています。
expressionのシンタックス・グラフ
とは形は異なりますが、同じ処理をすることが読み取れます。
演算の優先順位は、明示的に文法に記述する必要があります。
上記の例では、
のように下に行くほど優先順位が高くなります。これをそれぞれ
と明示的に定義します。
では、Interpreterで優先順位が正しく処理されているか見てみましょう。
1との和(expression)を行う前に、2と3の積の処理(product)が行われるのが分かります。
累乗(power)のように右結合演算子(right-associative)の処理は、次のように記述します。
expression : product ('+' product | '-' product)* ; product : power ('*' power | '/' power)* ; power : factor ('^' power)? ; factor : IDENTIFIER | CONSTANT | '(' expression ')' ;
ここで、powerは、右再帰となっています。
1+2^3^4*3
を入力としたときの構文木は、
のようになり、
1 + ((2^(3^4)) * 5)
の計算順序を正しく処理しているのが分かります。
ANTLRでも、yaccのように文法の中にアクションを記述することができます。 先の、例題にアクションを追加すると次のようになります。フォーマットはSampleCに合わせて 文法はタブ1個の後、アクションはタブ2個の後に記述することにします。
grammar E1; prog : e=expression NEWLINE { System.out.println($e.value);} | NEWLINE ; expression returns [int value] : e=product {$value = $e.value;} ( '+' e=product {$value += $e.value;} | '-' e=product {$value -= $e.value;} )* ; product returns [int value] : e=power {$value = $e.value;} ( '*' e=power {$value *= $e.value;} | '/' e=power {$value /= $e.value;} )* ; power returns [int value] : b=factor { $value = $b.value;} ( '^' e=power { double v = Math.pow($b.value, $e.value); $value = (int)v; } )? ; factor returns [int value] : IDENTIFIER {$value = 0;} | CONSTANT {$value = Integer.parseInt($CONSTANT.text);} | '(' expression ')' {$value = $expression.value;} ; IDENTIFIER : ('a'..'z'|'A'..'Z')+ ; CONSTANT : '0'..'9'+ ; NEWLINE : '\r'? '\n' ; WS : (' '|'\t')+ {skip();} ;
改行で計算をするようにprogを追加しました。
ノンターミナル(expression, product等)の後に
returns [int value]
を追加することで、戻り値の型と値を保持する変数を定義します。
e=product
のようにノンターミナルの前にラベル=を追加することで、ノンターミナルへの参照 をラベルで代用することができます。
例えば、power項では、
power returns [int value] : b=factor { $value = $b.value;} ( '^' e=power { double v = Math.pow($b.value, $e.value); $value = (int)v; } )? ;
とありますが、ここでb=factor, e=powerを使って
double v = Math.pow($b.value, $e.value);
累乗の計算をしています。
ANTLRWorksでは、テストプログラムを作成しなくても、文法を実行し、デバッグすることができます。
この記事は、
皆様のご意見、ご希望をお待ちしております。