cindex.py revision 1b945a7455e17fd792ef3bd3790dc88beea5faad
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 def __init__(self, obj): 270 ClangObject.__init__(self, obj) 271 272 @staticmethod 273 def create(excludeDecls=False, displayDiags=False): 274 """ 275 Create a new Index. 276 Parameters: 277 excludeDecls -- Exclude local declarations from translation units. 278 displayDiags -- Display diagnostics during translation unit creation. 279 """ 280 return Index(Index_create(excludeDecls, displayDiags)) 281 282 def __del__(self): 283 Index_dispose(self) 284 285 def read(self, path): 286 """Load the translation unit from the given AST file.""" 287 return TranslationUnit.read(self, path) 288 289 def parse(self, path, args = []): 290 """ 291 Load the translation unit from the given source code file by running 292 clang and generating the AST before loading. Additional command line 293 parameters can be passed to clang via the args parameter. 294 """ 295 return TranslationUnit.parse(self, path, args) 296 297 298class TranslationUnit(ClangObject): 299 """ 300 The TranslationUnit class represents a source code translation unit and 301 provides read-only access to its top-level declarations. 302 """ 303 def __init__(self, obj, free=False): 304 ClangObject.__init__(self, obj) 305 self.free = free 306 307 def __del__(self): 308 if self.free and self.obj: 309 TranslationUnit_dispose(self) 310 311 @property 312 def cursor(self): 313 """Retrieve the cursor that represents the given translation unit.""" 314 return TranslationUnit_cursor(self) 315 316 @property 317 def spelling(self): 318 """Get the original translation unit source file name.""" 319 return TranslationUnit_spelling(self) 320 321 @staticmethod 322 def read(ix, path): 323 """Create a translation unit from the given AST file.""" 324 ptr = TranslationUnit_read(ix, path) 325 return TranslationUnit(ptr, True) if ptr else None 326 327 @staticmethod 328 def parse(ix, path, args = []): 329 """ 330 Construct a translation unit from the given source file, applying 331 the given command line argument. 332 """ 333 # TODO: Support unsaved files. 334 argc, argv = len(args), create_string_vector(args) 335 ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0) 336 return TranslationUnit(ptr, True) if ptr else None 337 338class File(ClangObject): 339 """ 340 The File class represents a particular source file that is part of a 341 translation unit. 342 """ 343 344 @property 345 def name(self): 346 """Return the complete file and path name of the file, if valid.""" 347 return File_name(self) 348 349 @property 350 def time(self): 351 """Return the last modification time of the file, if valid.""" 352 return File_time(self) 353 354class Declaration(ClangObject): 355 """ 356 The Declaration class represents a declaration with a translation unit. 357 """ 358 def __init__(self, obj): 359 ClangObject.__init__(self, obj) 360 361 # Figure out the kind of cursor and inject a base class that provides 362 # some declaration-specific functionality. 363 self.cursor = Declaration_cursor(self) 364 365 @property 366 def kind(self): 367 """Retur the kind of cursor.""" 368 return self.cursor.kind 369 370 @property 371 def entity(self): 372 """Return an entity that represents this declaration.""" 373 return Entity(Declaration_entity(self)) 374 375 @property 376 def spelling(self): 377 """Return the spelling (name) of the declaration.""" 378 return Declaration_spelling(self) 379 380 def load(self, fun, data = None): 381 """ 382 Recursively visit any elements declared or referenced within this 383 declaration. 384 """ 385 f = lambda d, c, x: fun(Declaration(d), c, x) 386 Declaration_load(self, Callback(f), data) 387 388# Specific declaration kinds 389class ClassDeclaration: 390 pass 391 392class FunctionDeclaration: 393 pass 394 395class TypedefDeclaration: 396 pass 397 398# Additional Functions and Types 399 400# Wrap calls to TranslationUnit._load and Decl._load. 401Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p) 402 403# String Functions 404String_dispose = lib.clang_disposeString 405String_dispose.argtypes = [String] 406 407# Source Location Functions 408SourceLocation_loc = lib.clang_getInstantiationLocation 409SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p, 410 c_uint_p] 411 412# Source Range Functions 413SourceRange_start = lib.clang_getRangeStart 414SourceRange_start.argtypes = [SourceRange] 415SourceRange_start.restype = SourceLocation 416 417SourceRange_end = lib.clang_getRangeEnd 418SourceRange_end.argtypes = [SourceRange] 419SourceRange_end.restype = SourceLocation 420 421# Cursor Functions 422# TODO: Implement this function 423Cursor_get = lib.clang_getCursor 424Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint] 425Cursor.restype = Cursor 426 427Cursor_null = lib.clang_getNullCursor 428Cursor_null.restype = Cursor 429 430Cursor_kind = lib.clang_getCursorKind 431Cursor_kind.argtypes = [Cursor] 432Cursor_kind.res = c_int 433 434# FIXME: Not really sure what a USR is or what this function actually does... 435Cursor_usr = lib.clang_getCursorUSR 436 437Cursor_is_decl = lib.clang_isDeclaration 438Cursor_is_decl.argtypes = [CursorKind] 439Cursor_is_decl.restype = c_bool 440 441Cursor_is_ref = lib.clang_isReference 442Cursor_is_ref.argtypes = [CursorKind] 443Cursor_is_ref.restype = c_bool 444 445Cursor_is_expr = lib.clang_isExpression 446Cursor_is_expr.argtypes = [CursorKind] 447Cursor_is_expr.restype = c_bool 448 449Cursor_is_stmt = lib.clang_isStatement 450Cursor_is_stmt.argtypes = [CursorKind] 451Cursor_is_stmt.restype = c_bool 452 453Cursor_is_inv = lib.clang_isInvalid 454Cursor_is_inv.argtypes = [CursorKind] 455Cursor_is_inv.restype = c_bool 456 457Cursor_is_tu = lib.clang_isTranslationUnit 458Cursor_is_tu.argtypes = [CursorKind] 459Cursor_is_tu.restype = c_bool 460 461Cursor_is_def = lib.clang_isCursorDefinition 462Cursor_is_def.argtypes = [Cursor] 463Cursor_is_def.restype = c_bool 464 465Cursor_def = lib.clang_getCursorDefinition 466Cursor_def.argtypes = [Cursor] 467Cursor_def.restype = Cursor 468 469Cursor_eq = lib.clang_equalCursors 470Cursor_eq.argtypes = [Cursor, Cursor] 471Cursor_eq.restype = c_uint 472 473Cursor_spelling = lib.clang_getCursorSpelling 474Cursor_spelling.argtypes = [Cursor] 475Cursor_spelling.restype = String 476 477Cursor_loc = lib.clang_getCursorLocation 478Cursor_loc.argtypes = [Cursor] 479Cursor_loc.restype = SourceLocation 480 481Cursor_extent = lib.clang_getCursorExtent 482Cursor_extent.argtypes = [Cursor] 483Cursor_extent.restype = SourceRange 484 485Cursor_ref = lib.clang_getCursorReferenced 486Cursor_ref.argtypes = [Cursor] 487Cursor_ref.restype = Cursor 488 489# Index Functions 490Index_create = lib.clang_createIndex 491Index_create.argtypes = [c_int, c_int] 492Index_create.restype = c_object_p 493 494Index_dispose = lib.clang_disposeIndex 495Index_dispose.argtypes = [Index] 496 497# Translation Unit Functions 498TranslationUnit_read = lib.clang_createTranslationUnit 499TranslationUnit_read.argtypes = [Index, c_char_p] 500TranslationUnit_read.restype = c_object_p 501 502TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile 503TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p, 504 c_int, c_void_p] 505TranslationUnit_parse.restype = c_object_p 506 507TranslationUnit_cursor = lib.clang_getTranslationUnitCursor 508TranslationUnit_cursor.argtypes = [TranslationUnit] 509TranslationUnit_cursor.restype = Cursor 510 511TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling 512TranslationUnit_spelling.argtypes = [TranslationUnit] 513TranslationUnit_spelling.restype = String 514 515TranslationUnit_dispose = lib.clang_disposeTranslationUnit 516TranslationUnit_dispose.argtypes = [TranslationUnit] 517 518# File Functions 519File_name = lib.clang_getFileName 520File_name.argtypes = [File] 521File_name.restype = c_char_p 522 523File_time = lib.clang_getFileTime 524File_time.argtypes = [File] 525File_time.restype = c_uint 526