Java/GradleでANTLRを使い、DDLからテーブル定義書を作成、その2
前回の最後で「Positive Technologies MySQL grammar」というものからJavaパーサーを作ってみた。エラーは出ていないのでSQLを解析してみよう。
動かしてみる
import java.util.*;
import org.antlr.v4.runtime.*;
import mysqlparser.*;
import mysqlparser.MySqlParser.*;
public class TestMain {
static String SQL =
"SELECT COUNT(*) FROM TBLBOOK; INSERT INTO TBLBOOK (A, B) VALUES(1, 2)";
public static void main(String[] args) {
// 字句解析を行う
MySqlLexer lexer = new MySqlLexer(CharStreams.fromString(SQL));
// 字句解析の結果を構文解析する
CommonTokenStream stream = new CommonTokenStream(lexer);
MySqlParser parser = new MySqlParser(stream);
// 構文木を調べてみる
List<SqlStatementContext>sqls = parser.root().sqlStatements().sqlStatement();
sqls.stream().forEach(sql->{
System.out.println("" + sql.getText());
});
}
}
出力は以下のようになる。一つのSQL文全体を出力すると、スペースが除去される形になるらしい。
SELECTCOUNT(*)FROMTBLBOOK
INSERTINTOTBLBOOK(A,B)VALUES(1,2)
小文字は扱えない
実はこのパーサは小文字が扱えない。
static String SQL =
"select count(*) from tblbook; insert into tblbook (a, b) values(1, 2)";
などとすると、以下のようなエラーになる。
line 1:13 no viable alternative at input '(*'
この件は以下に記述がある。
これは非常に困る。本来は入力を一律に大文字に変換できないのだが(文字列リテラル中の小文字も大文字になってしまう)、しかし今回の目的にはこれでも構わない。
すべてを大文字にして入力することにする。
static String SQL =
"select count(*) from tblbook; insert into tblbook (a, b) values(1, 2)";
public static void main(String[] args) {
// 字句解析を行う、その前にすべてを大文字に変換
MySqlLexer lexer = new MySqlLexer(CharStreams.fromString(SQL.toUpperCase()));
コメントが無視されてしまう
これは当然だろうが、字句解析によってコメントは無視され、コメントを除去した上でのトークンがパーサー側に受け渡されているようだ。これは、MySqlLexer.g4の中の最初に示されている。
lexer grammar MySqlLexer;
channels { MYSQLCOMMENT, ERRORCHANNEL }
// SKIP
SPACE: [ \t\r\n]+ -> channel(HIDDEN);
SPEC_MYSQL_COMMENT: '/*!' .+? '*/' -> channel(MYSQLCOMMENT);
COMMENT_INPUT: '/*' .*? '*/' -> channel(HIDDEN);
LINE_COMMENT: (
('-- ' | '#') ~[\r\n]* ('\r'? '\n' | EOF)
| '--' ('\r'? '\n' | EOF)
) -> channel(HIDDEN);
空白類はHIDDENというチャンネル、コメントはMYSQLCOMMENTというチャンネルに送られるらしい。これを取得する方法は無いものだろうか?これは、以下の本に説明があった。
「4.5 Cool Lexical Features」の「Rewriting the Input Stream」、あるいは「12.1 Broadcasting Tokens on Different Channels」という箇所だ。
要するに、コメントや空白類を保持してしまうと、構文定義にそれらすべてを混ぜなければならない。それらを無視すれば構文定義がすっきりする。これが以前のやり方だったのだが、しかしそれでは必要とするコメントまで抜け落ちてしまう。構文木を解析する上位アプリでは、どんなコメントがあったのかわからないのだ。
そこで、コメントや空白は「別チャンネルに送ることにした」のだという。「保持されるが無視しうる」という状態なのだそうだ。
さて、具体的にどうやってコメントを拾うのかは、この本をもう少し読んでみないとわからない。
ディスカッション
コメント一覧
まだ、コメントがありません