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」という箇所だ。
要するに、コメントや空白類を保持してしまうと、構文定義にそれらすべてを混ぜなければならない。それらを無視すれば構文定義がすっきりする。これが以前のやり方だったのだが、しかしそれでは必要とするコメントまで抜け落ちてしまう。構文木を解析する上位アプリでは、どんなコメントがあったのかわからないのだ。
そこで、コメントや空白は「別チャンネルに送ることにした」のだという。「保持されるが無視しうる」という状態なのだそうだ。
さて、具体的にどうやってコメントを拾うのかは、この本をもう少し読んでみないとわからない。

 

ディスカッション
コメント一覧
まだ、コメントがありません