gen_ir.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #-------------------------------------------------------------------------------
  2. # Generate an intermediate representation of a clang AST dump.
  3. #-------------------------------------------------------------------------------
  4. import json, sys, subprocess
  5. def is_api_decl(decl, prefix):
  6. if 'name' in decl:
  7. return decl['name'].startswith(prefix)
  8. elif decl['kind'] == 'EnumDecl':
  9. # an anonymous enum, check if the items start with the prefix
  10. return decl['inner'][0]['name'].lower().startswith(prefix)
  11. else:
  12. return False
  13. def is_dep_decl(decl, dep_prefixes):
  14. for prefix in dep_prefixes:
  15. if is_api_decl(decl, prefix):
  16. return True
  17. return False
  18. def dep_prefix(decl, dep_prefixes):
  19. for prefix in dep_prefixes:
  20. if is_api_decl(decl, prefix):
  21. return prefix
  22. return None
  23. def filter_types(str):
  24. return str.replace('_Bool', 'bool')
  25. def parse_struct(decl):
  26. outp = {}
  27. outp['kind'] = 'struct'
  28. outp['name'] = decl['name']
  29. outp['fields'] = []
  30. for item_decl in decl['inner']:
  31. if item_decl['kind'] != 'FieldDecl':
  32. sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})")
  33. item = {}
  34. if 'name' in item_decl:
  35. item['name'] = item_decl['name']
  36. item['type'] = filter_types(item_decl['type']['qualType'])
  37. outp['fields'].append(item)
  38. return outp
  39. def parse_enum(decl):
  40. outp = {}
  41. if 'name' in decl:
  42. outp['kind'] = 'enum'
  43. outp['name'] = decl['name']
  44. needs_value = False
  45. else:
  46. outp['kind'] = 'consts'
  47. needs_value = True
  48. outp['items'] = []
  49. for item_decl in decl['inner']:
  50. if item_decl['kind'] == 'EnumConstantDecl':
  51. item = {}
  52. item['name'] = item_decl['name']
  53. if 'inner' in item_decl:
  54. const_expr = item_decl['inner'][0]
  55. if const_expr['kind'] != 'ConstantExpr':
  56. sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
  57. if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
  58. sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
  59. if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
  60. sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
  61. item['value'] = const_expr['inner'][0]['value']
  62. if needs_value and 'value' not in item:
  63. sys.exit(f"ERROR: anonymous enum items require an explicit value")
  64. outp['items'].append(item)
  65. return outp
  66. def parse_func(decl):
  67. outp = {}
  68. outp['kind'] = 'func'
  69. outp['name'] = decl['name']
  70. outp['type'] = filter_types(decl['type']['qualType'])
  71. outp['params'] = []
  72. if 'inner' in decl:
  73. for param in decl['inner']:
  74. if param['kind'] != 'ParmVarDecl':
  75. print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)")
  76. return None
  77. outp_param = {}
  78. outp_param['name'] = param['name']
  79. outp_param['type'] = filter_types(param['type']['qualType'])
  80. outp['params'].append(outp_param)
  81. return outp
  82. def parse_decl(decl):
  83. kind = decl['kind']
  84. if kind == 'RecordDecl':
  85. return parse_struct(decl)
  86. elif kind == 'EnumDecl':
  87. return parse_enum(decl)
  88. elif kind == 'FunctionDecl':
  89. return parse_func(decl)
  90. else:
  91. return None
  92. def clang(csrc_path):
  93. cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ]
  94. cmd.append(csrc_path)
  95. return subprocess.check_output(cmd)
  96. def gen(header_path, source_path, module, main_prefix, dep_prefixes):
  97. ast = clang(source_path)
  98. inp = json.loads(ast)
  99. outp = {}
  100. outp['module'] = module
  101. outp['prefix'] = main_prefix
  102. outp['dep_prefixes'] = dep_prefixes
  103. outp['decls'] = []
  104. for decl in inp['inner']:
  105. is_dep = is_dep_decl(decl, dep_prefixes)
  106. if is_api_decl(decl, main_prefix) or is_dep:
  107. outp_decl = parse_decl(decl)
  108. if outp_decl is not None:
  109. outp_decl['is_dep'] = is_dep
  110. outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
  111. outp['decls'].append(outp_decl)
  112. with open(f'{module}.json', 'w') as f:
  113. f.write(json.dumps(outp, indent=2));
  114. return outp