A Lua binding to the libclang library, which allows you to parse C and C++ code using the Clang compiler.
luaclang-parser provides an object-oriented interface over the libclang API. As of right now, a meaningful subset of libclang API is available, allowing you to write C/C++ header parsers, file diagnostics (warnings, errors) and code completion.
No more error-prone hand-written header/documentation parsers, yay! :)
Requirements
Lua 5.1
LLVM/Clang - read the getting started guide to find out how to obtain Clang from source. libclang is built and installed along with Clang compiler.
Building
luaclang-parser uses CMake to find your Lua installation and libclang. The preferred way to build the module is this:
luaclang-parser$ mkdir build; cd build
luaclang-parser/build$ cmake ..
luaclang-parser/build$ make
The example uses this directory layout to find the luaclang-parser without the need to install it system-wide.
Overview
libclang provides a cursor-based API to the abstract syntax tree (AST) of the C/C++ source files. This means that you can see what the compiler sees. These are the main classes used in libclang/luaclang-parser:
Index - represents a set of translation units that could be linked together
TranslationUnit - represents a source file
Cursor - represents an element in the AST in a translation unit
Type - the type of an element (variable, field, parameter, return type)
Examples
A simple code browser demonstrating the abilities of luaclang-parser is provided in cindex.lua. It takes command line arguments as the Clang compiler would and passes them to TranslationUnit:parse(args) (see below). Then it processes the headers and gathers information about classes, methods, functions and their argument, and saves this information into a SQLite3 database code.db with the following schema:
References between arguments, functions/methods and classes are done through the SQLite row identifier. For example to construct the database for libclang, run the following command:
lua cindex.lua /usr/local/include/clang-c/Index.h
You can then query the functions using SQL, for example to gather all functions which take CXCursor as the first argument:
SELECT *
FROM functions F
JOIN args A ON A.parent = F.rowid
WHERE A.idx = 1 AND A.type = 'CXCursor'
A sample file allqt.cpp which takes in most Qt headers is available. Using the qt4-qobjectdefs-injected.h include file, annotations for signals and slots are injected into QObject, which is recognized by cindex.lua and it is able to mark methods as either signals or slots. For example to find all classes and their signals, run:
SELECT C.name, M.signature
FROM classes C
JOIN methods M ON M.class = C.rowid
WHERE M.signal = 1
Just for a taste on how big the Qt framework is:
sqlite> SELECT COUNT(*) FROM classes;
1302
sqlite> SELECT COUNT(*) FROM methods;
19612
Reference
luaclang-parser
Use local parser = require "luaclang-parser" to load the module. It exports one function:
createIndex(excludePch : boolean, showDiagnostics : boolean) -> Index
Binding for clang_createIndex. Will create an Index into which you can parse or load pre-compiled TranslationUnits.
Binding for clang_parseTranslationUnit. This will parse a given source file sourceFile with the command line arguments args, which would be given to the compiler for compilation, i.e. include paths, defines. If only the args table is given, the source file is expected to be included in args.
Index:load(astFile : string) -> TranslationUnit
Binding for clang_createTranslationUnit. This will load the translation unit from an AST file which was constructed using clang -emit-ast. Useful when repeatedly processing large sets of files (like frameworks).
TranslationUnit
TranslationUnit:cursor() -> Cursor
Binding for clang_getTranslationUnitCursor. Returns the Cursor representing a given translation unit, which means you can access to classes and functions defined in a given file.
TranslationUnit:file(fileName : string) -> string, number
Binding for clang_getFile. Returns the absolute file path and a time_t last modification time of fileName.
TranslationUnit:diagnostics() -> { Diagnostic* }
Binding for clang_getDiagnostic. Returns a table array of Diagnostic, which represent warnings and errors. Each diagnostic is a table consisting of these keys: text - the diagnostic message, category - a diagnostic category.
Binding for code completion API. Returns the available code completion options at a given location using prior content. Each Completion is a table consisting of several chunks, each of which has a text and a chunk kind without the CXCompletionChunk_ prefix. If there are any annotations, the annotations key is a table of strings:
completion = {
priority = number, priority of given completion
chunks = {
kind = string, chunk kind
text = string, chunk text
},
[annotations = { string* }]
}
Cursor
You can compare whether two Cursors represent the same element using the standard == Lua operator.
Cursor:children() -> { Cursor * }
Binding over clang_visitChildren. This is the main function for AST traversal. Traverses the direct descendats of a given cursor and collects them in a table. If no child cursors are found, returns an empty table.
Cursor:parent() -> Cursor
Binding for clang_getCursorSemanticParent. Returns a cursor to the semantic parent of a given element. For example, for a method cursor, returns its class. For a global declaration, returns the translation unit cursor.
Cursor:name() -> string
Binding over clang_getCursorSpelling. Returns the name of the entity referenced by cursor. __tostring for Cursor also points to this function.
Cursor:displayName() -> string
Binding over clang_getCursorDisplayName. Returns the display name of the entity, which for example is a function signature.
Cursor:kind() -> string
Returns the cursor kind without the CXCursor_ prefix, i.e. "FunctionDecl".
Cursor:arguments() -> { Cursor* }
Binding of clang_Cursor_getArgument. Returns a table array of Cursors representing arguments of a function or a method. Returns an empty table if a cursor is not a method or function.
Cursor:resultType() -> Type
Binding for clang_getCursorResultType. For a function or a method cursor, returns the return type of the function.
Cursor:type() -> Type
Returns the Type of a given element or nil if not available.
Cursor:access() -> string
When cursor kind is "AccessSpecifier", returns one of "private", "protected" and "public".
Cursor:location() -> string, number, number, number, number
Binding for clang_getCursorExtent. Returns the file name, starting line, starting column, ending line and ending column of the given cursor. This can be used to look up the text a cursor consists of.
Cursor:referenced() -> Cursor
Binding for clang_getCursorReferenced. For a reference type, returns a cursor to the element it references, otherwise returns nil.
Cursor:definition() -> Cursor
Binding for clang_getCursorDefinition. For a reference or declaration, returns a cursor to the definition of the entity, otherwise returns nil.
Cursor:isVirtual() -> boolean
For a C++ method, returns whether the method is virtual.
Cursor:isStatic() -> boolean
For a C++ method, returns whether the method is static.
Type
You can compare whether two Types represent the same type using the standard == Lua operator.
Type:name() -> string
Binding of clang_getTypeKindSpelling. Returns one of CXTypeKind as a string without the CXType_ prefix. Type also has __tostring set to this method.
Binding of clang_getTypeDeclaration. Returns a Cursor to the declaration of a given type, or nil.
License
Copyright (c) 2012 Michal Kottman
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
请发表评论