我們在前一篇 Clang AST dump 中交代了指令可以利用 clang 完整 dump AST。其實已經可以自己寫程式 traverse AST 了。但又何必再 parse 一次呢? 可以直接利用 libclang 使用建立好的 AST。
走訪 AST 主要有兩種方式,可以利用 AST Visitor 或 AST Matcher,兩種是完全不同的概念,使用上也有互補的特性。本篇主要介紹 visitor ,借用這邊的 Visitor作法。
#include <clang-c/Index.h>
#include <iostream>
#include <string>
std::string getCursorKindName( CXCursorKind cursorKind )
{
CXString kindName = clang_getCursorKindSpelling( cursorKind );
std::string result = clang_getCString( kindName );
clang_disposeString( kindName );
return result;
}
std::string getCursorSpelling( CXCursor cursor )
{
CXString cursorSpelling = clang_getCursorSpelling( cursor );
std::string result = clang_getCString( cursorSpelling );
clang_disposeString( cursorSpelling );
return result;
}
CXChildVisitResult visitor( CXCursor cursor, CXCursor /* parent */, CXClientData clientData )
{
CXSourceLocation location = clang_getCursorLocation( cursor );
if( clang_Location_isFromMainFile( location ) == 0 )
return CXChildVisit_Continue;
CXCursorKind cursorKind = clang_getCursorKind( cursor );
unsigned int curLevel = *( reinterpret_cast<unsigned int*>( clientData ) );
unsigned int nextLevel = curLevel + 1;
std::cout << std::string( curLevel, '-' ) << " " << getCursorKindName(
cursorKind ) << " (" << getCursorSpelling( cursor ) << ")\n";
clang_visitChildren( cursor,
visitor,
&nextLevel );
return CXChildVisit_Continue;
}
int main( int argc, char** argv )
{
if( argc < 2 )
return -1;
CXIndex index = clang_createIndex( 0, 1 );
CXTranslationUnit tu = clang_createTranslationUnit( index, argv[1] );
if( !tu )
return -1;
CXCursor rootCursor = clang_getTranslationUnitCursor( tu );
unsigned int treeLevel = 0;
clang_visitChildren( rootCursor, visitor, &treeLevel );
clang_disposeTranslationUnit( tu );
clang_disposeIndex( index );
return 0;
}
稍微改寫成一個自動產生 header 的版本
#include <clang-c/Index.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<int> arrSizes;
vector<string> varNames;
vector<string> varTypes;
string getCursorKindName(CXCursorKind cursorKind) {
CXString kindName = clang_getCursorKindSpelling(cursorKind);
string result = clang_getCString(kindName);
clang_disposeString(kindName);
return result;
}
string getCursorSpelling(CXCursor cursor) {
CXString cursorSpelling = clang_getCursorSpelling(cursor);
string result = clang_getCString(cursorSpelling);
clang_disposeString(cursorSpelling);
return result;
}
string getCursorType(CXCursor cursor) {
CXType t = clang_getCursorType(cursor);
CXString s = clang_getTypeSpelling(t);
std::string result = clang_getCString(s);
clang_disposeString(s);
return result;
}
CXChildVisitResult visitor(CXCursor cursor, CXCursor parent,
CXClientData clientData) {
// check end of file
CXSourceLocation location = clang_getCursorLocation(cursor);
if (clang_Location_isFromMainFile(location) == 0) return CXChildVisit_Continue;
CXCursorKind cursorKind = clang_getCursorKind(cursor);
CXType cursorType = clang_getCursorType( cursor );
CXTypeKind typeKind = cursorType.kind;
// skip while not variable declaration
if (!clang_isDeclaration(cursorKind)) return CXChildVisit_Continue;
if (cursorKind != CXCursor_VarDecl) return CXChildVisit_Continue;
// variable name
string varName = getCursorSpelling(cursor);
varNames.push_back( varName );
if (typeKind == CXType_ConstantArray) { // is array type
// get array size
int arrSize = clang_getArraySize( cursorType );
// get type name
CXType arrType = clang_getArrayElementType( cursorType );
CXString s = clang_getTypeSpelling( arrType );
string varType = clang_getCString(s);
clang_disposeString(s);
arrSizes.push_back(arrSize);
varTypes.push_back(varType);
} else {
string varType = getCursorType(cursor);
arrSizes.push_back(0);
varTypes.push_back(varType);
}
return CXChildVisit_Continue;
}
void printHeader(FILE *file) {
fprintf(file, "// Automatic generated.\n");
int num = varNames.size();
for(int i = 0; i < num; ++i) {
const int arrSize = arrSizes[i];
const string &varType = varTypes[i];
const string &varName = varNames[i];
if ( arrSize != 0 ) {
fprintf(file, "extern %-40s %s[%d];\n", varType.c_str(), varName.c_str(), arrSize);
} else {
fprintf(file, "extern %-40s %s;\n", varType.c_str(), varName.c_str());
}
}
}
void printMacroLine(FILE *file, const char *funName) {
int num = varNames.size();
for(int i = 0; i < num; ++i) {
const string &varName = varNames[i];
if ( i == num - 1) {
fprintf(file, "%s( %s );\n", funName, varName.c_str());
} else {
fprintf(file, "%s( %s ); \\\n", funName, varName.c_str());
}
}
}
void printMacro(FILE *file) {
fprintf(file, "// Automatic generated.\n");
fprintf(file, "#define CFG_READ_MACRO \\\n");
printMacroLine(file, "CFG_READ");
fprintf(file, "#define CFG_WRITE_MACRO \\\n");
printMacroLine(file, "CFG_WRITE");
fprintf(file, "#define CFG_PRINT_MACRO \\\n");
printMacroLine(file, "CFG_PRINT");
}
int main(int argc, char** argv) {
if (argc < 2) return -1;
CXIndex index = clang_createIndex(0, 1);
CXTranslationUnit tu = clang_createTranslationUnit(index, argv[1]);
if (!tu) return -1;
CXCursor rootCursor = clang_getTranslationUnitCursor(tu);
clang_visitChildren(rootCursor, visitor, 0);
clang_disposeTranslationUnit(tu);
clang_disposeIndex(index);
// print result
FILE *fileHeader = fopen("header.inc", "w");
FILE *fileMacro = fopen("macro.inc", "w");
printHeader(fileHeader);
printMacro(fileMacro);
fclose(fileHeader);
fclose(fileMacro);
return 0;
}
:: input file set astInput=test.cpp :: generate temp ast file for traversing clang -emit-ast %astInput% -o tmp.ast :: save header.inc & macro.inc ..\Release\clangparser tmp.ast :: print result type header.inc type macro.inc pause
D:\workspace\clangparser\src>set astInput=test.cpp D:\workspace\clangparser\src>clang -emit-ast test.cpp -o tmp.ast D:\workspace\clangparser\src>..\Release\clangparser tmp.ast D:\workspace\clangparser\src>type header.inc // Automatic generated. extern int REG_A; extern float REG_B; extern int REG_C[10]; extern bool REG_D; extern clock_t t0; extern string str; D:\workspace\clangparser\src>type macro.inc // Automatic generated. #define CFG_READ_MACRO \ CFG_READ( REG_A ) \ CFG_READ( REG_B ) \ CFG_READ( REG_C ) \ CFG_READ( REG_D ) \ CFG_READ( t0 ) \ CFG_READ( str ) #define CFG_WRITE_MACRO \ CFG_WRITE( REG_A ) \ CFG_WRITE( REG_B ) \ CFG_WRITE( REG_C ) \ CFG_WRITE( REG_D ) \ CFG_WRITE( t0 ) \ CFG_WRITE( str ) #define CFG_PRINT_MACRO \ CFG_PRINT( REG_A ) \ CFG_PRINT( REG_B ) \ CFG_PRINT( REG_C ) \ CFG_PRINT( REG_D ) \ CFG_PRINT( t0 ) \ CFG_PRINT( str )
