ANTLR(2) - 四則演算

お決まりの四則演算。

one-pathでAST構築なし。合成属性関係が少しわかった。大学の授業レベルならこの辺の知識でよさそう。

文法

grammar Expr;

@header {
package introduction;
}

@lexer::header {
package introduction;
}

// entry
start returns [double value]
  : expr ';' EOF { $value = $expr.value; }
  ;

// +, -
expr returns [double value]
  : left=term { $value = $left.value; }
    ( '+' right=term { $value += $right.value; }
    | '-' right=term { $value -= $right.value; }
    )*
  ;

// *, /
term returns [double value]
  : left=unary { $value = $left.value; }
    ( '*' right=unary { $value *= $right.value; }
    | '/' right=unary { $value /= $right.value; }
    )*
  ;

// prefix operations
unary returns [double value]
  : factor { $value = $factor.value; }
  | '+' factor { $value = + $factor.value; }
  | '-' factor { $value = - $factor.value; }
  ;
  
// factor
factor returns [double value]
  : NUMBER { $value = Double.parseDouble($NUMBER.text); }
  | '(' expr ')' { $value = $expr.value; }
  ;

NUMBER  : ('0'..'9')+ ( '.' ('0'..'9')+ )?
  ;

SKIP  : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; }
  ;
  • term ( '+' term )* が書けた。LL(*)らしくEBNFはOKみたい。
  • returns [ ] : ...
    • 合成属性の定義。"expr returns [double value]" としておくと、exprルールを使った際に$expr.valueでexprルールの戻り値を取得できる。
    • 戻り値の設定は、ルールの中で { $value = 100; } のように $ = ...; とする感じ。
    • $NUMBER.text のように、トークンは必ず returns [String text] な属性が定義されているんだろう。
  • =
    • ルールに名前を付けるらしい。「expr : factor '+' factor」だと、{ $factor.. } とした時にどちらか判別できないため、「expr : f1=factor '+' f2=factor」と名前を付ける。ただ可読性は下がるのでどうしたもんか。

確認コード

public class ExprTest {
  public static void main(String[] args) throws IOException, RecognitionException {
    ANTLRInputStream in = new ANTLRInputStream(System.in, "UTF-8");
    Lexer lexer = new ExprLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    ExprParser parser = new ExprParser(tokens);
    System.out.println("result = " + parser.start());
  }
}
  • start returns [double value] は外まで流れ出てるみたい。ExprParser#start()の戻り値がdouble型になってた。

実行結果

> echo 1 + 2 * 3;
| java -cp bin;lib\antlr-runtime-3.0.1.jar introduction.ExprTest
result = 7.0

> echo (1 + 2) * 3;
| java -cp bin;lib\antlr-runtime-3.0.1.jar introduction.ExprTest
result = 9.0