pycparser
pycparser is a complete, standards-compliant C99 parser written in pure Python, producing an Abstract Syntax Tree (AST) that can be traversed, modified, and re-emitted as C code. Current version is 3.0, released January 2026, which replaced the bundled PLY (Lex/Yacc) backend with a hand-written lexer and recursive-descent parser—eliminating all external dependencies and improving parse speed by ~30%. Release cadence is irregular; major versions are rare (2.x spanned 2012–2025).
Warnings
- breaking v3.0 dropped PLY entirely and rewrote the lexer/parser from scratch. CParser.__init__ kwargs lex_optimize, yacc_optimize, yacc_debug, lextab, yacctab are no longer accepted — passing them raises TypeError.
- breaking Python 3.8 support was dropped in v3.0. Requires Python >=3.10.
- gotcha pycparser cannot parse raw C files that contain #include or macros — it requires preprocessed input. Passing unpreprocessed C to parser.parse() will produce parse errors for any non-trivial real-world file.
- gotcha fake_libc_include headers are NOT included in the PyPI pip wheel. Code that assumes pycparser.__file__ contains a utils/fake_libc_include directory will fail at runtime.
- gotcha The fake_libc_include headers target C11 and will cause errors if the preprocessor is invoked with -std=c99 or older standards.
- gotcha pycparser supports C99 and a subset of C11, but does NOT support GCC/Clang extensions (__attribute__, __extension__, __builtin_*, etc.) out of the box. Passing GCC-extended code will raise ParseError.
- gotcha Frozen-app packagers (cx_Freeze, PyInstaller) may fail to import pycparser 3.0 due to internal relative-import restructuring introduced when PLY was removed, raising ImportError on 'c_parser'.
Install
-
pip install pycparser -
pip install "pycparser<3"
Imports
- CParser
from pycparser import c_parser parser = c_parser.CParser()
- parse_file
from pycparser import parse_file
- c_generator.CGenerator
from pycparser import c_generator gen = c_generator.CGenerator()
- c_ast / NodeVisitor
from pycparser import c_ast class MyVisitor(c_ast.NodeVisitor): ...
Quickstart
from pycparser import c_parser, c_ast, c_generator
# Direct in-memory parse — only works for preprocessor-free, self-contained C
parser = c_parser.CParser()
src = r"""
int add(int a, int b) {
return a + b;
}
"""
ast = parser.parse(src, filename='<none>')
ast.show(attrnames=True, nodenames=True)
# Regenerate C source from AST
gen = c_generator.CGenerator()
print(gen.visit(ast))
# Visitor pattern: collect all function names
class FuncDefVisitor(c_ast.NodeVisitor):
def visit_FuncDef(self, node):
print('Function:', node.decl.name)
v = FuncDefVisitor()
v.visit(ast)