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