[解決済み】ANTLR:簡単な例はありますか?
質問
ANTLRを使い始めたいのですが、「ANTLRの使い方」のページで数時間かけてサンプルを確認したところ、「ANTLRの使い方」のページが一番わかりやすかったです。 antlr.org のサイトでは、文法からJavaの処理についてまだ明確に理解できていません。
ANTLRで実装した四則演算の電卓のように、パーサーの定義からJavaのソースコードに至るまで、何か簡単な例はありますか?
どのように解決するのですか?
注意事項 : この回答は ANTLR3 ! もし、あなたが ANTLR4 の例では、この Q&A を使用して、簡単な式パーサーと評価器を作成する方法を示しています。 ANTLR4 .
まず文法を作成します。以下は、4つの基本的な数学演算子を使って作られた式を評価するために使用できる小さな文法です。+また、括弧を使用して式をグループ化することもできます。
単項演算子(-1+9のマイナス)や.99のような小数(先頭の数字なし)は扱えません。これはあくまでも例であり、あなた自身で取り組むことができます。
以下は、文法ファイルの内容である。 Exp.g :
grammar Exp;
/* This will be the entry point of our parser. */
eval
: additionExp
;
/* Addition and subtraction have the lowest precedence. */
additionExp
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
/* Multiplication and division have a higher precedence. */
multiplyExp
: atomExp
( '*' atomExp
| '/' atomExp
)*
;
/* An expression atom is the smallest part of an expression: a number. Or
when we encounter parenthesis, we're making a recursive call back to the
rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp
: Number
| '(' additionExp ')'
;
/* A number: can be an integer value, or a decimal value */
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
/* We're going to ignore all white space characters */
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
(パーサーのルールは小文字で始まり、レキサーのルールは大文字で始まる)
文法を作成したら、そこからパーサーとレキサーを生成したいと思います。ダウンロードはこちら ANTLR jar を作成し、文法ファイルと同じディレクトリに保存してください。
シェル/コマンドプロンプトで以下のコマンドを実行します。
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
エラーメッセージは表示されず、ファイル ExpLexer.java , ExpParser.java と Exp.tokens が生成されるようになりました。
すべてが正しく動作するかどうかを確認するために、このテストクラスを作成します。
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
parser.eval();
}
}
を作成し、コンパイルしてください。
// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java
// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java
と入力し、実行します。
// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo
// Windows
java -cp .;antlr-3.2.jar ANTLRDemo
すべてがうまくいくと、コンソールに何も表示されなくなります。これは、パーサーがエラーを見つけられなかったことを意味します。このとき
"12*(5-6)"
を
"12*(5-6"
を再コンパイルして実行すると、以下のように表示されるはずです。
line 0:-1 mismatched input '<EOF>' expecting ')'
さて、今度は文法にちょっとした Java コードを追加して、パーサーが実際に何か役に立つことをできるようにしたいと思います。コードを追加するには
{
と
}
を文法の中に入れて、その中にプレーンなJavaのコードを入れてください。
しかし、その前に、文法ファイル内のすべてのパーサー・ルールはプリミティブなダブル値を返す必要があります。そのためには
returns [double value]
を各ルールの後に追加してください。
grammar Exp;
eval returns [double value]
: additionExp
;
additionExp returns [double value]
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
// ...
すべてのルールは、double 値を返すことが期待されています。さて、この返り値との対話です。
double value
(これはプレーンなJavaコードブロックの中にあるのではない
{...}
の前にドル記号を追加する必要があります。
value
:
grammar Exp;
/* This will be the entry point of our parser. */
eval returns [double value]
: additionExp { /* plain code block! */ System.out.println("value equals: "+$value); }
;
// ...
この文法に、Javaのコードを追加したものがこちらです。
grammar Exp;
eval returns [double value]
: exp=additionExp {$value = $exp.value;}
;
additionExp returns [double value]
: m1=multiplyExp {$value = $m1.value;}
( '+' m2=multiplyExp {$value += $m2.value;}
| '-' m2=multiplyExp {$value -= $m2.value;}
)*
;
multiplyExp returns [double value]
: a1=atomExp {$value = $a1.value;}
( '*' a2=atomExp {$value *= $a2.value;}
| '/' a2=atomExp {$value /= $a2.value;}
)*
;
atomExp returns [double value]
: n=Number {$value = Double.parseDouble($n.text);}
| '(' exp=additionExp ')' {$value = $exp.value;}
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
で、私たちの
eval
ルールが double を返すようになったので、 ANTLRDemo.java を以下のように変更します。
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
System.out.println(parser.eval()); // print the value
}
}
文法から新しいレキサとパーサを再び(再)生成し(1)、すべてのクラスをコンパイルし(2)、ANTLRDemoを実行します(3)。
// *nix/MacOS
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .:antlr-3.2.jar ANTLRDemo.java // 2
java -cp .:antlr-3.2.jar ANTLRDemo // 3
// Windows
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .;antlr-3.2.jar ANTLRDemo.java // 2
java -cp .;antlr-3.2.jar ANTLRDemo // 3
という式の結果が表示されます。
12*(5-6)
がコンソールに出力されます!
繰り返しになりますが、これは非常に簡単な説明です。ぜひ ANTLR ウィキ を読んで、チュートリアルを読んだり、今投稿した内容で少し遊んだりしてみてください。
頑張ってください。
EDIT
この投稿
は、上記の例を拡張して
Map<String, Double>
は、提供された式の変数を保持するために提供することができます。
このコードを現在のAntlrのバージョン(2014年6月)で動作させるためには、いくつかの変更を加える必要がありました。
ANTLRStringStream
になる必要がありました。
ANTLRInputStream
に変更する必要があり、返される値は
parser.eval()
から
parser.eval().value
を削除する必要がありました。
WS
のような属性値があるため、最後に
$channel
は、レキサーのアクションに登場することは許されなくなりました。
関連
-
[解決済み] JVMフラグCMSClassUnloadingEnabledは、実際に何をするのですか?
-
[解決済み] 一部の入力ファイルが非推奨のAPIを使用またはオーバーライドしている
-
[解決済み] javascriptでExpression言語を使うには?
-
[解決済み] ORA-01654: インデックスを拡張できません。
-
[解決済み] 午前0時からの時間を秒単位で取得する方法
-
[解決済み] HTTP ステータス 500 - サーブレットクラス pkg.coreServlet のインスタンス化に失敗しました。
-
[解決済み] どのように配列の10未満の値(x * 2)を倍増するコードを取得するには?(Java)
-
[解決済み] Mavenです。JARは空になります - 含有するためにマークされたコンテンツがありません
-
[解決済み] java.io.IOException。DER長の短い読み取り
-
[解決済み] ヘッドリカーシオンとテールリカーシオンの違い [重複]について
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] java.sql.SQLException: ORA-00933: SQL コマンドが正しく終了していません。
-
[解決済み] 警告: コンテキスト初期化中に例外が発生 - 更新の試みはキャンセルされました。
-
[解決済み] Androidのコールバックとは何ですか?重複
-
[解決済み] android.support.v4.app.FragmentActivity' で 'TAG' がプライベートアクセスされている。
-
[解決済み] java.util.MissingFormatArgumentException: 形式指定子 '%s' がありません。
-
[解決済み] JAVA_OPTIONS、JAVA_TOOL_OPTIONS、JAVA_OPTSの違いについて
-
[解決済み] Java- <T extends Comparable<T>>の意味?
-
[解決済み] タイプの安全性。アンチェック・キャスト
-
[解決済み] java swingアプリケーションでJCEがプロバイダBCを認証できない
-
[解決済み] java.io.IOException。DER長の短い読み取り