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