Java でちょっとしたパーサーを実装するときは ANTLR4 が便利
Java でちょっとしたDSLパーサーを実装するときはANTLR4 が最近もデファクトスタンダードなのかなあ。と思っています。
ANTLR4 はパーサージェネレーターです。BNF っぽい記法で書いたらいい感じに生成してくれます。手書きパーサーとかに比べると管理しやすい気がします。
gradle はコアプラグインに antlr プラグインがあるので簡単に利用できます。 https://docs.gradle.org/current/userguide/antlr_plugin.html
group 'com.example'
apply plugin: 'java'
apply plugin: 'antlr'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
antlr "org.antlr:antlr4:4.7"
testCompile 'junit:junit:4.12'
}
とかして、src/main/antlr に文法定義ファイルを置くだけ。
四則演算なら以下のような感じ。
grammar Expr;
@header {
package com.example;
}
prog: expr;
expr: term (('+'|'-') term)*;
term: factor (('*'|'/') factor)*;
factor: INT
| '(' expr ')'
;
INT : [0-9]+ ;
利用コードは以下のような感じ。
package com.example;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.io.IOException;
import java.util.List;
public class Test {
public static void main(String[] args) throws IOException {
// CharStreams.fromFile() とかもあるよ
ExprLexer lexer = new ExprLexer(CharStreams.fromString("3+2*4"));
CommonTokenStream stream = new CommonTokenStream(lexer);
ExprParser exprParser = new ExprParser(stream);
ExprParser.ExprContext expr = exprParser.expr();
System.out.println("toInfoString : " + expr.toInfoString(exprParser));
System.out.println("toString : " + expr.toString());
System.out.println("toStringTree : " + expr.toStringTree());
System.out.println("\n");
// AST はこういう形で辿れる
List<ExprParser.TermContext> term = expr.term();
System.out.println(term.get(0).factor(0).INT());
// ビジターパターンで処理することも可能
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(new ExprBaseListener(), expr);
}
}
全体のコードはこちら。 https://github.com/tokuhirom/antlr-demo