Skip to content

IR Types

The Intermediate Representation (IR) is the core data model of headerkit. Parser backends produce IR objects; writers consume them to generate output in various formats.

All IR types are Python dataclasses defined in the headerkit.ir module.

Container

The top-level object returned by all parser backends. Header is maintained as a backward-compatible alias for SourceUnit.

SourceUnit dataclass

SourceUnit(path, declarations=list(), included_headers=set(), language='c', classification='header')

Container for a parsed source or interface unit.

This is the top-level result returned by all parser backends. It contains the file path, extracted declarations, and input metadata.

::

from headerkit.backends import get_backend
from headerkit.ir import Struct, Function, SourceUnit

backend = get_backend()
unit = backend.parse(code, "myheader.h")

print(f"Parsed {len(unit.declarations)} declarations from {unit.path}")

for decl in unit.declarations:
    if isinstance(decl, Function):
        print(f"  Function: {decl.name}")

Parameters:

Name Type Description Default
path str

Path to the original source file.

required
declarations list[Declaration]

List of extracted declarations (structs, functions, etc.).

list()
included_headers set[str]

Set of header file basenames included by this unit (populated by libclang backend only).

set()
language str

Language identifier (e.g. 'c', 'cpp', 'nim', 'rust').

'c'
classification str

Source classification (e.g. 'header', 'source', 'interface').

Example

'header'

InputSpec dataclass

InputSpec(path, language='c', classification='header', content=None)

Specification of an input source unit, its language, and classification.

Parameters:

Name Type Description Default
path str

Path to the source file or virtual file.

required
language str

Language identifier (e.g. 'c', 'cpp', 'nim', 'rust', 'zig').

'c'
classification str

Classification ('header', 'source', 'interface', 'idl').

'header'
content str | None

Optional raw string content.

None

from_path classmethod

from_path(path, language=None, classification=None, content=None)

Infer language and classification from a file path extension.

Header module-attribute

Header = SourceUnit

Type Expressions

Type expressions form a recursive tree structure representing C type syntax. For example, const char** becomes Pointer(Pointer(CType("char", ["const"]))).

CType dataclass

CType(name, qualifiers=list(), is_elaborated=None)

A C type expression representing a base type with optional qualifiers.

This is the fundamental building block for all type representations. Qualifiers like const, volatile, unsigned are stored separately from the type name for easier manipulation.

Parameters:

Name Type Description Default
name str

The base type name (e.g., "int", "long", "char").

required
qualifiers list[str]

Type qualifiers (e.g., ["const"], ["unsigned"]).

list()
is_elaborated bool | None

Whether the source wrote an elaborated type specifier -- struct X, union X, enum X -- rather than the bare X. None when no parser recorded it.

C keeps tags and ordinary identifiers in separate namespaces, so
``struct Gauge { ... };`` and ``typedef unsigned char Gauge;`` are both
legal in one unit and name different types: the elaborated spelling is
the eight-byte record and the bare one is a one-byte integer. The
distinction lives only in how the *use site* was written, so a consumer
that receives ``"Gauge"`` for both cannot recover it -- and choosing
either meaning is silently wrong for the other.

Three states, not two. ``True`` and ``False`` are observations; ``None``
means nobody looked, and a consumer facing a contested name must refuse
rather than assume. That is the same contract as
:attr:`Enum.underlying_type_known`, for the same reason: twice now a
consumer of this IR has had to answer a question the IR did not record,
and both times treating "absent" as "fine" produced a wrong ABI that
imported cleanly.

Examples

Simple types::

int_type = CType("int")
unsigned_long = CType("long", ["unsigned"])
const_int = CType("int", ["const"])

Composite types with pointers::

from headerkit.ir import Pointer

# const char*
const_char_ptr = Pointer(CType("char", ["const"]))
None

Pointer dataclass

Pointer(pointee, qualifiers=list())

Pointer to another type.

Represents pointer types with optional qualifiers. Pointers can be nested to represent multi-level indirection (e.g., char**).

Parameters:

Name Type Description Default
pointee TypeExpr

The type being pointed to.

required
qualifiers list[str]

Qualifiers on the pointer itself (e.g., ["const"] for a const pointer, not a pointer to const).

Examples

Basic pointer::

int_ptr = Pointer(CType("int"))  # int*

Pointer to const::

const_char_ptr = Pointer(CType("char", ["const"]))  # const char*

Double pointer::

char_ptr_ptr = Pointer(Pointer(CType("char")))  # char**

Const pointer (pointer itself is const)::

const_ptr = Pointer(CType("int"), ["const"])  # int* const
list()

Array dataclass

Array(element_type, size=None)

Fixed-size or flexible array type.

Represents C array types, which can have a fixed numeric size, a symbolic size (macro or constant), or be flexible (incomplete).

Parameters:

Name Type Description Default
element_type TypeExpr

The type of array elements.

required
size Union[int, str] | None

Array size - an integer for fixed size, a string for symbolic/expression size (e.g., "MAX_SIZE"), or None for flexible/incomplete arrays.

Examples

Fixed-size array::

int_arr = Array(CType("int"), 10)

Flexible array (incomplete)::

flex_arr = Array(CType("char"), None)

Symbolic size::

buf = Array(CType("char"), "BUFFER_SIZE")

Multi-dimensional array::

matrix = Array(Array(CType("int"), 3), 3)
None

FunctionPointer dataclass

FunctionPointer(return_type, parameters=list(), is_variadic=False, calling_convention=None)

Function pointer type.

Represents a pointer to a function with a specific signature. Used for callbacks, vtables, and function tables.

Parameters:

Name Type Description Default
return_type TypeExpr

The function's return type.

required
parameters list[Parameter]

List of function parameters.

list()
is_variadic bool

True if the function accepts variable arguments (ends with ...).

False
calling_convention str | None

The calling convention if non-default (e.g., "stdcall", "cdecl", "fastcall"). None for the platform default calling convention.

Examples

Simple function pointer::

void_fn = FunctionPointer(CType("int"), [])  # int (*)(void)

With parameters::

callback = FunctionPointer(
    CType("void"),
    [Parameter("data", Pointer(CType("void")))]
)  # void (*)(void* data)

Variadic function pointer::

printf_fn = FunctionPointer(
    CType("int"),
    [Parameter("fmt", Pointer(CType("char", ["const"])))],
    is_variadic=True
)  # int (*)(const char* fmt, ...)
None

Declarations

Declaration types represent the top-level constructs found in C/C++ headers.

Enum dataclass

Enum(name, values=list(), is_typedef=False, namespace=None, location=None, is_scoped=False, cpp_name=None, underlying_type=None, underlying_type_known=True)

Enumeration declaration.

Represents a C enum type with named constants. Enums may be named or anonymous (used in typedefs or inline).

Parameters:

Name Type Description Default
name str | None

The enum tag name, or None for anonymous enums.

required
values list[EnumValue]

List of enumeration constants.

list()
is_typedef bool

True if this enum came from a typedef declaration.

False
namespace str | None

Enclosing C++ namespace, or None at global scope. Part of the enum's identity: a::E and b::E are distinct declarations.

None
location SourceLocation | None

Source location for error reporting.

None
is_scoped bool

True for a C++ enum class/enum struct. A scoped enumerator is a member of the tag, spelled E::X, and is not introduced into the enclosing namespace as X.

False
cpp_name str | None

Fully-qualified C++ spelling of the tag, when it is not derivable from namespace and name. A member enum is hoisted to the top level and loses its enclosing record, so class C { enum M; } records C::M here; None means namespace-plus-name is the whole spelling.

None
underlying_type str | None

The integer type the enum is represented as, as a C type spelling such as "unsigned char" or "long long", or None when the parser did not report one.

C leaves this implementation-defined, requiring only that it represent every enumerator, and both C++11 forms may fix it explicitly -- on a scoped enum (enum class E : unsigned char) and on an unscoped one (enum E : unsigned long long). It cannot be reconstructed from the enumerators: they constrain the width from below and say nothing about a type chosen to be wider, and an opaque declaration such as enum Fwd : long long; has no enumerators at all. A consumer that must know the width -- any binding generator, since this is the size and signedness of every value crossing the ABI -- has to be told, so it is recorded here rather than guessed at downstream.

None means the header declared none, and is only trustworthy when underlying_type_known is True. See that field.

None
underlying_type_known bool

Whether underlying_type is an observation or an absence of one.

A parser can fail to see a clause that is there. tree-sitter's **C**
grammar has no production for ``enum E : long long`` -- C23 standardised
it and both major compilers accepted it as an extension for years -- so
it parses as an ``ERROR`` node with no ``base`` field, which is
indistinguishable from a plain ``enum E`` if only ``underlying_type`` is
consulted. A consumer that reads the resulting ``None`` as "declared
none" falls back to guessing the width from the enumerators, and
``enum E : long long { A = 0 };`` becomes a four-byte type where the
compiler laid out eight.

False means the parser found something it could not represent, so the
width is unknown rather than absent, and a consumer must refuse rather
than infer. It defaults to True because "declared none" is the ordinary
case and every parser reports *that* reliably.

Examples

Named enum::

color = Enum("Color", [
    EnumValue("RED", 0),
    EnumValue("GREEN", 1),
    EnumValue("BLUE", 2),
])

Anonymous enum (typically used with typedef)::

anon = Enum(None, [EnumValue("FLAG_A", 1), EnumValue("FLAG_B", 2)])
True

qualified_name property

qualified_name

The tag's full C++ spelling, or None if it is anonymous.

EnumValue dataclass

EnumValue(name, value=None)

Single enumeration constant.

Represents one named constant within an enum definition.

Parameters:

Name Type Description Default
name str

The constant name.

required
value Union[int, str] | None

The constant's value - an integer for explicit values, a string for expressions (e.g., "FOO | BAR"), or None for auto-incremented values.

Examples

Explicit value::

red = EnumValue("RED", 0)

Auto-increment (implicit value)::

green = EnumValue("GREEN", None)  # follows previous value

Expression value::

mask = EnumValue("MASK", "FLAG_A | FLAG_B")
None

BaseSpecifier dataclass

BaseSpecifier(name, access='public', is_virtual=False)

C++ base class specifier in class inheritance.

Parameters:

Name Type Description Default
name str

Name of the base class.

required
access str

Access specifier ("public", "protected", "private").

'public'
is_virtual bool

True if virtually inherited.

False

Struct dataclass

Struct(name, fields=list(), methods=list(), is_union=False, is_cppclass=False, is_typedef=False, is_packed=False, namespace=None, template_params=list(), cpp_name=None, notes=list(), inner_typedefs=dict(), nested_records=list(), bases=list(), is_abstract=False, constructors=list(), destructor=None, conversions=list(), vtable_entries=list(), attributes=list(), is_deprecated=False, alignment=None, location=None)

Struct or union declaration.

Represents a C struct or union type definition. Both use the same IR class with is_union distinguishing between them.

Parameters:

Name Type Description Default
name str | None

The struct/union tag name, or None for anonymous types.

required
fields list[Field]

List of member fields.

list()
methods list[Function]

List of methods (for C++ classes only).

list()
is_union bool

True for unions, False for structs.

False
is_cppclass bool

True for C++ classes (uses cppclass in Cython).

False
is_typedef bool

True if this came from a typedef declaration.

False
is_packed bool

True if the struct has __attribute__((packed)), which disables padding and alignment. Affects memory layout.

False
nested_records list[Struct]

Records defined inside this record's body. A C++ nested class stays here rather than being lifted to the top level, because its name is only meaningful when qualified by the enclosing scope.

list()
location SourceLocation | None

Source location for error reporting.

Examples

Simple struct::

point = Struct("Point", [
    Field("x", CType("int")),
    Field("y", CType("int")),
])

Union::

data = Struct("Data", [
    Field("i", CType("int")),
    Field("f", CType("float")),
], is_union=True)

C++ class with method::

widget = Struct("Widget", [
    Field("width", CType("int")),
], methods=[
    Function("resize", CType("void"), [
        Parameter("w", CType("int")),
        Parameter("h", CType("int")),
    ])
], is_cppclass=True)

Anonymous struct::

anon = Struct(None, [Field("value", CType("int"))])
None

Field dataclass

Field(name, type, bit_width=None, anonymous_struct=None, is_anonymous_transparent=False, access=None, is_static=False, is_padding=False)

Struct or union field declaration.

Represents a single field within a struct or union definition.

Parameters:

Name Type Description Default
name str

The field name.

required
type TypeExpr

The field's type expression.

required
bit_width int | None

C bitfield width in bits, or None for non-bitfield fields. For example, uint32_t flags : 4 has bit_width=4.

None
anonymous_struct Struct | None

When this field is an anonymous nested struct or union, holds the :class:Struct IR node for the anonymous type. None for regular fields.

None
access str | None

Access specifier ("public", "protected", "private"), or None for C struct fields / default access.

None
is_static bool

True if this is a static data member.

False
is_padding bool

True for an unnamed bitfield (int : 3;), which C17 6.7.2.1p13 gives no member name. Such an entry is not an accessible member, but it does occupy bits, so a consumer that reconstructs layout (the ctypes writer) needs it. A bit_width of 0 is the zero-width form (int : 0;), which aligns the next field to a fresh storage unit rather than reserving bits. Writers that emit C source must skip these: the C compiler lays the record out from the original declaration.

Examples

Simple field::

x_field = Field("x", CType("int"))  # int x

Pointer field::

data = Field("data", Pointer(CType("void")))  # void* data

Array field::

buffer = Field("buffer", Array(CType("char"), 256))  # char buffer[256]

Bitfield::

flags = Field("flags", CType("uint32_t"), bit_width=4)  # uint32_t flags : 4

Anonymous nested struct::

inner = Struct(None, [Field("x", CType("int"))], is_union=False)
field = Field("pos", CType("void"), anonymous_struct=inner)

Unnamed bitfield padding::

pad = Field("", CType("unsigned int"), bit_width=3, is_padding=True)
False

Function dataclass

Function(name, return_type, parameters=list(), is_variadic=False, calling_convention=None, namespace=None, template_params=list(), is_static=False, is_const=False, is_virtual=False, is_pure_virtual=False, is_explicit=False, access=None, is_deleted=False, is_defaulted=False, is_noexcept=False, is_inline=False, body=None, attributes=list(), is_deprecated=False, location=None)

Function declaration.

Represents a C function prototype or declaration. Does not include the function body (declarations only).

Parameters:

Name Type Description Default
name str

The function name.

required
return_type TypeExpr

The function's return type.

required
parameters list[Parameter]

List of function parameters.

list()
is_variadic bool

True if the function accepts variable arguments.

False
calling_convention str | None

The calling convention if non-default (e.g., "stdcall", "cdecl", "fastcall"). None for the platform default calling convention.

None
template_params list[str]

Template parameter names for C++ function templates.

list()
location SourceLocation | None

Source location for error reporting.

Examples

Simple function::

exit_fn = Function("exit", CType("void"), [
    Parameter("status", CType("int"))
])

With return value::

strlen_fn = Function("strlen", CType("size_t"), [
    Parameter("s", Pointer(CType("char", ["const"])))
])

Variadic function::

printf_fn = Function(
    "printf",
    CType("int"),
    [Parameter("fmt", Pointer(CType("char", ["const"])))],
    is_variadic=True
)
None

Parameter dataclass

Parameter(name, type, default_value=None)

Function parameter declaration.

Represents a single parameter in a function signature. Parameters may be named or anonymous (common in prototypes).

Parameters:

Name Type Description Default
name str | None

Parameter name, or None for anonymous parameters.

required
type TypeExpr

The parameter's type expression.

required
default_value str | None

Default argument expression string, or None if none.

Examples

Named parameter::

x_param = Parameter("x", CType("int"))  # int x

With default value::

count_param = Parameter("count", CType("int"), default_value="0")  # int count = 0

Anonymous parameter::

anon = Parameter(None, Pointer(CType("void")))  # void*

Complex type::

callback = Parameter("fn", FunctionPointer(CType("void"), []))
None

Typedef dataclass

Typedef(name, underlying_type, namespace=None, attributes=list(), is_deprecated=False, location=None)

Type alias declaration.

Represents a C typedef that creates an alias for another type. Common patterns include aliasing primitives, struct tags, and function pointer types.

Parameters:

Name Type Description Default
name str

The new type name being defined.

required
underlying_type TypeExpr

The type being aliased.

required
location SourceLocation | None

Source location for error reporting.

Examples

Simple alias::

uint_alias = Typedef("uint", CType("unsigned int"))

Struct alias::

point_alias = Typedef("Point", CType("struct Point"))

Function pointer alias::

cb_alias = Typedef(
    "Callback",
    Pointer(FunctionPointer(CType("void"), [Parameter("code", CType("int"))]))
)
None

Variable dataclass

Variable(name, type, namespace=None, attributes=list(), is_deprecated=False, alignment=None, location=None)

Global or extern variable declaration.

Represents variable declarations at file scope, including extern variables and file-scope data definitions.

Parameters:

Name Type Description Default
name str

The variable identifier.

required
type TypeExpr

The variable's type expression.

required
location SourceLocation | None

Source location for error reporting.

Examples

Extern variable::

errno_var = Variable("errno", CType("int"))

Const string::

version = Variable("version", Pointer(CType("char", ["const"])))

Array variable::

lookup_table = Variable("table", Array(CType("int"), 256))
None

Constant dataclass

Constant(name, value=None, evaluated_value=None, raw_expression=None, type=None, is_macro=False, location=None)

Compile-time constant declaration.

Represents #define macros with constant values or const variable declarations. Only backends that support macro extraction (e.g., libclang) can populate macro constants.

Parameters:

Name Type Description Default
name str

The constant name.

required
value Union[int, float, str] | None

The constant's value - an integer, float, or string expression. None if the value cannot be determined.

None
evaluated_value Union[int, float, str] | None

The evaluated numeric or string value if evaluable.

None
raw_expression str | None

The un-evaluated macro or constant expression string.

None
type CType | None

For typed constants (const int), the C type. None for macros.

None
is_macro bool

True if this is a #define macro, False for const declarations.

False
location SourceLocation | None

Source location for error reporting.

Examples

Numeric macro::

size = Constant("SIZE", 100, is_macro=True)

Expression macro::

mask = Constant("MASK", 16, raw_expression="1 << 4", evaluated_value=16, is_macro=True)

Typed const::

max_val = Constant("MAX_VALUE", 255, type=CType("int"))

String macro::

version = Constant("VERSION", '"1.0.0"', is_macro=True)
None

Union Types

These are typing.Union aliases used in type annotations throughout headerkit.

Declaration

Declaration = Union[Enum, Struct, Function, Typedef, Variable, Constant]

Any top-level declaration that can appear in a Header.

TypeExpr

TypeExpr = Union[CType, Pointer, Array, FunctionPointer]

Any type expression that can appear in a declaration's type fields.

Source Location

SourceLocation dataclass

SourceLocation(file, line, column=None)

Location in source file for error reporting and filtering.

Used to track where declarations originated, enabling:

  • Better error messages during parsing
  • Filtering declarations by file (e.g., exclude system headers)
  • Source mapping for debugging

::

loc = SourceLocation("myheader.h", 42, 5)
print(f"Declaration at {loc.file}:{loc.line}")

Parameters:

Name Type Description Default
file str

Path to the source file.

required
line int

Line number (1-indexed).

required
column int | None

Column number (1-indexed), or None if unknown.

Example

None

Parser Backend Protocol

The parser backend protocol is defined alongside the IR types since backends produce IR directly. See also the Backends page for registry functions.

ParserBackend

Bases: Protocol

Protocol defining the interface for parser backends.

All parser backends must implement this protocol to be usable with headerkit. Backends are responsible for translating from their native AST format (pycparser, libclang, etc.) to the common :class:Header IR format.

Available Backends

  • libclang - LLVM clang-based parser with C++ support

Example

::

from headerkit.backends import get_backend

# Get default backend
backend = get_backend()

# Get specific backend
libclang = get_backend("libclang")

# Parse code
header = backend.parse("int foo(void);", "test.h")

name property

name

Human-readable name of this backend (e.g., "pycparser").

supports_macros property

supports_macros

Whether this backend can extract #define constants.

supports_cpp property

supports_cpp

Whether this backend can parse C++ code.

supported_languages property

supported_languages

Set of source languages supported by this backend (e.g., frozenset({"c", "cpp"})).

supported_classifications property

supported_classifications

Set of input classifications supported by this backend (e.g., frozenset({"header", "source"})).

parse

parse(code, filename, include_dirs=None, extra_args=None, *, use_default_includes=True, recursive_includes=True, max_depth=10, project_prefixes=None, allowlist=None, denylist=None)

Parse C/C++ code and return the IR representation.

Traversal and filtering

recursive_includes governs traversal: descending into each non-system included header and merging what it declares. project_prefixes decides which paths count as project rather than system headers, and max_depth (with a backend-internal cycle guard) bounds the descent.

allowlist and denylist govern filtering, and they narrow the merged result in both traversal modes:

  • recursive_includes=True with no allowlist returns declarations from every non-system included header.
  • recursive_includes=True with an allowlist returns the main file's declarations plus those of the named files, and nothing else.
  • recursive_includes=False with no allowlist returns the main file's declarations alone.
  • Deny wins over allow. A file named by both lists is excluded.
  • The main file is never denied; a denylist governs included files only.
  • A list entry matching nothing is not an error.

Parameters:

Name Type Description Default
code str

Source code to parse.

required
filename str

Name of the source file. Used for error messages and #line directives. Does not need to exist on disk.

required
include_dirs list[str] | None

Directories to search for #include files. Only used by backends that handle preprocessing.

None
extra_args list[str] | None

Additional arguments for the preprocessor/compiler. Format is backend-specific.

None
use_default_includes bool

If True, add system include directories.

True
recursive_includes bool

If True, descend into included project headers and merge their declarations into the result. False parses only the main file, and is the only way to exclude included declarations entirely.

True
max_depth int

Maximum recursion depth for include processing.

10
project_prefixes tuple[str, ...] | None

Path prefixes to treat as project headers rather than system headers, so that they are descended into.

None
allowlist list[str] | None

Files whose declarations are kept, alongside the main file's own. None keeps every non-system file reached by traversal. Absolute entries are used as-is; relative entries (including a bare basename) resolve against the parsed file's directory, then include_dirs, then the current working directory. An entry containing *, ? or [ is an :mod:fnmatch pattern whose directory part resolves the same way. Matching is on whole resolved paths, never substrings.

None
denylist list[str] | None

Files whose declarations are dropped, using the same resolution and glob rules as allowlist. Deny wins over allow. A denylist with no allowlist means "everything except these". The parsed file itself cannot be denied.

None

Returns:

Type Description
Header

Parsed header containing all extracted declarations.

Raises:

Type Description
RuntimeError

If parsing fails due to syntax errors.