スポンサーリンク

ANTLR4を使ってHTMLパーサをC++から使う

ANTLR4を使うと自分のプロジェクトに様々な言語のパーサーを実装できる。

ANTLR4の実行形式(javaの実行形式)を実行して、「パースしたい言語」のための、「実装したい言語」用のソースコードを生成する。

今回は、HTMLをパースするためのC++のソースコードを作成する。

こうして出力したC++のパース用コードは、ANTLR4のライブラリとリンクすることでコンパイルできるようになる。

ANTLR4をダウンロード

https://www.antlr.org/download.html

URLから、Complete ANTLR 4.13.1 java binaries jarから、antlr-4.13.1-complete.jar をダウンロードする。

C++用のパーサのコードを作成

-Dlanguage=Cppを指定してC++のコードを出力することを指定。トークン定義ファイルHTMLLexer.g4と、HTMLParser.g4構文解析用のファイル。

実行はHTMLLexer.g4 → HTMLParser.g4 の順で行う。

java -jar antlr-4.13.1-complete.jar -Dlanguage=Cpp HTMLLexer.g4

以下が生成される:

・HTMLLexer.cpp
・HTMLLexer.h
・HTMLLexer.interp
・HTMLLexer.tokens

java -jar antlr-4.13.1-complete.jar -Dlanguage=Cpp HTMLParser.g4

以下が生成される:

・HTMLParser.cpp
・HTMLParser.h
・HTMLParser.interp
・HTMLParser.tokens
・HTMLParserBaseListener.cpp
・HTMLParserBaseListener.h
・HTMLParserListener.cpp
・HTMLParserListener.h

エラーが出た場合

以下のようなエラーが出る可能性がある(出た)。Java系のエラーなので新しいJavaをインストールして解決する。

Exception in thread "main" java.lang.UnsupportedClassVersionError: org/antlr/v4/Tool has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

このエラーは、「実行しようとしているツールはclass file version 55 (=Java 11)で作られているが、システムに入っているclass file version 52 (=Java 8)だ」という意味。

解決するために、今回は以下からJava 11 をダウンロードする。なおJava 11からJREがなくなったらしい。

https://www.oracle.com/jp/java/technologies/javase/jdk11-archive-downloads.html

なおダウンロードにはOracleプロファイルの作成が必要。勘弁してくれ。

antlr4をコードからCMake

生成したコードはそれだけでは動かないので、antlr4-devをCMakeする。

antlr4-devをダウンロード

以下からantlr4のソースコードをダウンロードし、antlr4-dev をVC++用にCMakeにかける。

https://github.com/antlr/antlr4

CMake

antlr4-dev/runtime/Cpp/ にCMakeLists.txtがある。

CMAKE_INSTALL_PREFIXだけ好きな場所を指定し、後はそのままConfigure→Generate→Open Project。

自身のプロジェクトへ導入

・インクルードディレクトリに以下を追加

D:\library\antlr4-install\include\antlr4-runtime

・追加のライブラリディレクトリに以下を追加

D:\library\antlr4-install\lib

・C++言語標準を「ISO C++17標準」以上にする

・リンクするライブラリ

antlr4-runtime.lib
gmock.lib
gmock_main.lib
gtest.lib
gtest_main.lib

サンプルコード

サンプルコード

#include <iostream>

#include "HTMLLexer.h"
#include "HTMLParser.h"
#include <antlr4-runtime.h>

#pragma comment(lib,"antlr4-runtime.lib")
#pragma comment(lib,"gmock.lib")
#pragma comment(lib,"gmock_main.lib")
#pragma comment(lib,"gtest.lib")
#pragma comment(lib,"gtest_main.lib")


int main(int, const char**) {

    // 入力
    std::ifstream mysrc;
    mysrc.open("D:\\test.txt");

    // ANTLR入力ストリームを作成
    antlr4::ANTLRInputStream input(mysrc);

    // Lexer、トークンストリームを作成
    HTMLLexer lexer(&input);
    antlr4::CommonTokenStream tokens(&lexer);

    // トークンストリームをパーサに渡す
    HTMLParser parser(&tokens);

    // HTML用のパーサなので最上位がhtmlDocument
    auto tree = parser.htmlDocument();


    // ツリーを表示
    std::cout << tree->toStringTree(&parser,true) << std::endl;

    return 0;
}

HTMLデータ

<html>
  <head>
    <title>test</title>
  </head>
  <body>
    <p>test</p>
  </body>
</html>

出力

(htmlDocument
        (htmlElements
            (htmlElement < html >
                (htmlContent
                    (htmlChardata \n)
                    (htmlElement < head >
                        (htmlContent
                            (htmlElement < title >
                                (htmlContent
                                    (htmlChardata test)) < / title >)) < / head >)
                    (htmlChardata \n)
                    (htmlElement < body >
                        (htmlContent
                            (htmlChardata \n)
                            (htmlElement < p >
                                (htmlContent
                                    (htmlChardata test)) < / p >)
                            (htmlChardata \n)) < / body >)
                    (htmlChardata \n)) < / html >)))

参考

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: