cindex.py revision aa22984a82121b55841715123f7314bb850cb664
1# -*- coding: utf-8 -*- 2 3from ctypes import * 4 5def get_cindex_library(): 6 # FIXME: It's probably not the case that the library is actually found in 7 # this location. We need a better system of identifying and loading the 8 # CIndex library. It could be on path or elsewhere, or versioned, etc. 9 import platform 10 name = platform.system() 11 if name == 'Darwin': 12 return cdll.LoadLibrary('libCIndex.dylib') 13 elif name == 'Windows': 14 return cdll.LoadLibrary('libCIndex.dll') 15 else: 16 return cdll.LoadLibrary('libCIndex.so') 17 18## Utility Types and Functions ## 19def alloc_string_vector(strs): 20 """ 21 Allocate a string buffer large enough to accommodate the given list of 22 python strings. 23 """ 24 n = 0 25 for i in strs: n += len(i) + 1 26 return create_string_buffer(n) 27 28def copy_string_vector(vec, strs): 29 """ 30 Copy the contents of each string into the vector, preserving null 31 terminated elements. 32 """ 33 n = 0 34 for i in strs: 35 # This is terribly inefficient, but I can't figure out how to copy a 36 # chunk of characters into the resultant vector. t should be: something 37 # like this: vec[n:n + len(i)] = i[:]; n += len(i) + 1 38 for j in i: 39 vec[n] = j 40 n += 1 41 n += 1 42 43def create_string_vector(strs): 44 """ 45 Create a string vector (char *[]) from the given list of strings. 46 """ 47 vec = alloc_string_vector(strs) 48 copy_string_vector(vec, strs) 49 return vec 50 51# Aliases for convenience 52c_int_p = POINTER(c_int) 53c_uint_p = POINTER(c_uint) 54c_bool = c_uint 55 56# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper 57# object. This is a problem, because it means that from_parameter will see an 58# integer and pass the wrong value on platforms where int != void*. Work around 59# this by marshalling object arguments as void**. 60c_object_p = POINTER(c_void_p) 61 62lib = get_cindex_library() 63 64## Typedefs ## 65CursorKind = c_int 66 67### Structures and Utility Classes ### 68 69class String(Structure): 70 """ 71 The String class is a simple wrapper around constant string data returned 72 from functions in the CIndex library. 73 74 String objects do not provide any of the operations that Python strings 75 support. However, these objects can be explicitly cast using the str() 76 function. 77 """ 78 _fields_ = [("spelling", c_char_p), ("free", c_int)] 79 80 def __del__(self): 81 if self.free: 82 String_dispose(self) 83 84 def __str__(self): 85 return self.spelling 86 87class SourceLocation(Structure): 88 """ 89 A SourceLocation Represents a particular location within a source file. 90 """ 91 _fields_ = [("ptr_data", c_void_p), ("int_data", c_uint)] 92 93 def init(self): 94 """ 95 Initialize the source location, setting its file, line and column. 96 """ 97 f, l, c = c_void_p(), c_uint(), c_uint() 98 SourceLocation_loc(self, byref(f), byref(l), byref(c)) 99 self.file, self.line, self.column = File(f), l, c 100 return self 101 102class SourceRange(Structure): 103 """ 104 A SourceRange describes a range of source locations within the source 105 code. 106 """ 107 _fields_ = [ 108 ("ptr_data", c_void_p), 109 ("begin_int_data", c_uint), 110 ("end_int_data", c_uint)] 111 112 def start(self): 113 """ 114 Return a SourceLocation representing the first character within a 115 source range. 116 """ 117 return SourceRange_start(self).init() 118 119 def end(self): 120 """ 121 Return a SourceLocation representing the last character within a 122 source range. 123 """ 124 return SourceRange_end(self).init() 125 126class Cursor(Structure): 127 """ 128 The Cursor class represents a reference to an element within the AST. It 129 acts as a kind of iterator. 130 """ 131 _fields_ = [("kind", c_int), ("data", c_void_p * 3)] 132 133 def __eq__(self, other): 134 return Cursor_eq(self, other) 135 136 def __ne__(self, other): 137 return not Cursor_eq(self, other) 138 139 @staticmethod 140 def null(): 141 """Return the null cursor object.""" 142 return Cursor_null() 143 144 def is_declaration(self): 145 """Return True if the cursor points to a declaration.""" 146 return Cursor_is_decl(self.kind) 147 148 def is_reference(self): 149 """Return True if the cursor points to a refernce.""" 150 return Cursor_is_ref(self.kind) 151 152 def is_expression(self): 153 """Return True if the cursor points to an expression.""" 154 return Cursor_is_expr(self.kind) 155 156 def is_statement(self): 157 """Return True if the cursor points to a statement.""" 158 return Cursor_is_stmt(self.kind) 159 160 def is_translation_unit(self): 161 """Return True if the cursor points to a translation unit.""" 162 return Cursor_is_tu(self.kind) 163 164 def is_invalid(self): 165 """Return True if the cursor points to an invalid entity.""" 166 return Cursor_is_inv(self.kind) 167 168 def is_definition(self): 169 """ 170 Returns true if the declaration pointed at by the cursor is also a 171 definition of that entity. 172 """ 173 return Cursor_is_def(self) 174 175 def get_declaration(self): 176 """ 177 Return the underlying declaration for the cursor. If the cursor kind 178 is a declaration, then this simpy returns the declaration. If the 179 cursor is a reference, then this returns the referenced declaration. 180 """ 181 if not self.is_declaration(): 182 raise Exception("Cursor does not refer to a Declaration") 183 return Cursor_decl(self) 184 185 def get_definition(self): 186 """ 187 If the cursor is a reference to a declaration or a declaration of 188 some entity, return a cursor that points to the definition of that 189 entity. 190 """ 191 # TODO: Should probably check that this is either a reference or 192 # declaration prior to issuing the lookup. 193 return Cursor_def(self) 194 195 @property 196 def spelling(self): 197 """Return the spelling of the entity pointed at by the cursor.""" 198 if not self.is_declaration(): 199 # FIXME: This should be documented in Index.h 200 raise ValueError("Cursor does not refer to a Declaration") 201 return Cursor_spelling(self) 202 203 @property 204 def location(self): 205 """ 206 Return the source location (the starting character) of the entity 207 pointed at by the cursor. 208 """ 209 return Cursor_loc(self).init() 210 211 @property 212 def extent(self): 213 """ 214 Return the source range (the range of text) occupied by the entity 215 pointed at by the cursor. 216 """ 217 return Cursor_extent(self) 218 219 @property 220 def file(self): 221 """ 222 Return the file containing the pointed-at entity. This is an alias for 223 location.file. 224 """ 225 return self.location.file 226 227# FIXME: Implement this class. 228class Entity(Structure): 229 """ 230 An Entity is a uniqe token for accessing "visible" declarations within 231 a translation unit. 232 """ 233 # NOTE: Index is written here as a void*, but given in the API as CXIndex. 234 # Be careful to translate back to Index when returning this member. 235 # TODO: Rename as _index and write a property? 236 _fields_ = [("index", c_void_p), ("data", c_void_p)] 237 238## CIndex Objects ## 239 240# CIndex objects (derived from ClangObject) are essentially lightweight 241# wrappers attached to some underlying object, which is exposed via CIndex as 242# a void*. 243 244class ClangObject(object): 245 """ 246 A helper for Clang objects. This class helps act as an intermediary for 247 the ctypes library and the Clang CIndex library. 248 """ 249 def __init__(self, obj): 250 assert isinstance(obj, c_object_p) and obj 251 self.obj = self._as_parameter_ = obj 252 253 def from_param(self): 254 return self._as_parameter_ 255 256class Index(ClangObject): 257 """ 258 The Index type provides the primary interface to the Clang CIndex library, 259 primarily by providing an interface for reading and parsing translation 260 units. 261 """ 262 def __init__(self, obj): 263 ClangObject.__init__(self, obj) 264 265 @staticmethod 266 def create(excludeDecls=False, displayDiags=False): 267 """ 268 Create a new Index. 269 Parameters: 270 excludeDecls -- Exclude local declarations from translation units. 271 displayDiags -- Display diagnostics during translation unit creation. 272 """ 273 return Index(Index_create(excludeDecls, displayDiags)) 274 275 def __del__(self): 276 Index_dispose(self) 277 278 def read(self, path): 279 """Load the translation unit from the given AST file.""" 280 return TranslationUnit.read(self, path) 281 282 def parse(self, path, args = []): 283 """ 284 Load the translation unit from the given source code file by running 285 clang and generating the AST before loading. Additional command line 286 parameters can be passed to clang via the args parameter. 287 """ 288 return TranslationUnit.parse(self, path, args) 289 290 291class TranslationUnit(ClangObject): 292 """ 293 The TranslationUnit class represents a source code translation unit and 294 provides read-only access to its top-level declarations. 295 """ 296 def __init__(self, obj, free=False): 297 ClangObject.__init__(self, obj) 298 self.free = free 299 300 def __del__(self): 301 if self.free and self.obj: 302 TranslationUnit_dispose(self) 303 304 def load(self, fun, data = None): 305 # Actually call this over a lambda that attaches an object the 306 # underlying void pointer. 307 f = lambda t, c, x: fun(TranslationUnit(t), c, x) 308 TranslationUnit_load(self.obj, Callback(f), data) 309 310 @property 311 def spelling(self): 312 return TranslationUnit_spelling(self) 313 314 @staticmethod 315 def read(ix, path): 316 """Create a translation unit from the given AST file.""" 317 ptr = TranslationUnit_read(ix, path) 318 return TranslationUnit(ptr, True) if ptr else None 319 320 @staticmethod 321 def parse(ix, path, args = []): 322 """ 323 Construct a translation unit from the given source file, applying 324 the given command line argument. 325 """ 326 # TODO: Support unsaved files. 327 argc, argv = len(args), create_string_vector(args) 328 ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0) 329 return TranslationUnit(ptr, True) if ptr else None 330 331class File(ClangObject): 332 """ 333 The File class... 334 """ 335 def __init__(self, obj): 336 ClangObject.__init__(self, obj) 337 338 @property 339 def is_valid(self): 340 return self.obj is not None 341 342 @property 343 def name(self): 344 """Return the name of the file, if valid. Otherwise, an empty string.""" 345 return File_name(self) if self.obj else "" 346 347 @property 348 def time(self): 349 """Return the time of the file, if valid. Otherwise, -1.""" 350 return File_time(self) if self.obj else -1 351 352class Declaration(ClangObject): 353 """ 354 The Declaration class represents a declaration with a translation unit. 355 """ 356 def __init__(self, obj): 357 ClangObject.__init__(self, obj) 358 359 # Figure out the kind of cursor and inject a base class that provides 360 # some declaration-specific functionality. 361 self.cursor = Declaration_cursor(self) 362 363 @property 364 def kind(self): 365 """Retur the kind of cursor.""" 366 return self.cursor.kind 367 368 @property 369 def entity(self): 370 """Return an entity that represents this declaration.""" 371 return Entity(Declaration_entity(self)) 372 373 @property 374 def spelling(self): 375 """Return the spelling (name) of the declaration.""" 376 return Declaration_spelling(self) 377 378 def load(self, fun, data = None): 379 """ 380 Recursively visit any elements declared or referenced within this 381 declaration. 382 """ 383 f = lambda d, c, x: fun(Declaration(d), c, x) 384 Declaration_load(self, Callback(f), data) 385 386# Specific declaration kinds 387class ClassDeclaration: 388 pass 389 390class FunctionDeclaration: 391 pass 392 393class TypedefDeclaration: 394 pass 395 396# Additional Functions and Types 397 398# Wrap calls to TranslationUnit._load and Decl._load. 399Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p) 400 401# String Functions 402String_dispose = lib.clang_disposeString 403String_dispose.argtypes = [String] 404 405# Source Location Functions 406SourceLocation_loc = lib.clang_getInstantiationLocation 407SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_void_p), c_uint_p, c_uint_p] 408 409# Source Range Functions 410SourceRange_start = lib.clang_getRangeStart 411SourceRange_start.argtypes = [SourceLocation] 412SourceRange_start.restype = SourceRange 413 414SourceRange_end = lib.clang_getRangeEnd 415SourceRange_end.argtypes = [SourceLocation] 416SourceRange_end.restype = SourceRange 417 418# Cursor Functions 419# TODO: Implement this function 420Cursor_get = lib.clang_getCursor 421Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint] 422Cursor.restype = Cursor 423 424Cursor_null = lib.clang_getNullCursor 425Cursor_null.restype = Cursor 426 427Cursor_kind = lib.clang_getCursorKind 428Cursor_kind.argtypes = [Cursor] 429Cursor_kind.res = c_int 430 431# FIXME: Not really sure what a USR is or what this function actually does... 432Cursor_usr = lib.clang_getCursorUSR 433 434Cursor_is_decl = lib.clang_isDeclaration 435Cursor_is_decl.argtypes = [CursorKind] 436Cursor_is_decl.restype = c_bool 437 438Cursor_is_ref = lib.clang_isReference 439Cursor_is_ref.argtypes = [CursorKind] 440Cursor_is_ref.restype = c_bool 441 442Cursor_is_expr = lib.clang_isExpression 443Cursor_is_expr.argtypes = [CursorKind] 444Cursor_is_expr.restype = c_bool 445 446Cursor_is_stmt = lib.clang_isStatement 447Cursor_is_stmt.argtypes = [CursorKind] 448Cursor_is_stmt.restype = c_bool 449 450Cursor_is_inv = lib.clang_isInvalid 451Cursor_is_inv.argtypes = [CursorKind] 452Cursor_is_inv.restype = c_bool 453 454Cursor_is_tu = lib.clang_isTranslationUnit 455Cursor_is_tu.argtypes = [CursorKind] 456Cursor_is_tu.restype = c_bool 457 458Cursor_is_def = lib.clang_isCursorDefinition 459Cursor_is_def.argtypes = [Cursor] 460Cursor_is_def.restype = c_bool 461 462Cursor_def = lib.clang_getCursorDefinition 463Cursor_def.argtypes = [Cursor] 464Cursor_def.restype = Cursor 465 466Cursor_eq = lib.clang_equalCursors 467Cursor_eq.argtypes = [Cursor, Cursor] 468Cursor_eq.restype = c_uint 469 470Cursor_spelling = lib.clang_getCursorSpelling 471Cursor_spelling.argtypes = [Cursor] 472Cursor_spelling.restype = String 473 474Cursor_loc = lib.clang_getCursorLocation 475Cursor_loc.argtypes = [Cursor] 476Cursor_loc.restype = SourceLocation 477 478Cursor_extent = lib.clang_getCursorExtent 479Cursor_extent.argtypes = [Cursor] 480Cursor_extent.restype = SourceRange 481 482Cursor_ref = lib.clang_getCursorReferenced 483Cursor_ref.argtypes = [Cursor] 484Cursor_ref.restype = Cursor 485 486# Index Functions 487Index_create = lib.clang_createIndex 488Index_create.argtypes = [c_int, c_int] 489Index_create.restype = c_object_p 490 491Index_dispose = lib.clang_disposeIndex 492Index_dispose.argtypes = [Index] 493 494# Translation Unit Functions 495TranslationUnit_read = lib.clang_createTranslationUnit 496TranslationUnit_read.argtypes = [Index, c_char_p] 497TranslationUnit_read.restype = c_object_p 498 499TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile 500TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p, 501 c_int, c_void_p] 502TranslationUnit_parse.restype = c_object_p 503 504TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling 505TranslationUnit_spelling.argtypes = [TranslationUnit] 506TranslationUnit_spelling.restype = String 507 508TranslationUnit_dispose = lib.clang_disposeTranslationUnit 509TranslationUnit_dispose.argtypes = [TranslationUnit] 510 511# File Functions 512File_name = lib.clang_getFileName 513File_name.argtypes = [File] 514File_name.restype = c_char_p 515 516File_time = lib.clang_getFileTime 517File_time.argtypes = [File] 518File_time.restype = c_uint 519