cindex.py revision 99d593ed41216ffc8e6ebb25fdcb5102c3866b99
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_object_p(), c_uint(), c_uint() 98 SourceLocation_loc(self, byref(f), byref(l), byref(c)) 99 f = File(f) if f else None 100 self.file, self.line, self.column = f, int(l.value), int(c.value) 101 return self 102 103 def __repr__(self): 104 return "<SourceLocation file %r, line %r, column %r>" % ( 105 self.file.name if self.file else None, self.line, self.column) 106 107class SourceRange(Structure): 108 """ 109 A SourceRange describes a range of source locations within the source 110 code. 111 """ 112 _fields_ = [ 113 ("ptr_data", c_void_p), 114 ("begin_int_data", c_uint), 115 ("end_int_data", c_uint)] 116 117 @property 118 def start(self): 119 """ 120 Return a SourceLocation representing the first character within a 121 source range. 122 """ 123 return SourceRange_start(self).init() 124 125 @property 126 def end(self): 127 """ 128 Return a SourceLocation representing the last character within a 129 source range. 130 """ 131 return SourceRange_end(self).init() 132 133class Cursor(Structure): 134 """ 135 The Cursor class represents a reference to an element within the AST. It 136 acts as a kind of iterator. 137 """ 138 _fields_ = [("kind", c_int), ("data", c_void_p * 3)] 139 140 def __eq__(self, other): 141 return Cursor_eq(self, other) 142 143 def __ne__(self, other): 144 return not Cursor_eq(self, other) 145 146 @staticmethod 147 def null(): 148 """Return the null cursor object.""" 149 return Cursor_null() 150 151 def is_declaration(self): 152 """Return True if the cursor points to a declaration.""" 153 return Cursor_is_decl(self.kind) 154 155 def is_reference(self): 156 """Return True if the cursor points to a refernce.""" 157 return Cursor_is_ref(self.kind) 158 159 def is_expression(self): 160 """Return True if the cursor points to an expression.""" 161 return Cursor_is_expr(self.kind) 162 163 def is_statement(self): 164 """Return True if the cursor points to a statement.""" 165 return Cursor_is_stmt(self.kind) 166 167 def is_translation_unit(self): 168 """Return True if the cursor points to a translation unit.""" 169 return Cursor_is_tu(self.kind) 170 171 def is_invalid(self): 172 """Return True if the cursor points to an invalid entity.""" 173 return Cursor_is_inv(self.kind) 174 175 def is_definition(self): 176 """ 177 Returns true if the declaration pointed at by the cursor is also a 178 definition of that entity. 179 """ 180 return Cursor_is_def(self) 181 182 def get_declaration(self): 183 """ 184 Return the underlying declaration for the cursor. If the cursor kind 185 is a declaration, then this simpy returns the declaration. If the 186 cursor is a reference, then this returns the referenced declaration. 187 """ 188 if not self.is_declaration(): 189 raise Exception("Cursor does not refer to a Declaration") 190 return Cursor_decl(self) 191 192 def get_definition(self): 193 """ 194 If the cursor is a reference to a declaration or a declaration of 195 some entity, return a cursor that points to the definition of that 196 entity. 197 """ 198 # TODO: Should probably check that this is either a reference or 199 # declaration prior to issuing the lookup. 200 return Cursor_def(self) 201 202 @property 203 def spelling(self): 204 """Return the spelling of the entity pointed at by the cursor.""" 205 if not self.is_declaration(): 206 # FIXME: This should be documented in Index.h 207 raise ValueError("Cursor does not refer to a Declaration") 208 return Cursor_spelling(self) 209 210 @property 211 def location(self): 212 """ 213 Return the source location (the starting character) of the entity 214 pointed at by the cursor. 215 """ 216 return Cursor_loc(self).init() 217 218 @property 219 def extent(self): 220 """ 221 Return the source range (the range of text) occupied by the entity 222 pointed at by the cursor. 223 """ 224 return Cursor_extent(self) 225 226 @property 227 def file(self): 228 """ 229 Return the file containing the pointed-at entity. This is an alias for 230 location.file. 231 """ 232 return self.location.file 233 234# FIXME: Implement this class. 235class Entity(Structure): 236 """ 237 An Entity is a uniqe token for accessing "visible" declarations within 238 a translation unit. 239 """ 240 # NOTE: Index is written here as a void*, but given in the API as CXIndex. 241 # Be careful to translate back to Index when returning this member. 242 # TODO: Rename as _index and write a property? 243 _fields_ = [("index", c_void_p), ("data", c_void_p)] 244 245## CIndex Objects ## 246 247# CIndex objects (derived from ClangObject) are essentially lightweight 248# wrappers attached to some underlying object, which is exposed via CIndex as 249# a void*. 250 251class ClangObject(object): 252 """ 253 A helper for Clang objects. This class helps act as an intermediary for 254 the ctypes library and the Clang CIndex library. 255 """ 256 def __init__(self, obj): 257 assert isinstance(obj, c_object_p) and obj 258 self.obj = self._as_parameter_ = obj 259 260 def from_param(self): 261 return self._as_parameter_ 262 263class Index(ClangObject): 264 """ 265 The Index type provides the primary interface to the Clang CIndex library, 266 primarily by providing an interface for reading and parsing translation 267 units. 268 """ 269 270 @staticmethod 271 def create(excludeDecls=False, displayDiags=False): 272 """ 273 Create a new Index. 274 Parameters: 275 excludeDecls -- Exclude local declarations from translation units. 276 displayDiags -- Display diagnostics during translation unit creation. 277 """ 278 return Index(Index_create(excludeDecls, displayDiags)) 279 280 def __del__(self): 281 Index_dispose(self) 282 283 def read(self, path): 284 """Load the translation unit from the given AST file.""" 285 return TranslationUnit.read(self, path) 286 287 def parse(self, path, args = []): 288 """ 289 Load the translation unit from the given source code file by running 290 clang and generating the AST before loading. Additional command line 291 parameters can be passed to clang via the args parameter. 292 """ 293 return TranslationUnit.parse(self, path, args) 294 295 296class TranslationUnit(ClangObject): 297 """ 298 The TranslationUnit class represents a source code translation unit and 299 provides read-only access to its top-level declarations. 300 """ 301 302 def __del__(self): 303 TranslationUnit_dispose(self) 304 305 @property 306 def cursor(self): 307 """Retrieve the cursor that represents the given translation unit.""" 308 return TranslationUnit_cursor(self) 309 310 @property 311 def spelling(self): 312 """Get the original translation unit source file name.""" 313 return TranslationUnit_spelling(self) 314 315 @staticmethod 316 def read(ix, path): 317 """Create a translation unit from the given AST file.""" 318 ptr = TranslationUnit_read(ix, path) 319 return TranslationUnit(ptr) if ptr else None 320 321 @staticmethod 322 def parse(ix, path, args = []): 323 """ 324 Construct a translation unit from the given source file, applying 325 the given command line argument. 326 """ 327 # TODO: Support unsaved files. 328 argc, argv = len(args), create_string_vector(args) 329 ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0) 330 return TranslationUnit(ptr) if ptr else None 331 332class File(ClangObject): 333 """ 334 The File class represents a particular source file that is part of a 335 translation unit. 336 """ 337 338 @property 339 def name(self): 340 """Return the complete file and path name of the file, if valid.""" 341 return File_name(self) 342 343 @property 344 def time(self): 345 """Return the last modification time of the file, if valid.""" 346 return File_time(self) 347 348class Declaration(ClangObject): 349 """ 350 The Declaration class represents a declaration with a translation unit. 351 """ 352 def __init__(self, obj): 353 ClangObject.__init__(self, obj) 354 355 # Figure out the kind of cursor and inject a base class that provides 356 # some declaration-specific functionality. 357 self.cursor = Declaration_cursor(self) 358 359 @property 360 def kind(self): 361 """Retur the kind of cursor.""" 362 return self.cursor.kind 363 364 @property 365 def entity(self): 366 """Return an entity that represents this declaration.""" 367 return Entity(Declaration_entity(self)) 368 369 @property 370 def spelling(self): 371 """Return the spelling (name) of the declaration.""" 372 return Declaration_spelling(self) 373 374 def load(self, fun, data = None): 375 """ 376 Recursively visit any elements declared or referenced within this 377 declaration. 378 """ 379 f = lambda d, c, x: fun(Declaration(d), c, x) 380 Declaration_load(self, Callback(f), data) 381 382# Specific declaration kinds 383class ClassDeclaration: 384 pass 385 386class FunctionDeclaration: 387 pass 388 389class TypedefDeclaration: 390 pass 391 392# Additional Functions and Types 393 394# Wrap calls to TranslationUnit._load and Decl._load. 395Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p) 396 397# String Functions 398String_dispose = lib.clang_disposeString 399String_dispose.argtypes = [String] 400 401# Source Location Functions 402SourceLocation_loc = lib.clang_getInstantiationLocation 403SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p, 404 c_uint_p] 405 406# Source Range Functions 407SourceRange_start = lib.clang_getRangeStart 408SourceRange_start.argtypes = [SourceRange] 409SourceRange_start.restype = SourceLocation 410 411SourceRange_end = lib.clang_getRangeEnd 412SourceRange_end.argtypes = [SourceRange] 413SourceRange_end.restype = SourceLocation 414 415# Cursor Functions 416# TODO: Implement this function 417Cursor_get = lib.clang_getCursor 418Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint] 419Cursor.restype = Cursor 420 421Cursor_null = lib.clang_getNullCursor 422Cursor_null.restype = Cursor 423 424Cursor_kind = lib.clang_getCursorKind 425Cursor_kind.argtypes = [Cursor] 426Cursor_kind.res = c_int 427 428# FIXME: Not really sure what a USR is or what this function actually does... 429Cursor_usr = lib.clang_getCursorUSR 430 431Cursor_is_decl = lib.clang_isDeclaration 432Cursor_is_decl.argtypes = [CursorKind] 433Cursor_is_decl.restype = c_bool 434 435Cursor_is_ref = lib.clang_isReference 436Cursor_is_ref.argtypes = [CursorKind] 437Cursor_is_ref.restype = c_bool 438 439Cursor_is_expr = lib.clang_isExpression 440Cursor_is_expr.argtypes = [CursorKind] 441Cursor_is_expr.restype = c_bool 442 443Cursor_is_stmt = lib.clang_isStatement 444Cursor_is_stmt.argtypes = [CursorKind] 445Cursor_is_stmt.restype = c_bool 446 447Cursor_is_inv = lib.clang_isInvalid 448Cursor_is_inv.argtypes = [CursorKind] 449Cursor_is_inv.restype = c_bool 450 451Cursor_is_tu = lib.clang_isTranslationUnit 452Cursor_is_tu.argtypes = [CursorKind] 453Cursor_is_tu.restype = c_bool 454 455Cursor_is_def = lib.clang_isCursorDefinition 456Cursor_is_def.argtypes = [Cursor] 457Cursor_is_def.restype = c_bool 458 459Cursor_def = lib.clang_getCursorDefinition 460Cursor_def.argtypes = [Cursor] 461Cursor_def.restype = Cursor 462 463Cursor_eq = lib.clang_equalCursors 464Cursor_eq.argtypes = [Cursor, Cursor] 465Cursor_eq.restype = c_uint 466 467Cursor_spelling = lib.clang_getCursorSpelling 468Cursor_spelling.argtypes = [Cursor] 469Cursor_spelling.restype = String 470 471Cursor_loc = lib.clang_getCursorLocation 472Cursor_loc.argtypes = [Cursor] 473Cursor_loc.restype = SourceLocation 474 475Cursor_extent = lib.clang_getCursorExtent 476Cursor_extent.argtypes = [Cursor] 477Cursor_extent.restype = SourceRange 478 479Cursor_ref = lib.clang_getCursorReferenced 480Cursor_ref.argtypes = [Cursor] 481Cursor_ref.restype = Cursor 482 483# Index Functions 484Index_create = lib.clang_createIndex 485Index_create.argtypes = [c_int, c_int] 486Index_create.restype = c_object_p 487 488Index_dispose = lib.clang_disposeIndex 489Index_dispose.argtypes = [Index] 490 491# Translation Unit Functions 492TranslationUnit_read = lib.clang_createTranslationUnit 493TranslationUnit_read.argtypes = [Index, c_char_p] 494TranslationUnit_read.restype = c_object_p 495 496TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile 497TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p, 498 c_int, c_void_p] 499TranslationUnit_parse.restype = c_object_p 500 501TranslationUnit_cursor = lib.clang_getTranslationUnitCursor 502TranslationUnit_cursor.argtypes = [TranslationUnit] 503TranslationUnit_cursor.restype = Cursor 504 505TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling 506TranslationUnit_spelling.argtypes = [TranslationUnit] 507TranslationUnit_spelling.restype = String 508 509TranslationUnit_dispose = lib.clang_disposeTranslationUnit 510TranslationUnit_dispose.argtypes = [TranslationUnit] 511 512# File Functions 513File_name = lib.clang_getFileName 514File_name.argtypes = [File] 515File_name.restype = c_char_p 516 517File_time = lib.clang_getFileTime 518File_time.argtypes = [File] 519File_time.restype = c_uint 520