Created
January 30, 2022 16:58
-
-
Save p7g/14e892bbf3cf05627449dd320563cc48 to your computer and use it in GitHub Desktop.
Ask clang what a struct looks like
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Ask clang what a struct looks like | |
Structs like stat don't necessarily have the same fields in the same order on | |
different platforms. This makes writing a language with C FFI sound pretty | |
awful... since all the different platforms would need to be handled by the FFI | |
library of that language. Maybe with something like this, the C compiler could | |
be queried to find out what these structs look like? | |
""" | |
import io | |
import re | |
import subprocess | |
import tempfile | |
from pycparser import c_ast, c_generator, parse_file | |
csource = """\ | |
#define __attribute__(X) | |
#define __restrict restrict | |
#include <sys/stat.h> | |
struct stat st; | |
""" | |
with tempfile.NamedTemporaryFile(suffix=".c") as in_file: | |
in_file.write(csource.encode("utf-8")) | |
in_file.seek(0) | |
ast = parse_file( | |
in_file.name, | |
use_cpp=True, | |
cpp_path="clang", | |
cpp_args=["-E"], | |
) | |
typedefs = {} | |
for node in ast: | |
if ( | |
isinstance(node, c_ast.Decl) | |
and node.name is None | |
and isinstance(node.type, c_ast.Struct) | |
and node.type.name == "stat" | |
): | |
struct_stat = node | |
elif isinstance(node, c_ast.Typedef): | |
typedefs[node.name] = node.type.type | |
def resolve(ty): | |
if isinstance(ty, c_ast.TypeDecl): | |
return resolve(ty.type) | |
elif isinstance(ty, c_ast.ArrayDecl): | |
return c_ast.ArrayDecl( | |
type=c_ast.TypeDecl( | |
declname=ty.type.declname, | |
quals=ty.type.quals, | |
align=ty.type.align, | |
type=resolve(ty.type), | |
), | |
dim=ty.dim, | |
dim_quals=ty.dim_quals, | |
) | |
if not isinstance(ty, c_ast.IdentifierType) or len(ty.names) != 1: | |
return ty | |
name = ty.names[0] | |
if name not in typedefs: | |
return ty | |
return resolve(typedefs[name]) | |
resolved_struct = c_ast.Struct(name="stat", decls=[]) | |
for decl in struct_stat.type.decls: | |
resolved_type = resolve(decl.type) | |
if not isinstance(resolved_type, c_ast.ArrayDecl): | |
resolved_type = c_ast.TypeDecl( | |
declname=decl.name, | |
type=resolve(decl.type), | |
quals=getattr(decl.type, "quals", []), | |
align=getattr(decl.type, "align", None), | |
) | |
resolved_struct.decls.append( | |
c_ast.Decl( | |
name=decl.name, | |
type=resolved_type, | |
quals=decl.quals, | |
align=decl.align, | |
storage=decl.storage, | |
funcspec=decl.funcspec, | |
init=decl.init, | |
bitsize=decl.bitsize, | |
) | |
) | |
g = c_generator.CGenerator(reduce_parentheses=True) | |
print(g.visit(resolved_struct)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment