Chapter 4. A Quick Tour^2

4.2 Building Calculator Using a Visitor

前節の算術式構文解析器は、値を計算するために少々Javaでコードを書く必要があった。ANTLR v4は私たちに文法をクリーンなままにし、言語アプリを実装するため構文解析木訪問器やその他の訪問器を使うよう促している。この節では、よく知られたvisitorパターンを小さな計算機を実装するために使う。簡単にするため、ANTLRはvisitorインタフェースと空の実装を自動的に生成する。

visitorを得るには、文法を少し変更する必要がある。まず最初に、構文候補規則にラベルが必要(ラベルは任意の識別子、規則名と衝突しないもの)。構文候補にラベルがないと、ANTLRは規則につき1つのvisitorメソッドしか生成しない。ここでは、入力句の種類毎に異なる「イベント」を得られるよう、構文候補毎に異なるvisitorメソッドとしたい。ラベルは構文候補の右端に現れる#で始まる記号。

<snip>tour/LabeledExpr.g4</snip>

次に、visitorの中で字句(トークン)名をJava定数として参照できるように、演算子リテラルに字句名を定義する。

<snip>tour/LabeledExpr.g4</snip>

文法の拡張は以上。計算機のコーディングを開始して、メインプログラムがどうなるか見てみよう。ファイルCalc.javaのメインプログラムは、前のExproJoyRide.javaのmain()とほとんど同じ。最初の違いは、字句解析器と構文解析器をExprでなく、LabeledExprから受け継いでいること。

<snip>tour/Calc.java</snip>

また木をテキストとして表示するためのprint文も取り除ける。他の違いは、visitorクラスのインスタンスEvalVisitorを生成すること。メソッドprog()の返した構文解析木を引数にしてvisit()を呼び出す。

<snip>tour/Calc.java</snip>

残っているのは、構文解析木を探索して計算値を返すvisitorを実装すること。始めるために、次のコマンドでANTLRが何を生成するか見てみる。

$ antlr4 -no-listener -visitor LabeldExpr.g4

まずANTLRは、ラベル付けされた構文候補のメソッドを持つvisitorインタフェースを生成する。

public interface LabeledExprVisitor<T> extends ParseTreeVisitor<T> {
	T visitId(LabeledExprParser.IdContext ctx);		//	from label id
	T visitAssign(LabeledExprParser.AssignContext ctx);	//	from label Assign
	T visitMulDiv(LabeledExprParser.MulDivContext ctx);	//	from label MulDiv
...
}

インタフェース定義はvisitメソッドの戻り値をパラメタ化した総称型としている。これで計算に適した実装で必要とする選択した戻り値型で実装クラスを導出できる。

次にANTLRはLabeldExprBaseと呼ばれるデフォルトのvisitor実装を生成する。この場合、式の結果は整数なので、EvalVisitorはLabeldExpreBaseVisitor<integer>を継承する。そして文と式構文候補に関連するメソッドをオーバーライドして計算機を実装する。

<snip>tour/EvalVisitor.java</snip>

コンパイルとファイルt.exprに記述された式を評価するテストの手順は以下のとおり。

<snip>seq of compile and test</snip>

ANTLR v3で必要だった文法へのJavaコード挿入なしで計算機を作成できた。文法はアプリからの独立とプログラミング言語に中立な状態を維持している。visitorメカニズムは、慣れ親しんだJavaの領域で認識関連のものを超えてすべてを保持する。生成された構文解析器で、言語アプリを作るために学ぶ追加のANTLR表記は無い。

clear文を追加することで、この式言語を拡張しようとするかもしれない。それは手始めにやるには手頃で、実際に詳細のすべてを知ることなしにやれる。clearコマンドはメモリマップをクリアする、そしてそれを認識するため規則statに新たな構文候補が必要となる。構文候補のラベルは#clearで、ANTLRを実行してvisitorインタフェースへ追加する。そしてclearで何かを実行するため、visitClear()を実装する。コンパイルと実行は前と同じ。

from The Definitive ANTLR4 Reference by Terence Parr