Glud Documentation¶
Do you need to find something in a C++ file? Perhaps you’re generating bindings for C++ library, generating serialization code, collecting code-metrics or developing a style checker?
You can solve all of these problems using libclang, a library provided as part of Clang compiler. However, the challenge with libclang is that it’s easy to end up in a situation where the code you write can be difficult to write, compose and maintain.
Glud solves this problem by providing a higher level abstraction that makes it easier to find C++ constructs, inspired by libclangastmatchers.
Example¶
For example, to find all of the classes in C/C++ file:
import sys
import glud
matcher = glud.isClass()
translation_unit = glud.parse(sys.argv[1])
for cls in glud.walk(matcher, translation_unit.cursor):
print(cls.type.spelling)
Contents¶
Installation¶
This part of the documentation covers the installation of Glud.
Glud is a pure Python package, with minimal dependencies, but does expect that libclang (the native code library that exposes the stable bindings to the Clang compiler) is installed and can be dynamically loaded.
For Python 2 development, it’s also recommended that you install the Python bindings that match your libclang, rather than relying on versions provided by PyPI.
Prerequisites¶
On Ubuntu distributions, the easiest way to get an appropriate build of libclang you can install pre-built binaries from the LLVM apt repositories.
Ubuntu Trusty (14.04)¶
In your terminal, enter the following commands to install libclang:
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update -qq
sudo apt-get install -y python-clang-3.8 libclang1-3.8
You can then add libclang.so to your loader path, which makes the library discoverable.
export LD_LIBRARY_PATH=/usr/lib/llvm-3.8/lib
Quickstart¶
What is an Abstract Syntax Tree?¶
An Abstract Syntax Tree is the data structure that a compiler converts the source code of your program into, that is easier to work with than the bare source code. Python has it’s own ast module in the standard library.
To see the what Glud knows about an AST, you can parse source, and then display the AST. During parsing, Glud assumes that the code you are passing it is well-formed (would compile without errors), and will throw an exception if this does not occur.
import sys
from glud import *
translation_unit = parse(sys.argv[1])
print(dump(translation_unit.cursor))
Which when invoked on a C++ file (tmp.cpp) that looks like this:
class X {};
struct Y {};
class Z {
public:
int f(double x, char);
};
You would expect to see an AST like:
TRANSLATION_UNIT tmp.cpp
+--CLASS_DECL X
+--STRUCT_DECL Y
+--CLASS_DECL Z
+--CXX_ACCESS_SPEC_DECL
+--CXX_METHOD f
+--PARM_DECL x
+--PARM_DECL
Finding classes with methods¶
You can find class methods using cxxMethodDecl
import sys
from glud import *
matcher = cxxRecordDecl(isClass(), has(cxxMethodDecl()))
translation_unit = parse(sys.argv[1])
for cls in walk(matcher, translation_unit.cursor):
print(cls.type.spelling)
Composing Matchers¶
You can compose a matchers together to match more interesting criteria. For example, if you wanted to match methods that were public and non-static, you could write
matcher = cxxMethodDecl(
isPublic(),
hasStaticStorageDuration())
API¶
Parsing¶
Simplify the parsing of C/C++ using libclang. This module specifically supports the use case of single translation units where it isn’t important that symbols from other translation units would be visible.
parse (name, **kwargs) |
Parse a C/C++ file |
parse_string (contents[, name]) |
Parse a string of C/C++ code |
Traversal¶
Traverse the libclang generate AST using semantics similar to the python ast module.
walk (predicate, cursor) |
Yield all nodes found by recursively visiting the AST |
iter_child_nodes (predicate, cursor) |
Yield all direct child nodes of node |
AST Pretty Printing¶
Display the libclang AST, or a filtered subset of
dump (cursor) |
Display the AST represented by the cursor |
Matchers¶
Code that implements Abstract Syntax Tree node matchers, or allow
allOf (*args) |
Matches if all of the argument matchers match |
anyOf (*args) |
Matches if any of the argument matchers match |
anything () |
Matches anything |
builtinType (*args) |
Matches builtin primitive types (eg/ integers, booleans and float) |
classTemplateDecl (*args) |
Match C++ template class declarations |
cxxConstructorDecl (*args) |
Match C++ constructors |
cxxDestructorDecl (*args) |
Match C++ destructors |
cxxMethodDecl (*args) |
Match C++ methods |
cxxRecordDecl (*args) |
Matches C++ class declarations. |
decl (*args) |
Match any declaration |
enumDecl (*args) |
Match enumerations |
fieldDecl (*args) |
Match struct / class fields |
functionDecl (*args) |
Match function declarations |
has (*args) |
Match if a cursor has a child that matches |
hasAncestor (matcher) |
Matches if the current cursor has an ancestor that matches |
hasAnyParameter (matcher) |
Match if any method or function argument matches |
hasCanonicalType (m) |
Matches if a cursor has the specified number of arguments |
hasName (name) |
Match a cursors spelling against a pattern |
hasParameter (N, inner) |
Matches if the N-th parameter matches the inner matcher |
hasParent (*args) |
Matches if the direct parent node matches |
hasReturnType (matcher) |
Match a function/method with a specified return type |
hasStaticStorageDuration () |
Match an item has static storage duration |
hasType (matcher) |
Matches if the type associated with the current cursor matches |
hasTypename (typename) |
Match if the spelling of the type of a cursor matches a pattern |
isClass () |
Matches if a cursor is a class |
isDefinition () |
Matches if the cursor is a definition |
isDerivedFrom (name) |
Match if a C++ type inherits from a named class |
isExpansionInFileMatching (pattern) |
Matches if the nodes location matches a pattern |
isPrivate () |
Test if a cursor is private |
isProtected () |
Test if a cursor is protected |
isPublic () |
Test if a cursor is public |
isSameOrDerivedFrom (name) |
Match if derives-from (or is-a) class with a given name |
isStruct () |
Matches if a cursor is a struct |
namespaceDecl (*args) |
Match a C++ namespace declaration |
parameterCountIs (N) |
Matches if a cursor has the specified number of arguments |
pointee (inner) |
Traverse from a pointer to the dereferenced type |
pointerType (*args) |
Test if a cursor has pointer type |
recordDecl (*args) |
Matches class, struct, and union declarations. |
stmt (*args) |
Matches statements |
typedefDecl (*args) |
Matches typedef declarations |
unless (*args) |
Inverts the match of the children |
varDecl (*args) |
Matches variable declarations |
Definitions¶
-
exception
glud.parsing.
ClangDiagnosticException
(diagnostic)[source]¶ Encapsulates Clang diagnostics as an exception
-
glud.traversal.
walk
(predicate, cursor)[source]¶ Yield all nodes found by recursively visiting the AST
-
glud.matchers.
allOf
(*args)[source]¶ Matches if all of the argument matchers match
>>> from glud import * >>> config = ''' ... class X; ... class Y; ... ''' >>> m = allOf(cxxRecordDecl(), hasName('X')) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
anyOf
(*args)[source]¶ Matches if any of the argument matchers match
>>> from glud import * >>> config = ''' ... int x; ... class Y {}; ... enum Z {}; ... ''' >>> m = anyOf(varDecl(), isClass()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) x Y
-
glud.matchers.
hasType
(matcher)[source]¶ Matches if the type associated with the current cursor matches
>>> from glud import * >>> config = ''' ... int x; ... long y; ... ''' >>> m = varDecl(hasType(hasName('int'))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) x
-
glud.matchers.
anything
()[source]¶ Matches anything
>>> from glud import * >>> config = ''' ... namespace W { ... int x; ... class Y; ... enum Z {}; ... } ... ''' >>> m = allOf(anything(), hasParent(namespaceDecl())) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) x Y Z
-
glud.matchers.
hasAnyParameter
(matcher)[source]¶ Match if any method or function argument matches
>>> from glud import * >>> config = ''' ... void f(); ... void g(int); ... ''' >>> m = functionDecl(hasAnyParameter(hasType(builtinType()))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) g
-
glud.matchers.
builtinType
(*args)[source]¶ Matches builtin primitive types (eg/ integers, booleans and float)
>>> from glud import * >>> config = ''' ... int x; ... bool y; ... ''' >>> m = varDecl(hasType(builtinType())) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) x y
-
glud.matchers.
classTemplateDecl
(*args)[source]¶ Match C++ template class declarations
>>> from glud import * >>> config = ''' ... template<typename T> class X {}; ... class Y {}; ... ''' >>> m = classTemplateDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
cxxRecordDecl
(*args)[source]¶ Matches C++ class declarations.
>>> from glud import * >>> config = ''' ... class W; ... template<typename T> class X {}; ... struct Y {}; ... union Z {}; ... ''' >>> m = cxxRecordDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) W X
-
glud.matchers.
cxxConstructorDecl
(*args)[source]¶ Match C++ constructors
>>> from glud import * >>> config = ''' ... class X { ... X(); ... }; ... class Y {}; ... ''' >>> m = cxxConstructorDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
cxxDestructorDecl
(*args)[source]¶ Match C++ destructors
>>> from glud import * >>> config = ''' ... class X { ... ~X(); ... }; ... class Y {}; ... ''' >>> m = cxxDestructorDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) ~X
-
glud.matchers.
cxxMethodDecl
(*args)[source]¶ Match C++ methods
>>> from glud import * >>> config = ''' ... class X { ... void u(); ... void v(); ... }; ... ''' >>> m = cxxMethodDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) u v
-
glud.matchers.
decl
(*args)[source]¶ Match any declaration
>>> from glud import * >>> config = ''' ... class X {}; ... struct Y {}; ... enum Z {}; ... ''' >>> m = decl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X Y Z
-
glud.matchers.
enumDecl
(*args)[source]¶ Match enumerations
>>> from glud import * >>> config = ''' ... enum X {}; ... enum class Y {}; ... ''' >>> m = enumDecl() >>> args = '-x c++ -std=c++11'.split() >>> for c in walk(m, parse_string(config, args=args).cursor): ... print(c.spelling) X Y
-
glud.matchers.
fieldDecl
(*args)[source]¶ Match struct / class fields
>>> from glud import * >>> config = ''' ... struct X { ... int u; ... int v; ... }; ... ''' >>> m = fieldDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) u v
-
glud.matchers.
functionDecl
(*args)[source]¶ Match function declarations
>>> from glud import * >>> config = ''' ... int u(); ... int v(); ... ''' >>> m = functionDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) u v
-
glud.matchers.
has
(*args)[source]¶ Match if a cursor has a child that matches
>>> from glud import * >>> config = ''' ... class X { ... void f(); ... }; ... class Y; ... ''' >>> m = cxxRecordDecl(has(cxxMethodDecl())) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
hasName
(name)[source]¶ Match a cursors spelling against a pattern
>>> from glud import * >>> config = ''' ... class X {}; ... class Y {}; ... ''' >>> m = cxxRecordDecl(hasName('X')) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
hasReturnType
(matcher)[source]¶ Match a function/method with a specified return type
>>> from glud import * >>> config = ''' ... class X {}; ... X u(); ... int v(); ... ''' >>> m = functionDecl(hasReturnType(builtinType())) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) v
-
glud.matchers.
hasStaticStorageDuration
()[source]¶ Match an item has static storage duration
>>> from glud import * >>> config = ''' ... class X { ... static void u(); ... void v(); ... }; ... ''' >>> m = cxxMethodDecl( ... hasStaticStorageDuration()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) u
-
glud.matchers.
hasTypename
(typename)[source]¶ Match if the spelling of the type of a cursor matches a pattern
>>> from glud import * >>> config = ''' ... namespace X { ... class Y {}; ... } ... class Y {}; ... ''' >>> m = cxxRecordDecl(hasTypename('X::Y')) >>> for c in walk(m, parse_string(config).cursor): ... print(c.type.spelling) X::Y
-
glud.matchers.
isDerivedFrom
(name)[source]¶ Match if a C++ type inherits from a named class
>>> from glud import * >>> config = ''' ... class X {}; ... class Y : public X {}; ... class Z : public Y {}; ... ''' >>> m = cxxRecordDecl(isDerivedFrom('X')) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) Y Z
-
glud.matchers.
isSameOrDerivedFrom
(name)[source]¶ Match if derives-from (or is-a) class with a given name
>>> from glud import * >>> config = ''' ... class X {}; ... class Y : public X {}; ... class Z : public Y {}; ... ''' >>> m = cxxRecordDecl(isSameOrDerivedFrom('X')) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X Y Z
-
glud.matchers.
namespaceDecl
(*args)[source]¶ Match a C++ namespace declaration
>>> from glud import * >>> config = ''' ... namespace X { } ... ''' >>> m = namespaceDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
recordDecl
(*args)[source]¶ Matches class, struct, and union declarations.
>>> from glud import * >>> config = ''' ... class W; ... template<typename T> class X {}; ... struct Y {}; ... union Z {}; ... ''' >>> m = recordDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) W X Y Z
-
glud.matchers.
stmt
(*args)[source]¶ Matches statements
>>> from glud import * >>> config = ''' ... void f() { } ... ''' >>> m = stmt() >>> i = 0 >>> for c in parse_string(config).cursor.walk_preorder(): ... if m(c): ... i += 1 >>> print(i) 1
-
glud.matchers.
typedefDecl
(*args)[source]¶ Matches typedef declarations
>>> from glud import * >>> config = ''' ... typedef int X; ... ''' >>> m = typedefDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
unless
(*args)[source]¶ Inverts the match of the children
>>> from glud import * >>> config = ''' ... class X { }; ... class Y {}; ... class Z {}; ... ''' >>> m = cxxRecordDecl(unless(hasName('Y'))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X Z
-
glud.matchers.
isDefinition
()[source]¶ Matches if the cursor is a definition
>>> from glud import * >>> config = ''' ... class X {}; ... class Y; ... ''' >>> m = cxxRecordDecl(isDefinition()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
hasAncestor
(matcher)[source]¶ Matches if the current cursor has an ancestor that matches
>>> from glud import * >>> config = ''' ... namespace X { ... class Y {}; ... } ... class Z {}; ... ''' >>> m = cxxRecordDecl( ... hasName('Y'), ... hasAncestor(namespaceDecl(hasName('X')))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) Y
-
glud.matchers.
isExpansionInFileMatching
(pattern)[source]¶ Matches if the nodes location matches a pattern
>>> from glud import * >>> config = ''' ... class X; ... ''' >>> m = isExpansionInFileMatching('tmp.cpp') >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
varDecl
(*args)[source]¶ Matches variable declarations
>>> from glud import * >>> config = ''' ... int a; ... ''' >>> m = varDecl() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) a
-
glud.matchers.
hasParent
(*args)[source]¶ Matches if the direct parent node matches
>>> from glud import * >>> config = ''' ... namespace X { ... int a; ... } ... int b; ... ''' >>> m = varDecl(hasParent(namespaceDecl(hasName('X')))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) a
-
glud.matchers.
parameterCountIs
(N)[source]¶ Matches if a cursor has the specified number of arguments
>>> from glud import * >>> config = ''' ... int f(); ... int g(int); ... int h(int, int); ... ''' >>> m = functionDecl(parameterCountIs(1)) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) g
-
glud.matchers.
hasCanonicalType
(m)[source]¶ Matches if a cursor has the specified number of arguments
>>> from glud import * >>> config = ''' ... namespace X { ... struct Y; ... Y f(); ... } ... ''' >>> m = functionDecl(hasReturnType(hasCanonicalType(hasName('X::Y')))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) f
-
glud.matchers.
isStruct
()[source]¶ Matches if a cursor is a struct
>>> from glud import * >>> config = ''' ... class X; ... struct Y; ... ''' >>> m = isStruct() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) Y
-
glud.matchers.
isClass
()[source]¶ Matches if a cursor is a class
>>> from glud import * >>> config = ''' ... class X; ... struct Y; ... ''' >>> m = isClass() >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) X
-
glud.matchers.
isPublic
()[source]¶ Test if a cursor is public
>>> from glud import * >>> config = ''' ... class X { ... public: ... int y; ... }; ... ''' >>> m = fieldDecl(isPublic()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) y
-
glud.matchers.
isProtected
()[source]¶ Test if a cursor is protected
>>> from glud import * >>> config = ''' ... class X { ... protected: ... int y; ... }; ... ''' >>> m = fieldDecl(isProtected()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) y
-
glud.matchers.
isPrivate
()[source]¶ Test if a cursor is private
>>> from glud import * >>> config = ''' ... class X { ... private: ... int y; ... }; ... ''' >>> m = fieldDecl(isPrivate()) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) y
-
glud.matchers.
pointerType
(*args)[source]¶ Test if a cursor has pointer type
>>> from glud import * >>> config = ''' ... int w; ... int* x; ... int** y; ... int& z = w; ... ''' >>> m = varDecl(hasType(pointerType())) >>> for c in parse_string(config, args='-x c++ -std=c++11'.split()).cursor.walk_preorder(): ... if m(c): ... print(c.spelling) x y
-
glud.matchers.
pointee
(inner)[source]¶ Traverse from a pointer to the dereferenced type
>>> from glud import * >>> config = ''' ... int *x; ... void *y; ... ''' >>> m = varDecl(hasType(pointerType(pointee(hasName('int'))))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) x
-
glud.matchers.
hasParameter
(N, inner)[source]¶ Matches if the N-th parameter matches the inner matcher
>>> from glud import * >>> config = ''' ... void f(int x); ... void g(int y, int x); ... void h(); ... ''' >>> m = functionDecl(hasParameter(1, hasName('x'))) >>> for c in walk(m, parse_string(config).cursor): ... print(c.spelling) g
Internal API¶
Internal implementation details that may be useful for building new matchers, or understanding the behavior of existing matchers. Nothing in this section is guaranteed to remain stable between releases.
-
glud.predicates.
has_storage_class
(kind)[source]¶ Check if the cursor has a particular (eg/ static) storage class
-
glud.predicates.
is_builtin
(n)[source]¶ Test if a type is a simple types (integer, boolean, char, float)
-
glud.predicates.
is_definition
(cursor)[source]¶ Test if a cursor refers to a definition
This occurs when the cursor has a definition, and shares the location of that definiton
Changelog¶
Release History¶
0.4.0 (2016-07-23)¶
New Features
- Added new matchers - parameterCountIs, isExpansionInFileMatching, varDecl, hasParent, hasParameter, hasCanonicalType, pointee and pointerType
Improvements
- Improved behavior on older versions of libclang
- Moved existing code from the internal API into the stable interface (isPublic, isProtected, isPrivate)
Bug Fixes
- Corrected the behavior of recordDecl and cxxRecordDecl to more closely map to the libclangastmatchers vision of those matchers
- Corrected the name of anyArgument to hasAnyParameter