2013年3月14日木曜日

clang::Preprocessorで字句解析をするまで

clangを使ってC++のコードについてのメトリクスを取得したりするためのツールが作れないか,と思って手を出してみたものの,まず字句解析するだけでも一苦労だった.とりあえず色々調べてみた結果,まずはプリプロセッサを作るところかららしいので,clangのDoxygenのドキュメントを参考にプリプロセッサオブジェクトを作ってみた.

環境は,Mac Book AirのMountain Lionで.

// main.cpp
#include 
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearchOptions.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"

using namespace std;

int main(int argc, char * argv[])
{
    clang::DiagnosticIDs * diagnosticIds =
        new clang::DiagnosticIDs;
    clang::DiagnosticOptions * diagnosticOptions =
        new clang::DiagnosticOptions;
    clang::IgnoringDiagConsumer * diagnosticConsumer =
       new clang::IgnoringDiagConsumer;

    clang::DiagnosticsEngine diagnosticsEngine(
        diagnosticIds,
        diagnosticOptions,
        diagnosticConsumer
    );
    clang::LangOptions langOptions;
    clang::TargetOptions targetOptions;
    targetOptions.Triple = LLVM_DEFAULT_TARGET_TRIPLE;
    clang::TargetInfo * targetInfo = 
        clang::TargetInfo::CreateTargetInfo(
           diagnosticsEngine,
           &targetOptions
        );

    clang::FileSystemOptions fileSystemOptions;
    clang::FileManager fileManager(fileSystemOptions);
    clang::SourceManager sourceManager(
        diagnosticsEngine,
        fileManager
    );

    clang::HeaderSearchOptions * headerSearchOptions =
        new clang::HeaderSearchOptions;
    clang::HeaderSearch headerSearch(
        headerSearchOptions,
        fileManager,
        diagnosticsEngine,
        langOptions,
        targetInfo
    );

    clang::CompilerInstance compilerInstance;

    clang::PreprocessorOptions * preprocessorOptions =
        new clang::PreprocessorOptions;
    clang::Preprocessor preprocessor(
        preprocessorOptions,
        diagnosticsEngine,
        langOptions,
        targetInfo,
        sourceManager,
        headerSearch,
        compilerInstance
    );

    clang::FileEntry const * pFileEntry =
        fileManager.getFile("input.c");
    clang::FileID fileID = 
        sourceManager.createMainFileID(pFileEntry);
    preprocessor.EnterMainSourceFile();

    clang::Token token;
    do {
        preprocessor.Lex(token);

        if(diagnosticsEngine.hasErrorOccurred())
            break;

        preprocessor.DumpToken(token);
        std::cout << std::endl;
    } while(token.isNot(clang::tok::eof));

    return 0;
}


入力のinput.cはこんな感じ.
main()
{
}

Makefileはこんな感じ.結構色々リンクする必要がある.llvm関連については,llvm-configで簡単に設定できる.
# Makefile
main: main.cpp
 clang++ `llvm-config --cxxflags --ldflags --libs` $^ -o $@ -lclangBasic -lclangFrontend -lclangAST -lclangLex -lclangSerialization -lclangSema -lclangDriver -lclangEdit -lclangAnalysis -lclangParse

出力はこんな感じに.
identifier 'main'
l_paren '('
r_paren ')'
l_brace '{'
r_brace '}'
eof ''

コードの説明も書いておこうかと思ったけど力尽きた.
気が向いたらそのうち書こう.

0 件のコメント:

コメントを投稿