├── CMakeLists.txt ├── README.md ├── examples ├── allqt.cpp ├── cindex.lua └── qt4-qobjectdefs-injected.h └── luaclang-parser.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(luaclang CXX) 3 | 4 | find_package(Lua51 REQUIRED) 5 | find_library(CLANG_LIBRARY NAMES clang libclang) 6 | 7 | if(APPLE) 8 | set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS 9 | "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS} -flat_namespace -undefined dynamic_lookup") 10 | endif(APPLE) 11 | 12 | include_directories(${LUA_INCLUDE_DIR}) 13 | add_library(luaclang-parser MODULE luaclang-parser.cpp) 14 | target_link_libraries(luaclang-parser ${CLANG_LIBRARY}) 15 | set_target_properties(luaclang-parser PROPERTIES PREFIX "") -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | luaclang-parser 2 | =============== 3 | 4 | A Lua binding to the [`libclang`](http://clang.llvm.org/doxygen/group__CINDEX.html) library, which allows you to parse C and C++ code using the Clang compiler. 5 | 6 | `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. 7 | 8 | No more error-prone hand-written header/documentation parsers, yay! :) 9 | 10 | Requirements 11 | ============ 12 | 13 | * Lua 5.1 14 | * LLVM/Clang - read the [getting started](http://clang.llvm.org/get_started.html) guide to find out how to obtain Clang from source. `libclang` is built and installed along with Clang compiler. 15 | 16 | Building 17 | ======== 18 | 19 | `luaclang-parser` uses CMake to find your Lua installation and `libclang`. The preferred way to build the module is this: 20 | 21 | luaclang-parser$ mkdir build; cd build 22 | luaclang-parser/build$ cmake .. 23 | luaclang-parser/build$ make 24 | 25 | The example uses this directory layout to find the `luaclang-parser` without the need to install it system-wide. 26 | 27 | Overview 28 | ======== 29 | 30 | `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`: 31 | 32 | * `Index` - represents a set of translation units that could be linked together 33 | * `TranslationUnit` - represents a source file 34 | * `Cursor` - represents an element in the AST in a translation unit 35 | * `Type` - the type of an element (variable, field, parameter, return type) 36 | 37 | Examples 38 | ======== 39 | 40 | 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: 41 | 42 | CREATE TABLE args (ismethod, parent, name, idx, type, const, defval); 43 | CREATE TABLE classes (module, name, parent); 44 | CREATE TABLE functions (module, name, result, signature); 45 | CREATE TABLE methods (class, name, kind, access, result, signature, static, virtual, signal, slot); 46 | 47 | References between arguments, functions/methods and classes are done through the [SQLite row identifier](http://www.sqlite.org/lang_createtable.html#rowid). For example to construct the database for `libclang`, run the following command: 48 | 49 | lua cindex.lua /usr/local/include/clang-c/Index.h 50 | 51 | You can then query the functions using SQL, for example to gather all functions which take `CXCursor` as the first argument: 52 | 53 | SELECT * 54 | FROM functions F 55 | JOIN args A ON A.parent = F.rowid 56 | WHERE A.idx = 1 AND A.type = 'CXCursor' 57 | 58 | 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: 59 | 60 | SELECT C.name, M.signature 61 | FROM classes C 62 | JOIN methods M ON M.class = C.rowid 63 | WHERE M.signal = 1 64 | 65 | Just for a taste on how big the Qt framework is: 66 | 67 | sqlite> SELECT COUNT(*) FROM classes; 68 | 1302 69 | sqlite> SELECT COUNT(*) FROM methods; 70 | 19612 71 | 72 | Reference 73 | ========= 74 | 75 | `luaclang-parser` 76 | ---------- 77 | 78 | Use `local parser = require "luaclang-parser"` to load the module. It exports one function: 79 | 80 | * `createIndex(excludePch : boolean, showDiagnostics : boolean) -> Index` 81 | 82 | Binding for [clang_createIndex](http://clang.llvm.org/doxygen/group__CINDEX.html#func-members). Will create an `Index` into which you can parse or load pre-compiled `TranslationUnit`s. 83 | 84 | `Index` 85 | ------- 86 | 87 | * `Index:parse([sourceFile : string,] args : table) -> TranslationUnit` 88 | 89 | Binding for [clang_parseTranslationUnit](http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#ga2baf83f8c3299788234c8bce55e4472e). 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`. 90 | 91 | * `Index:load(astFile : string) -> TranslationUnit` 92 | 93 | Binding for [clang_createTranslationUnit](http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#gaa2e74f6e28c438692fd4f5e3d3abda97). 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). 94 | 95 | `TranslationUnit` 96 | ----------------- 97 | 98 | * `TranslationUnit:cursor() -> Cursor` 99 | 100 | Binding for [clang_getTranslationUnitCursor](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#gaec6e69127920785e74e4a517423f4391). Returns the `Cursor` representing a given translation unit, which means you can access to classes and functions defined in a given file. 101 | 102 | * `TranslationUnit:file(fileName : string) -> string, number` 103 | 104 | Binding for [clang_getFile](http://clang.llvm.org/doxygen/group__CINDEX__FILES.html#gaa0554e2ea48ecd217a29314d3cbd2085). Returns the absolute file path and a `time_t` last modification time of `fileName`. 105 | 106 | * `TranslationUnit:diagnostics() -> { Diagnostic* }` 107 | 108 | Binding for [clang_getDiagnostic](http://clang.llvm.org/doxygen/group__CINDEX__DIAG.html#ga3f54a79e820c2ac9388611e98029afe5). 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. 109 | 110 | * `TranslationUnit:codeCompleteAt(file : string, line : number, column : number) -> { Completion* }, { Diagnostics* }` 111 | 112 | Binding for [code completion API](http://clang.llvm.org/doxygen/group__CINDEX__CODE__COMPLET.html). 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](http://clang.llvm.org/doxygen/group__CINDEX__CODE__COMPLET.html#ga82570056548565efdd6fc74e57e75bbd) without the `CXCompletionChunk_` prefix. If there are any annotations, the `annotations` key is a table of strings: 113 | 114 | completion = { 115 | priority = number, priority of given completion 116 | chunks = { 117 | kind = string, chunk kind 118 | text = string, chunk text 119 | }, 120 | [annotations = { string* }] 121 | } 122 | 123 | 124 | `Cursor` 125 | -------- 126 | 127 | You can compare whether two `Cursor`s represent the same element using the standard `==` Lua operator. 128 | 129 | * `Cursor:children() -> { Cursor * }` 130 | 131 | Binding over [clang_visitChildren](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__TRAVERSAL.html#ga5d0a813d937e1a7dcc35f206ad1f7a91). 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. 132 | 133 | * `Cursor:parent() -> Cursor` 134 | 135 | Binding for [clang_getCursorSemanticParent](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#gabc327b200d46781cf30cb84d4af3c877). 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. 136 | 137 | * `Cursor:name() -> string` 138 | 139 | Binding over [clang_getCursorSpelling](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gaad1c9b2a1c5ef96cebdbc62f1671c763). Returns the name of the entity referenced by cursor. `__tostring` for `Cursor` also points to this function. 140 | 141 | * `Cursor:displayName() -> string` 142 | 143 | Binding over [clang_getCursorDisplayName](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gac3eba3224d109a956f9ef96fd4fe5c83). Returns the display name of the entity, which for example is a function signature. 144 | 145 | * `Cursor:kind() -> string` 146 | 147 | Returns the [cursor kind](http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013) without the `CXCursor_` prefix, i.e. `"FunctionDecl"`. 148 | 149 | * `Cursor:arguments() -> { Cursor* }` 150 | 151 | Binding of [clang_Cursor_getArgument](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga673c5529d33eedd0b78aca5ac6fc1d7c). Returns a table array of `Cursor`s representing arguments of a function or a method. Returns an empty table if a cursor is not a method or function. 152 | 153 | * `Cursor:resultType() -> Type` 154 | 155 | Binding for [clang_getCursorResultType](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6995a2d6352e7136868574b299005a63). For a function or a method cursor, returns the return type of the function. 156 | 157 | * `Cursor:type() -> Type` 158 | 159 | Returns the `Type` of a given element or `nil` if not available. 160 | 161 | * `Cursor:access() -> string` 162 | 163 | When cursor kind is `"AccessSpecifier"`, returns one of `"private"`, `"protected"` and `"public"`. 164 | 165 | * `Cursor:location() -> string, number, number, number, number` 166 | 167 | Binding for [clang_getCursorExtent](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__SOURCE.html#ga79f6544534ab73c78a8494c4c0bc2840). 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. 168 | 169 | * `Cursor:referenced() -> Cursor` 170 | 171 | Binding for [clang_getCursorReferenced](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gabf059155921552e19fc2abed5b4ff73a). For a reference type, returns a cursor to the element it references, otherwise returns `nil`. 172 | 173 | * `Cursor:definition() -> Cursor` 174 | 175 | Binding for [clang_getCursorDefinition](http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gafcfbec461e561bf13f1e8540bbbd655b). For a reference or declaration, returns a cursor to the definition of the entity, otherwise returns `nil`. 176 | 177 | * `Cursor:isVirtual() -> boolean` 178 | 179 | For a C++ method, returns whether the method is virtual. 180 | 181 | * `Cursor:isStatic() -> boolean` 182 | 183 | For a C++ method, returns whether the method is static. 184 | 185 | `Type` 186 | ------ 187 | 188 | You can compare whether two `Type`s represent the same type using the standard `==` Lua operator. 189 | 190 | * `Type:name() -> string` 191 | 192 | Binding of [clang_getTypeKindSpelling](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6bd7b366d998fc67f4178236398d0666). Returns one of [CXTypeKind](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaad39de597b13a18882c21860f92b095a) as a string without the `CXType_` prefix. Type also has `__tostring` set to this method. 193 | 194 | * `Type:canonical() -> Type` 195 | 196 | Binding of [clang_getCanonicalType](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaa9815d77adc6823c58be0a0e32010f8c). Returns underlying type with all typedefs removed. 197 | 198 | * `Type:pointee() -> Type` 199 | 200 | Binding of [clang_getPointeeType](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaafa3eb34932d8da1358d50ed949ff3ee). For pointer type returns the type of the pointee. 201 | 202 | * `Type:isPod() -> boolean` 203 | 204 | Binding of [clang_isPODType](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga3e7fdbe3d246ed03298bd074c5b3703e)Returns true if the type is a "Plain Old Data" type. 205 | 206 | * `Type:isConst() -> boolean` 207 | 208 | Binding of [clang_isConstQualifiedType](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga8c3f8029254d5862bcd595d6c8778e5b). Returns true if the type has a "const" qualifier. 209 | 210 | * `Type:declaration() -> Cursor` 211 | 212 | Binding of [clang_getTypeDeclaration](http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga0aad74ea93a2f5dea58fd6fc0db8aad4). Returns a `Cursor` to the declaration of a given type, or `nil`. 213 | 214 | 215 | License 216 | ======= 217 | 218 | Copyright (c) 2012 Michal Kottman 219 | 220 | 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: 221 | 222 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 223 | 224 | 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. -------------------------------------------------------------------------------- /examples/allqt.cpp: -------------------------------------------------------------------------------- 1 | #define QT_NO_TRANSLATION 2 | #define QT_NO_QOBJECT_CHECK 3 | 4 | #include "qt4-qobjectdefs-injected.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | -------------------------------------------------------------------------------- /examples/cindex.lua: -------------------------------------------------------------------------------- 1 | package.cpath = package.cpath .. ';../build/?.so' 2 | 3 | local clang = require 'luaclang-parser' 4 | local sqlite3 = require 'luasql.sqlite3'.sqlite3 5 | 6 | -- from http://stevedonovan.github.com/Penlight/api/modules/pl.text.html#format_operator 7 | do 8 | local format = string.format 9 | 10 | -- a more forgiving version of string.format, which applies 11 | -- tostring() to any value with a %s format. 12 | local function formatx (fmt,...) 13 | local args = {...} 14 | local i = 1 15 | for p in fmt:gmatch('%%.') do 16 | if p == '%s' and type(args[i]) ~= 'string' then 17 | args[i] = tostring(args[i]) 18 | end 19 | i = i + 1 20 | end 21 | return format(fmt,unpack(args)) 22 | end 23 | 24 | -- Note this goes further than the original, and will allow these cases: 25 | -- 1. a single value 26 | -- 2. a list of values 27 | getmetatable("").__mod = function(a, b) 28 | if b == nil then 29 | return a 30 | elseif type(b) == "table" then 31 | return formatx(a,unpack(b)) 32 | else 33 | return formatx(a,b) 34 | end 35 | end 36 | end 37 | 38 | do 39 | local start = os.clock() 40 | local lastTime = start 41 | function SECTION(...) 42 | local now = os.clock() 43 | print(("[%6.3f/%6.3f]"):format(now-start, now-lastTime), ...) 44 | lastTime = now 45 | end 46 | end 47 | 48 | SECTION "Start" 49 | 50 | ---[[ 51 | local DBG = function() end 52 | --[=[]] 53 | local DBG = print 54 | --]=] 55 | 56 | do 57 | local cache = setmetatable({}, {__mode="k"}) 58 | function getExtent(file, fromRow, fromCol, toRow, toCol) 59 | if not file then 60 | DBG(file, fromRow, fromCol, toRow, toCol) 61 | return '' 62 | end 63 | if toRow - fromRow > 3 then 64 | return ('%s: %d:%d - %d:%d'):format(file, fromRow, fromCol, toRow, toCol) 65 | end 66 | if not cache[file] then 67 | local f = assert(io.open(file)) 68 | local t, n = {}, 0 69 | for l in f:lines() do 70 | n = n + 1 71 | t[n] = l 72 | end 73 | cache[file] = t 74 | end 75 | local lines = cache[file] 76 | if not (lines and lines[fromRow] and lines[toRow]) then 77 | DBG('!!! Missing lines '..fromRow..'-'..toRow..' in file '..file) 78 | return '' 79 | end 80 | if fromRow == toRow then 81 | return lines[fromRow]:sub(fromCol, toCol-1) 82 | else 83 | local res = {} 84 | for i=fromRow, toRow do 85 | if i==fromRow then 86 | res[#res+1] = lines[i]:sub(fromCol) 87 | elseif i==toRow then 88 | res[#res+1] = lines[i]:sub(1,toCol-1) 89 | else 90 | res[#res+1] = lines[i] 91 | end 92 | end 93 | return table.concat(res, '\n') 94 | end 95 | end 96 | end 97 | 98 | function findChildrenByType(cursor, type) 99 | local children, n = {}, 0 100 | local function finder(cur) 101 | for i,c in ipairs(cur:children()) do 102 | if c and (c:kind() == type) then 103 | n = n + 1 104 | children[n] = c 105 | end 106 | finder(c) 107 | end 108 | end 109 | finder(cursor) 110 | return children 111 | end 112 | 113 | function translateType(cur, typ) 114 | if not typ then 115 | typ = cur:type() 116 | end 117 | 118 | local typeKind = tostring(typ) 119 | if typeKind == 'Typedef' or typeKind == 'Record' then 120 | return typ:declaration():name() 121 | elseif typeKind == 'Pointer' then 122 | return translateType(cur, typ:pointee()) .. '*' 123 | elseif typeKind == 'LValueReference' then 124 | return translateType(cur, typ:pointee()) .. '&' 125 | elseif typeKind == 'Unexposed' then 126 | local def = getExtent(cur:location()) 127 | DBG('!Unexposed!', def) 128 | return def 129 | else 130 | return typeKind 131 | end 132 | end 133 | 134 | 135 | 136 | SECTION 'Creating index' 137 | local index = clang.createIndex(false, true) 138 | 139 | SECTION 'Creating translation unit' 140 | ---[[ 141 | local tu = assert(index:parse(arg)) 142 | --[=[]] 143 | local tu = assert(index:load('precompiled.ast')) 144 | --]=] 145 | 146 | SECTION "Writing code.xml - raw AST" 147 | 148 | local function trim(s) 149 | local from = s:match"^%s*()" 150 | local res = from > #s and "" or s:match(".*%S", from) 151 | return (res:gsub('&', '&'):gsub('<', '<'):gsub('>', '>'):gsub('"', '"')) 152 | end 153 | 154 | local xml = assert(io.open('code.xml', 'w')) 155 | local function dumpXML(cur) 156 | local tag = cur:kind() 157 | local name = trim(cur:name()) 158 | local attr = ' name="' .. name .. '"' 159 | local dname = trim(cur:displayName()) 160 | if dname ~= name then 161 | attr = attr .. ' display="' .. dname .. '"' 162 | end 163 | attr = attr ..' text="' .. trim(getExtent(cur:location())) .. '"' 164 | local children = cur:children() 165 | if #children == 0 then 166 | xml:write('<', tag, attr, ' />\n') 167 | else 168 | xml:write('<', tag, attr, ' >\n') 169 | for _,c in ipairs(children) do 170 | dumpXML(c) 171 | end 172 | xml:write('\n') 173 | end 174 | end 175 | dumpXML(tu:cursor()) 176 | 177 | SECTION "Finished" 178 | 179 | local default_table_meta = {} 180 | function new_default_table(t) 181 | return setmetatable(t or {}, default_table_meta) 182 | end 183 | function default_table_meta.__index(t,k) 184 | local v = {} 185 | rawset(t, k, v) 186 | return v 187 | end 188 | 189 | local DB = new_default_table() 190 | 191 | function dumpChildren(cur, indent) 192 | indent = indent or '\t\t@' 193 | local children = cur:children() 194 | for i,c in ipairs(children) do 195 | DBG(indent, i..'/'..#children, c:kind(), c:name(), getExtent(c:location())) 196 | dumpChildren(c, indent..'\t') 197 | end 198 | end 199 | 200 | function processArgument(idx, arg) 201 | local name = arg:name() 202 | local type = translateType(arg, arg:type()) 203 | 204 | local children = arg:children() 205 | 206 | local const, default 207 | 208 | if tostring(type) == 'LValueReference' then 209 | const = type:pointee():isConst() 210 | end 211 | 212 | if #children > 0 then 213 | if #children == 1 and children[1]:kind() ~= 'TypeRef' then 214 | default = getExtent(children[1]:location()) 215 | else 216 | local newtype = {} 217 | for i,c in ipairs(children) do 218 | local kind = c:kind() 219 | if kind == 'NamespaceRef' or kind == 'TypeRef' then 220 | newtype[#newtype+1] = c:referenced():name() 221 | elseif kind == 'DeclRef' then 222 | default = getExtent(c:location()) 223 | end 224 | end 225 | if #newtype > 0 then type = table.concat(newtype, '::') end 226 | end 227 | end 228 | 229 | DBG('', '', idx, name, type) 230 | 231 | return { 232 | name = name, 233 | type = type, 234 | const = const, 235 | default = default, 236 | } 237 | end 238 | 239 | function processMethod(method, kind, access) 240 | DBG('', '=>', access, kind, method:displayName(), getExtent(method:location())) 241 | 242 | -- process argument 243 | local argTable = {} 244 | local args = method:arguments() 245 | for i, arg in ipairs(args) do 246 | argTable[i] = processArgument(i, arg) 247 | end 248 | 249 | -- check for signal / slot, courtesy of qt4-qobjectdefs-injected.h 250 | local signal, slot 251 | for _, child in ipairs(method:children()) do 252 | if child:kind() == 'AnnotateAttr' then 253 | local name = child:name() 254 | if name == 'qt_signal' then 255 | signal = true 256 | elseif name == 'qt_slot' then 257 | slot = true 258 | end 259 | end 260 | end 261 | 262 | -- get return type 263 | local result 264 | if kind == 'CXXMethod' then 265 | result = translateType(method, method:resultType()) 266 | end 267 | 268 | -- virtual / static 269 | local virtual, static 270 | if method:isVirtual() then 271 | virtual = true 272 | elseif method:isStatic() then 273 | static = true 274 | end 275 | 276 | return { 277 | name = method:name(), 278 | access = access, 279 | signature = method:displayName(), 280 | kind = kind, 281 | args = argTable, 282 | result = result, 283 | signal = signal, 284 | slot = slot, 285 | virtual = virtual, 286 | static = static 287 | } 288 | end 289 | 290 | SECTION "Processing classes" 291 | 292 | local classes = findChildrenByType(tu:cursor(), 'ClassDecl') 293 | for _, class in ipairs(classes) do 294 | local name = class:name() 295 | local dname = class:displayName() 296 | 297 | if name ~= dname then 298 | DBG(name, '->', dname) 299 | else 300 | DBG(name) 301 | end 302 | 303 | local DBClass = DB[class:displayName()] 304 | DBClass.methods = DBClass.methods or {} 305 | 306 | local children = class:children() 307 | local access = 'private' 308 | for _, method in ipairs(children) do 309 | local kind = method:kind() 310 | DBG('=>', kind) 311 | if kind == 'CXXMethod' then 312 | table.insert(DBClass.methods, processMethod(method, kind, access)) 313 | elseif kind == 'Constructor' then 314 | table.insert(DBClass.methods, processMethod(method, kind, access)) 315 | -- table.insert(DBClass.constructors, processMethod(method, kind, access)) 316 | elseif kind == 'Destructor' then 317 | table.insert(DBClass.methods, processMethod(method, kind, access)) 318 | -- table.insert(DBClass.destructors, processMethod(method, kind, access)) 319 | elseif kind == 'CXXAccessSpecifier' then 320 | access = method:access() 321 | elseif kind == 'EnumDecl' then 322 | DBG('', 'enum', method:displayName()) 323 | for _,enum in ipairs(method:children()) do 324 | DBG('', '->', enum:name()) 325 | end 326 | elseif kind == 'VarDecl' or kind == 'FieldDecl' then 327 | DBG(name, access, kind, method:name(), translateType(method)) 328 | elseif kind == 'UnexposedDecl' then 329 | DBG('!!!', name, getExtent(method:location())) 330 | elseif kind == 'CXXBaseSpecifier' then 331 | local parent = method:referenced() 332 | DBClass.parent = parent:name() 333 | else 334 | DBG('???', name, kind, getExtent(method:location())) 335 | end 336 | end 337 | end 338 | 339 | SECTION "Processing functions" 340 | 341 | function processFunction(func) 342 | DBG('=>', func:displayName(), getExtent(func:location())) 343 | 344 | -- process argument 345 | local argTable = {} 346 | local args = func:arguments() 347 | for i, arg in ipairs(args) do 348 | argTable[i] = processArgument(i, arg) 349 | end 350 | 351 | local result = translateType(func, func:resultType()) 352 | 353 | return { 354 | name = func:name(), 355 | signature = func:displayName(), 356 | args = argTable, 357 | result = result, 358 | } 359 | end 360 | 361 | local functions = findChildrenByType(tu:cursor(), "FunctionDecl") 362 | local FUNC = {} 363 | for _, func in ipairs(functions) do 364 | local name = func:name() 365 | local dname = func:displayName() 366 | if not FUNC[dname] then 367 | DBG(_, name, dname) 368 | for i,arg in ipairs(func:arguments()) do 369 | DBG('', i, arg:name(), translateType(arg, arg:type())) 370 | FUNC[dname] = processFunction(func) 371 | end 372 | end 373 | end 374 | 375 | 376 | SECTION "Saving results" 377 | 378 | local env = sqlite3() 379 | local db = env:connect('code.db') 380 | local E = function(s) 381 | if type(s) == 'nil' or (type(s) == "string" and #s == 0) then 382 | return 'NULL' 383 | elseif type(s) == 'string' then 384 | return "'"..s:gsub("'", "''").."'" 385 | else 386 | print('???', s, type(s)) 387 | return 'NULL' 388 | end 389 | end 390 | local B = function(b) return b and '1' or '0' end 391 | 392 | for _,tab in ipairs{"modules", "classes", "methods", "functions", "args"} do 393 | assert(db:execute("DROP TABLE IF EXISTS %s" % tab)) 394 | end 395 | assert(db:execute("CREATE TABLE modules (name PRIMARY KEY)")) 396 | assert(db:execute("CREATE TABLE classes (module, name, parent)")) 397 | assert(db:execute("CREATE TABLE methods (class, name, kind, access, result, signature, static, virtual, signal, slot)")) 398 | assert(db:execute("CREATE TABLE functions (module, name, result, signature)")) 399 | assert(db:execute("CREATE TABLE args (ismethod, parent, name, idx, type, const, defval)")) 400 | 401 | db:execute("BEGIN") 402 | 403 | assert(db:execute("INSERT INTO modules VALUES ('test')")); 404 | local modId = db:getlastautoid() 405 | 406 | for name, class in pairs(DB) do 407 | assert(db:execute("INSERT INTO classes VALUES (%d, %s, %s)" % {modId, E(name), E(class.parent)})) 408 | local cid = db:getlastautoid() 409 | 410 | for _, m in ipairs(class.methods) do 411 | assert(db:execute( 412 | "INSERT INTO methods VALUES (%d, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % { 413 | cid, E(m.name), E(m.kind), E(m.access), E(m.result), E(m.signature), 414 | B(m.static), B(m.virtual), B(m.signal), B(m.slot) 415 | })) 416 | local mid = db:getlastautoid() 417 | for i, a in ipairs(m.args) do 418 | local cmd = "INSERT INTO args VALUES (1, %d, %s, %d, %s, %d, %s)" % { 419 | mid, E(a.name), i, E(a.type), B(a.const), E(a.default) 420 | } 421 | local ok, err = db:execute(cmd) 422 | if not ok then print(cmd) error(err) end 423 | end 424 | end 425 | end 426 | 427 | 428 | for _, f in pairs(FUNC) do 429 | assert(db:execute( 430 | "INSERT INTO functions VALUES (%d, %s, %s, %s)" % { 431 | modId, E(f.name), E(f.result), E(f.signature) 432 | })) 433 | local fid = db:getlastautoid() 434 | for i, a in ipairs(f.args) do 435 | local cmd = "INSERT INTO args VALUES (0, %d, %s, %d, %s, %d, %s)" % { 436 | fid, E(a.name), i, E(a.type), B(a.const), E(a.default) 437 | } 438 | local ok, err = db:execute(cmd) 439 | if not ok then print(cmd) error(err) end 440 | end 441 | end 442 | 443 | 444 | db:execute("COMMIT") 445 | -------------------------------------------------------------------------------- /examples/qt4-qobjectdefs-injected.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 4 | ** All rights reserved. 5 | ** Contact: Nokia Corporation (qt-info@nokia.com) 6 | ** 7 | ** This file is part of the QtCore module of the Qt Toolkit. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser General Public 12 | ** License version 2.1 as published by the Free Software Foundation and 13 | ** appearing in the file LICENSE.LGPL included in the packaging of this 14 | ** file. Please review the following information to ensure the GNU Lesser 15 | ** General Public License version 2.1 requirements will be met: 16 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 17 | ** 18 | ** In addition, as a special exception, Nokia gives you certain additional 19 | ** rights. These rights are described in the Nokia Qt LGPL Exception 20 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 21 | ** 22 | ** GNU General Public License Usage 23 | ** Alternatively, this file may be used under the terms of the GNU General 24 | ** Public License version 3.0 as published by the Free Software Foundation 25 | ** and appearing in the file LICENSE.GPL included in the packaging of this 26 | ** file. Please review the following information to ensure the GNU General 27 | ** Public License version 3.0 requirements will be met: 28 | ** http://www.gnu.org/copyleft/gpl.html. 29 | ** 30 | ** Other Usage 31 | ** Alternatively, this file may be used in accordance with the terms and 32 | ** conditions contained in a signed written agreement between you and Nokia. 33 | ** 34 | ** 35 | ** 36 | ** 37 | ** 38 | ** $QT_END_LICENSE$ 39 | ** 40 | ****************************************************************************/ 41 | 42 | #ifndef QOBJECTDEFS_H 43 | #define QOBJECTDEFS_H 44 | 45 | #include 46 | 47 | QT_BEGIN_HEADER 48 | 49 | QT_BEGIN_NAMESPACE 50 | 51 | QT_MODULE(Core) 52 | 53 | class QByteArray; 54 | 55 | class QString; 56 | 57 | #ifndef Q_MOC_OUTPUT_REVISION 58 | #define Q_MOC_OUTPUT_REVISION 63 59 | #endif 60 | 61 | // The following macros are our "extensions" to C++ 62 | // They are used, strictly speaking, only by the moc. 63 | 64 | #ifndef Q_MOC_RUN 65 | # if defined(QT_NO_KEYWORDS) 66 | # define QT_NO_EMIT 67 | # else 68 | # define slots __attribute__((annotate("qt_slot"))) 69 | # define signals protected __attribute__((annotate("qt_signal"))) 70 | # endif 71 | # define Q_SLOTS __attribute__((annotate("qt_slot"))) 72 | # define Q_SIGNALS protected __attribute__((annotate("qt_signal"))) 73 | # define Q_PRIVATE_SLOT(d, signature) 74 | # define Q_EMIT 75 | #ifndef QT_NO_EMIT 76 | # define emit 77 | #endif 78 | #define Q_CLASSINFO(name, value) 79 | #define Q_INTERFACES(x) 80 | #define Q_PROPERTY(text) 81 | #define Q_PRIVATE_PROPERTY(d, text) 82 | #define Q_REVISION(v) 83 | #define Q_OVERRIDE(text) 84 | #define Q_ENUMS(x) 85 | #define Q_FLAGS(x) 86 | #ifdef QT3_SUPPORT 87 | # define Q_SETS(x) 88 | #endif 89 | #define Q_SCRIPTABLE 90 | #define Q_INVOKABLE 91 | #define Q_SIGNAL __attribute__((annotate("qt_signal"))) 92 | #define Q_SLOT __attribute__((annotate("qt_slot"))) 93 | 94 | #ifndef QT_NO_TRANSLATION 95 | # ifndef QT_NO_TEXTCODEC 96 | // full set of tr functions 97 | // ### Qt 5: merge overloads 98 | # define QT_TR_FUNCTIONS \ 99 | static inline QString tr(const char *s, const char *c = 0) \ 100 | { return staticMetaObject.tr(s, c); } \ 101 | static inline QString trUtf8(const char *s, const char *c = 0) \ 102 | { return staticMetaObject.trUtf8(s, c); } \ 103 | static inline QString tr(const char *s, const char *c, int n) \ 104 | { return staticMetaObject.tr(s, c, n); } \ 105 | static inline QString trUtf8(const char *s, const char *c, int n) \ 106 | { return staticMetaObject.trUtf8(s, c, n); } 107 | # else 108 | // no QTextCodec, no utf8 109 | // ### Qt 5: merge overloads 110 | # define QT_TR_FUNCTIONS \ 111 | static inline QString tr(const char *s, const char *c = 0) \ 112 | { return staticMetaObject.tr(s, c); } \ 113 | static inline QString tr(const char *s, const char *c, int n) \ 114 | { return staticMetaObject.tr(s, c, n); } 115 | # endif 116 | #else 117 | // inherit the ones from QObject 118 | # define QT_TR_FUNCTIONS 119 | #endif 120 | 121 | #if defined(QT_NO_QOBJECT_CHECK) 122 | /* tmake ignore Q_OBJECT */ 123 | #define Q_OBJECT_CHECK 124 | #else 125 | 126 | /* This is a compile time check that ensures that any class cast with qobject_cast 127 | actually contains a Q_OBJECT macro. Note: qobject_cast will fail if a QObject 128 | subclass doesn't contain Q_OBJECT. 129 | 130 | In qt_check_for_QOBJECT_macro, we call a dummy templated function with two 131 | parameters, the first being "this" and the other the target of the qobject 132 | cast. If the types are not identical, we know that a Q_OBJECT macro is missing. 133 | 134 | If you get a compiler error here, make sure that the class you are casting 135 | to contains a Q_OBJECT macro. 136 | */ 137 | 138 | /* tmake ignore Q_OBJECT */ 139 | #define Q_OBJECT_CHECK \ 140 | template inline void qt_check_for_QOBJECT_macro(const T &_q_argument) const \ 141 | { int i = qYouForgotTheQ_OBJECT_Macro(this, &_q_argument); i = i; } 142 | 143 | template 144 | inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; } 145 | 146 | template 147 | inline void qYouForgotTheQ_OBJECT_Macro(T1, T2) {} 148 | #endif // QT_NO_QOBJECT_CHECK 149 | 150 | #ifdef Q_NO_DATA_RELOCATION 151 | #define Q_OBJECT_GETSTATICMETAOBJECT static const QMetaObject &getStaticMetaObject(); 152 | #else 153 | #define Q_OBJECT_GETSTATICMETAOBJECT 154 | #endif 155 | 156 | /* tmake ignore Q_OBJECT */ 157 | #define Q_OBJECT \ 158 | public: \ 159 | Q_OBJECT_CHECK \ 160 | static const QMetaObject staticMetaObject; \ 161 | Q_OBJECT_GETSTATICMETAOBJECT \ 162 | virtual const QMetaObject *metaObject() const; \ 163 | virtual void *qt_metacast(const char *); \ 164 | QT_TR_FUNCTIONS \ 165 | virtual int qt_metacall(QMetaObject::Call, int, void **); \ 166 | private: \ 167 | Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \ 168 | Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); 169 | 170 | /* tmake ignore Q_OBJECT */ 171 | #define Q_OBJECT_FAKE Q_OBJECT 172 | /* tmake ignore Q_GADGET */ 173 | #define Q_GADGET \ 174 | public: \ 175 | static const QMetaObject staticMetaObject; \ 176 | Q_OBJECT_GETSTATICMETAOBJECT \ 177 | private: 178 | #else // Q_MOC_RUN 179 | #define slots slots 180 | #define signals signals 181 | #define Q_SLOTS Q_SLOTS 182 | #define Q_SIGNALS Q_SIGNALS 183 | #define Q_CLASSINFO(name, value) Q_CLASSINFO(name, value) 184 | #define Q_INTERFACES(x) Q_INTERFACES(x) 185 | #define Q_PROPERTY(text) Q_PROPERTY(text) 186 | #define Q_PRIVATE_PROPERTY(d, text) Q_PRIVATE_PROPERTY(d, text) 187 | #define Q_REVISION(v) Q_REVISION(v) 188 | #define Q_OVERRIDE(text) Q_OVERRIDE(text) 189 | #define Q_ENUMS(x) Q_ENUMS(x) 190 | #define Q_FLAGS(x) Q_FLAGS(x) 191 | #ifdef QT3_SUPPORT 192 | # define Q_SETS(x) Q_SETS(x) 193 | #endif 194 | /* tmake ignore Q_OBJECT */ 195 | #define Q_OBJECT Q_OBJECT 196 | /* tmake ignore Q_OBJECT */ 197 | #define Q_OBJECT_FAKE Q_OBJECT_FAKE 198 | /* tmake ignore Q_GADGET */ 199 | #define Q_GADGET Q_GADGET 200 | #define Q_SCRIPTABLE Q_SCRIPTABLE 201 | #define Q_INVOKABLE Q_INVOKABLE 202 | #define Q_SIGNAL Q_SIGNAL 203 | #define Q_SLOT Q_SLOT 204 | #endif //Q_MOC_RUN 205 | 206 | // macro for onaming members 207 | #ifdef METHOD 208 | #undef METHOD 209 | #endif 210 | #ifdef SLOT 211 | #undef SLOT 212 | #endif 213 | #ifdef SIGNAL 214 | #undef SIGNAL 215 | #endif 216 | 217 | Q_CORE_EXPORT const char *qFlagLocation(const char *method); 218 | 219 | #define QTOSTRING_HELPER(s) #s 220 | #define QTOSTRING(s) QTOSTRING_HELPER(s) 221 | #ifndef QT_NO_DEBUG 222 | # define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__) 223 | # ifndef QT_NO_KEYWORDS 224 | # define METHOD(a) qFlagLocation("0"#a QLOCATION) 225 | # endif 226 | # define SLOT(a) a 227 | # define SIGNAL(a) a 228 | #else 229 | # ifndef QT_NO_KEYWORDS 230 | # define METHOD(a) "0"#a 231 | # endif 232 | # define SLOT(a) "1"#a 233 | # define SIGNAL(a) "2"#a 234 | #endif 235 | 236 | #ifdef QT3_SUPPORT 237 | #define METHOD_CODE 0 // member type codes 238 | #define SLOT_CODE 1 239 | #define SIGNAL_CODE 2 240 | #endif 241 | 242 | #define QMETHOD_CODE 0 // member type codes 243 | #define QSLOT_CODE 1 244 | #define QSIGNAL_CODE 2 245 | 246 | #define Q_ARG(type, data) QArgument(#type, data) 247 | #define Q_RETURN_ARG(type, data) QReturnArgument(#type, data) 248 | 249 | class QObject; 250 | class QMetaMethod; 251 | class QMetaEnum; 252 | class QMetaProperty; 253 | class QMetaClassInfo; 254 | 255 | 256 | class Q_CORE_EXPORT QGenericArgument 257 | { 258 | public: 259 | inline QGenericArgument(const char *aName = 0, const void *aData = 0) 260 | : _data(aData), _name(aName) {} 261 | inline void *data() const { return const_cast(_data); } 262 | inline const char *name() const { return _name; } 263 | 264 | private: 265 | const void *_data; 266 | const char *_name; 267 | }; 268 | 269 | class Q_CORE_EXPORT QGenericReturnArgument: public QGenericArgument 270 | { 271 | public: 272 | inline QGenericReturnArgument(const char *aName = 0, void *aData = 0) 273 | : QGenericArgument(aName, aData) 274 | {} 275 | }; 276 | 277 | template 278 | class QArgument: public QGenericArgument 279 | { 280 | public: 281 | inline QArgument(const char *aName, const T &aData) 282 | : QGenericArgument(aName, static_cast(&aData)) 283 | {} 284 | }; 285 | template 286 | class QArgument: public QGenericArgument 287 | { 288 | public: 289 | inline QArgument(const char *aName, T &aData) 290 | : QGenericArgument(aName, static_cast(&aData)) 291 | {} 292 | }; 293 | 294 | 295 | template 296 | class QReturnArgument: public QGenericReturnArgument 297 | { 298 | public: 299 | inline QReturnArgument(const char *aName, T &aData) 300 | : QGenericReturnArgument(aName, static_cast(&aData)) 301 | {} 302 | }; 303 | 304 | struct Q_CORE_EXPORT QMetaObject 305 | { 306 | const char *className() const; 307 | const QMetaObject *superClass() const; 308 | 309 | QObject *cast(QObject *obj) const; 310 | const QObject *cast(const QObject *obj) const; 311 | 312 | #ifndef QT_NO_TRANSLATION 313 | // ### Qt 4: Merge overloads 314 | QString tr(const char *s, const char *c) const; 315 | QString trUtf8(const char *s, const char *c) const; 316 | QString tr(const char *s, const char *c, int n) const; 317 | QString trUtf8(const char *s, const char *c, int n) const; 318 | #endif // QT_NO_TRANSLATION 319 | 320 | int methodOffset() const; 321 | int enumeratorOffset() const; 322 | int propertyOffset() const; 323 | int classInfoOffset() const; 324 | 325 | int constructorCount() const; 326 | int methodCount() const; 327 | int enumeratorCount() const; 328 | int propertyCount() const; 329 | int classInfoCount() const; 330 | 331 | int indexOfConstructor(const char *constructor) const; 332 | int indexOfMethod(const char *method) const; 333 | int indexOfSignal(const char *signal) const; 334 | int indexOfSlot(const char *slot) const; 335 | int indexOfEnumerator(const char *name) const; 336 | int indexOfProperty(const char *name) const; 337 | int indexOfClassInfo(const char *name) const; 338 | 339 | QMetaMethod constructor(int index) const; 340 | QMetaMethod method(int index) const; 341 | QMetaEnum enumerator(int index) const; 342 | QMetaProperty property(int index) const; 343 | QMetaClassInfo classInfo(int index) const; 344 | QMetaProperty userProperty() const; 345 | 346 | static bool checkConnectArgs(const char *signal, const char *method); 347 | static QByteArray normalizedSignature(const char *method); 348 | static QByteArray normalizedType(const char *type); 349 | 350 | // internal index-based connect 351 | static bool connect(const QObject *sender, int signal_index, 352 | const QObject *receiver, int method_index, 353 | int type = 0, int *types = 0); 354 | // internal index-based disconnect 355 | static bool disconnect(const QObject *sender, int signal_index, 356 | const QObject *receiver, int method_index); 357 | static bool disconnectOne(const QObject *sender, int signal_index, 358 | const QObject *receiver, int method_index); 359 | // internal slot-name based connect 360 | static void connectSlotsByName(QObject *o); 361 | 362 | // internal index-based signal activation 363 | static void activate(QObject *sender, int signal_index, void **argv); //obsolete 364 | static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete 365 | static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv); 366 | static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete 367 | 368 | // internal guarded pointers 369 | static void addGuard(QObject **ptr); 370 | static void removeGuard(QObject **ptr); 371 | static void changeGuard(QObject **ptr, QObject *o); 372 | 373 | static bool invokeMethod(QObject *obj, const char *member, 374 | Qt::ConnectionType, 375 | QGenericReturnArgument ret, 376 | QGenericArgument val0 = QGenericArgument(0), 377 | QGenericArgument val1 = QGenericArgument(), 378 | QGenericArgument val2 = QGenericArgument(), 379 | QGenericArgument val3 = QGenericArgument(), 380 | QGenericArgument val4 = QGenericArgument(), 381 | QGenericArgument val5 = QGenericArgument(), 382 | QGenericArgument val6 = QGenericArgument(), 383 | QGenericArgument val7 = QGenericArgument(), 384 | QGenericArgument val8 = QGenericArgument(), 385 | QGenericArgument val9 = QGenericArgument()); 386 | 387 | static inline bool invokeMethod(QObject *obj, const char *member, 388 | QGenericReturnArgument ret, 389 | QGenericArgument val0 = QGenericArgument(0), 390 | QGenericArgument val1 = QGenericArgument(), 391 | QGenericArgument val2 = QGenericArgument(), 392 | QGenericArgument val3 = QGenericArgument(), 393 | QGenericArgument val4 = QGenericArgument(), 394 | QGenericArgument val5 = QGenericArgument(), 395 | QGenericArgument val6 = QGenericArgument(), 396 | QGenericArgument val7 = QGenericArgument(), 397 | QGenericArgument val8 = QGenericArgument(), 398 | QGenericArgument val9 = QGenericArgument()) 399 | { 400 | return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, 401 | val4, val5, val6, val7, val8, val9); 402 | } 403 | 404 | static inline bool invokeMethod(QObject *obj, const char *member, 405 | Qt::ConnectionType type, 406 | QGenericArgument val0 = QGenericArgument(0), 407 | QGenericArgument val1 = QGenericArgument(), 408 | QGenericArgument val2 = QGenericArgument(), 409 | QGenericArgument val3 = QGenericArgument(), 410 | QGenericArgument val4 = QGenericArgument(), 411 | QGenericArgument val5 = QGenericArgument(), 412 | QGenericArgument val6 = QGenericArgument(), 413 | QGenericArgument val7 = QGenericArgument(), 414 | QGenericArgument val8 = QGenericArgument(), 415 | QGenericArgument val9 = QGenericArgument()) 416 | { 417 | return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, 418 | val3, val4, val5, val6, val7, val8, val9); 419 | } 420 | 421 | static inline bool invokeMethod(QObject *obj, const char *member, 422 | QGenericArgument val0 = QGenericArgument(0), 423 | QGenericArgument val1 = QGenericArgument(), 424 | QGenericArgument val2 = QGenericArgument(), 425 | QGenericArgument val3 = QGenericArgument(), 426 | QGenericArgument val4 = QGenericArgument(), 427 | QGenericArgument val5 = QGenericArgument(), 428 | QGenericArgument val6 = QGenericArgument(), 429 | QGenericArgument val7 = QGenericArgument(), 430 | QGenericArgument val8 = QGenericArgument(), 431 | QGenericArgument val9 = QGenericArgument()) 432 | { 433 | return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, 434 | val1, val2, val3, val4, val5, val6, val7, val8, val9); 435 | } 436 | 437 | QObject *newInstance(QGenericArgument val0 = QGenericArgument(0), 438 | QGenericArgument val1 = QGenericArgument(), 439 | QGenericArgument val2 = QGenericArgument(), 440 | QGenericArgument val3 = QGenericArgument(), 441 | QGenericArgument val4 = QGenericArgument(), 442 | QGenericArgument val5 = QGenericArgument(), 443 | QGenericArgument val6 = QGenericArgument(), 444 | QGenericArgument val7 = QGenericArgument(), 445 | QGenericArgument val8 = QGenericArgument(), 446 | QGenericArgument val9 = QGenericArgument()) const; 447 | 448 | enum Call { 449 | InvokeMetaMethod, 450 | ReadProperty, 451 | WriteProperty, 452 | ResetProperty, 453 | QueryPropertyDesignable, 454 | QueryPropertyScriptable, 455 | QueryPropertyStored, 456 | QueryPropertyEditable, 457 | QueryPropertyUser, 458 | CreateInstance 459 | }; 460 | 461 | int static_metacall(Call, int, void **) const; 462 | static int metacall(QObject *, Call, int, void **); 463 | 464 | #ifdef QT3_SUPPORT 465 | QT3_SUPPORT const char *superClassName() const; 466 | #endif 467 | 468 | struct { // private data 469 | const QMetaObject *superdata; 470 | const char *stringdata; 471 | const uint *data; 472 | const void *extradata; 473 | } d; 474 | }; 475 | 476 | typedef const QMetaObject& (*QMetaObjectAccessor)(); 477 | 478 | struct QMetaObjectExtraData 479 | { 480 | #ifdef Q_NO_DATA_RELOCATION 481 | const QMetaObjectAccessor *objects; 482 | #else 483 | const QMetaObject **objects; 484 | #endif 485 | 486 | typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); //from revision 6 487 | //typedef int (*StaticMetaCall)(QMetaObject::Call, int, void **); //used from revison 2 until revison 5 488 | StaticMetacallFunction static_metacall; 489 | }; 490 | 491 | inline const char *QMetaObject::className() const 492 | { return d.stringdata; } 493 | 494 | inline const QMetaObject *QMetaObject::superClass() const 495 | { return d.superdata; } 496 | 497 | #ifdef QT3_SUPPORT 498 | inline const char *QMetaObject::superClassName() const 499 | { return d.superdata ? d.superdata->className() : 0; } 500 | #endif 501 | 502 | QT_END_NAMESPACE 503 | 504 | QT_END_HEADER 505 | 506 | #endif // QOBJECTDEFS_H 507 | -------------------------------------------------------------------------------- /luaclang-parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define LCM_INDEX "ClangIndex" 5 | #define LCM_TU "ClangTU" 6 | #define LCM_CURSOR "ClangCursor" 7 | #define LCM_TYPE "ClangType" 8 | #define LCM_FILE "ClangFile" 9 | 10 | static CXIndex * newIndex(lua_State *L) { 11 | CXIndex *idx = (CXIndex*) lua_newuserdata(L, sizeof(CXIndex)); 12 | luaL_getmetatable(L, LCM_INDEX); 13 | lua_setmetatable(L, -2); 14 | return idx; 15 | } 16 | 17 | static CXIndex toIndex(lua_State *L, int n) { 18 | CXIndex * idx = (CXIndex*) luaL_checkudata(L, n, LCM_INDEX); 19 | return *idx; 20 | } 21 | 22 | static CXTranslationUnit * newTU(lua_State *L) { 23 | CXTranslationUnit *tu = (CXTranslationUnit*) lua_newuserdata(L, sizeof(CXTranslationUnit)); 24 | luaL_getmetatable(L, LCM_TU); 25 | lua_setmetatable(L, -2); 26 | return tu; 27 | } 28 | 29 | static CXTranslationUnit toTU(lua_State *L, int n) { 30 | CXTranslationUnit * tu = (CXTranslationUnit*) luaL_checkudata(L, n, LCM_TU); 31 | return *tu; 32 | } 33 | 34 | static CXCursor * newCursor(lua_State *L) { 35 | CXCursor *c = (CXCursor*) lua_newuserdata(L, sizeof(CXCursor)); 36 | luaL_getmetatable(L, LCM_CURSOR); 37 | lua_setmetatable(L, -2); 38 | return c; 39 | } 40 | 41 | static CXCursor toCursor(lua_State *L, int n) { 42 | CXCursor * c = (CXCursor*) luaL_checkudata(L, n, LCM_CURSOR); 43 | return *c; 44 | } 45 | 46 | static CXType * newType(lua_State *L) { 47 | CXType *t = (CXType*) lua_newuserdata(L, sizeof(CXType)); 48 | luaL_getmetatable(L, LCM_TYPE); 49 | lua_setmetatable(L, -2); 50 | return t; 51 | } 52 | 53 | static CXType toType(lua_State *L, int n) { 54 | CXType *t = (CXType*) luaL_checkudata(L, n, LCM_TYPE); 55 | return *t; 56 | } 57 | 58 | static void pushCXString(lua_State *L, CXString s) { 59 | if (s.data == NULL) 60 | lua_pushnil(L); 61 | else { 62 | lua_pushstring(L, clang_getCString(s)); 63 | clang_disposeString(s); 64 | } 65 | } 66 | 67 | static void pushDiagnostic(lua_State *L, CXDiagnostic diag) { 68 | lua_createtable(L, 0, 0); 69 | 70 | pushCXString(L, clang_getDiagnosticCategoryText(diag)); 71 | lua_setfield(L, -2, "category"); 72 | 73 | pushCXString(L, clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions())); 74 | lua_setfield(L, -2, "text"); 75 | 76 | clang_disposeDiagnostic(diag); 77 | } 78 | 79 | /****** CLANG ******/ 80 | 81 | static int l_createIndex(lua_State *L) { 82 | int excludePch = lua_toboolean(L, 1); 83 | int diagnostics = lua_toboolean(L, 2); 84 | CXIndex *idx = newIndex(L); 85 | *idx = clang_createIndex(excludePch, diagnostics); 86 | return 1; 87 | } 88 | 89 | static luaL_Reg clang_functions[] = { 90 | {"createIndex", l_createIndex}, 91 | {NULL, NULL} 92 | }; 93 | 94 | /****** INDEX ******/ 95 | 96 | static int l_loadTU(lua_State *L) { 97 | CXIndex idx = toIndex(L, 1); 98 | const char *astFile = luaL_checkstring(L, 2); 99 | 100 | CXTranslationUnit *tu = newTU(L); 101 | *tu = clang_createTranslationUnit(idx, astFile); 102 | if (*tu == NULL) { 103 | lua_pushnil(L); 104 | lua_pushliteral(L, "Failed to open file: "); 105 | lua_pushvalue(L, 2); 106 | lua_concat(L, 2); 107 | return 2; 108 | } 109 | 110 | return 1; 111 | } 112 | 113 | static int l_parseTU(lua_State *L) { 114 | CXIndex idx = toIndex(L, 1); 115 | int tabIndex; 116 | const char *sourceFile; 117 | 118 | if (lua_type(L, 2) == LUA_TTABLE) { 119 | sourceFile = NULL; 120 | tabIndex = 2; 121 | } else { 122 | sourceFile = luaL_checkstring(L, 2); 123 | luaL_checktype(L, 3, LUA_TTABLE); 124 | tabIndex = 3; 125 | } 126 | 127 | int nArgs = lua_objlen(L, tabIndex); 128 | lua_checkstack(L, nArgs); 129 | char const ** args = new char const *[nArgs]; 130 | for (int i=0; iNumResults : 0; 236 | // clang_sortCodeCompletionResults(results, nResults); 237 | lua_createtable(L, nResults, 0); 238 | for (int i=0; results && iResults[i]; 240 | CXCompletionString com = res.CompletionString; 241 | 242 | lua_createtable(L, 0, 2); 243 | 244 | lua_pushinteger(L, clang_getCompletionPriority(com)); 245 | lua_setfield(L, -2, "priority"); 246 | 247 | int nChunks = clang_getNumCompletionChunks(com); 248 | if (nChunks > 0) { 249 | lua_createtable(L, nChunks, 0); 250 | for (int j=0; j 0) { 264 | lua_createtable(L, nAnnot, 0); 265 | for (int j=0; jkind == CXType_Invalid) 516 | lua_pushnil(L); 517 | return 1; 518 | } 519 | 520 | static int l_access(lua_State *L) { 521 | CXCursor cur = toCursor(L, 1); 522 | CX_CXXAccessSpecifier spec = clang_getCXXAccessSpecifier(cur); 523 | switch (spec) { 524 | case CX_CXXInvalidAccessSpecifier: lua_pushnil(L); break; 525 | case CX_CXXPublic: lua_pushliteral(L, "public"); break; 526 | case CX_CXXProtected: lua_pushliteral(L, "protected"); break; 527 | case CX_CXXPrivate: lua_pushliteral(L, "private"); break; 528 | } 529 | return 1; 530 | } 531 | 532 | static int l_location(lua_State *L) { 533 | CXCursor cur = toCursor(L, 1); 534 | CXSourceRange range = clang_getCursorExtent(cur); 535 | 536 | CXFile file; 537 | unsigned int line, col; 538 | 539 | CXSourceLocation loc = clang_getRangeStart(range); 540 | clang_getSpellingLocation(loc, &file, &line, &col, NULL); 541 | CXString fileName = clang_getFileName(file); 542 | lua_pushstring(L, clang_getCString(fileName)); 543 | clang_disposeString(fileName); 544 | lua_pushinteger(L, line); 545 | lua_pushinteger(L, col); 546 | 547 | loc = clang_getRangeEnd(range); 548 | clang_getSpellingLocation(loc, &file, &line, &col, NULL); 549 | lua_pushinteger(L, line); 550 | lua_pushinteger(L, col); 551 | return 5; 552 | } 553 | 554 | static int l_usr(lua_State *L) { 555 | CXCursor cur = toCursor(L, 1); 556 | CXString str = clang_getCursorUSR(cur); 557 | lua_pushstring(L, clang_getCString(str)); 558 | clang_disposeString(str); 559 | return 1; 560 | } 561 | 562 | static int l_referenced(lua_State *L) { 563 | CXCursor cur = toCursor(L, 1); 564 | CXCursor *res = newCursor(L); 565 | *res = clang_getCursorReferenced(cur); 566 | if (clang_Cursor_isNull(*res)) 567 | lua_pushnil(L); 568 | return 1; 569 | } 570 | 571 | static int l_definition(lua_State *L) { 572 | CXCursor cur = toCursor(L, 1); 573 | CXCursor *res = newCursor(L); 574 | *res = clang_getCursorDefinition(cur); 575 | if (clang_Cursor_isNull(*res)) 576 | lua_pushnil(L); 577 | return 1; 578 | } 579 | 580 | static int l_isStatic(lua_State *L) { 581 | CXCursor cur = toCursor(L, 1); 582 | lua_pushboolean(L, clang_CXXMethod_isStatic(cur)); 583 | return 1; 584 | } 585 | 586 | static int l_isVirtual(lua_State *L) { 587 | CXCursor cur = toCursor(L, 1); 588 | lua_pushboolean(L, clang_CXXMethod_isVirtual(cur)); 589 | return 1; 590 | } 591 | 592 | static int l_resultType(lua_State *L) { 593 | CXCursor cur = toCursor(L, 1); 594 | CXType *type = newType(L); 595 | *type = clang_getCursorResultType(cur); 596 | if (type->kind == CXType_Invalid) 597 | lua_pushnil(L); 598 | return 1; 599 | } 600 | 601 | static int l_cursorEqual(lua_State *L) { 602 | CXCursor cur1 = toCursor(L, 1); 603 | CXCursor cur2 = toCursor(L, 2); 604 | lua_pushboolean(L, clang_equalCursors(cur1, cur2)); 605 | return 1; 606 | } 607 | 608 | static luaL_Reg cursor_functions[] = { 609 | {"children", l_children}, 610 | {"kind", l_kind}, 611 | {"name", l_name}, 612 | {"__tostring", l_name}, 613 | {"displayName", l_displayName}, 614 | {"parent", l_parent}, 615 | {"arguments", l_arguments}, 616 | {"type", l_type}, 617 | {"access", l_access}, 618 | {"location", l_location}, 619 | {"usr", l_usr}, 620 | {"referenced", l_referenced}, 621 | {"definition", l_definition}, 622 | {"isStatic", l_isStatic}, 623 | {"isVirtual", l_isVirtual}, 624 | {"resultType", l_resultType}, 625 | {"__eq", l_cursorEqual}, 626 | {NULL, NULL} 627 | }; 628 | 629 | /******** TYPE ********/ 630 | 631 | static int l_typeToString(lua_State *L) { 632 | CXType type = toType(L, 1); 633 | CXTypeKind kind = type.kind; 634 | CXString str = clang_getTypeKindSpelling(kind); 635 | lua_pushstring(L, clang_getCString(str)); 636 | clang_disposeString(str); 637 | return 1; 638 | } 639 | 640 | static int l_canonical(lua_State *L) { 641 | CXType type = toType(L, 1); 642 | CXType *can = newType(L); 643 | if (type.kind == CXType_Pointer) { 644 | *can = clang_getPointeeType(type); 645 | } else 646 | *can = clang_getCanonicalType(type); 647 | if (can->kind == CXType_Invalid) 648 | lua_pushnil(L); 649 | return 1; 650 | } 651 | 652 | static int l_isPod(lua_State *L) { 653 | CXType type = toType(L, 1); 654 | lua_pushboolean(L, clang_isPODType(type)); 655 | return 1; 656 | } 657 | 658 | static int l_typeEq(lua_State *L) { 659 | CXType type = toType(L, 1); 660 | CXType type2 = toType(L, 2); 661 | lua_pushboolean(L, clang_equalTypes(type, type2)); 662 | return 1; 663 | } 664 | 665 | static int l_declaration(lua_State *L) { 666 | CXType type = toType(L, 1); 667 | CXCursor *cur = newCursor(L); 668 | *cur = clang_getTypeDeclaration(type); 669 | if (clang_Cursor_isNull(*cur)) 670 | lua_pushnil(L); 671 | return 1; 672 | } 673 | 674 | static int l_pointee(lua_State *L) { 675 | CXType type = toType(L, 1); 676 | CXType *res = newType(L); 677 | *res = clang_getPointeeType(type); 678 | if (res->kind == CXType_Invalid) 679 | lua_pushnil(L); 680 | return 1; 681 | } 682 | 683 | static int l_isConst(lua_State *L) { 684 | CXType type = toType(L, 1); 685 | int isConst = clang_isConstQualifiedType(type); 686 | lua_pushboolean(L, isConst); 687 | return 1; 688 | } 689 | 690 | static luaL_Reg type_functions[] = { 691 | {"__tostring", l_typeToString}, 692 | {"name", l_typeToString}, 693 | {"canonical", l_canonical}, 694 | {"pointee", l_pointee}, 695 | {"isPod", l_isPod}, 696 | {"isConst", l_isConst}, 697 | {"declaration", l_declaration}, 698 | {"__eq", l_typeEq}, 699 | {NULL, NULL} 700 | }; 701 | 702 | /******** API ********/ 703 | 704 | void newMetatable(lua_State *L, const char * name, luaL_Reg *reg) { 705 | luaL_newmetatable(L, name); 706 | luaL_register(L, NULL, reg); 707 | lua_pushvalue(L, -1); 708 | lua_setfield(L, -2, "__index"); 709 | } 710 | 711 | extern "C" int luaopen_parser(lua_State *L) { 712 | newMetatable(L, LCM_INDEX, index_functions); 713 | newMetatable(L, LCM_TU, tu_functions); 714 | newMetatable(L, LCM_CURSOR, cursor_functions); 715 | newMetatable(L, LCM_TYPE, type_functions); 716 | 717 | lua_newtable(L); 718 | luaL_register(L, NULL, clang_functions); 719 | return 1; 720 | } 721 | --------------------------------------------------------------------------------