gen_c3.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #-------------------------------------------------------------------------------
  2. # gen_c3.py
  3. #
  4. # Generate C3 bindings.
  5. #-------------------------------------------------------------------------------
  6. import gen_ir
  7. import gen_util as util
  8. import os, shutil, sys
  9. bindings_root = 'sokol-c3'
  10. c_root = f'{bindings_root}/sokol.c3l/c'
  11. module_root = f'{bindings_root}/sokol.c3l'
  12. # TODO: Consider changing module names to something shorter.
  13. # For example we could C prefixes, for example `sg` instead of current `gfx`.
  14. module_names = {
  15. 'slog_': 'slog',
  16. 'sg_': 'sg',
  17. 'sapp_': 'sapp',
  18. 'stm_': 'stm',
  19. 'saudio_': 'saudio',
  20. 'sgl_': 'sgl',
  21. 'sdtx_': 'sdtx',
  22. 'sshape_': 'sshape',
  23. 'sglue_': 'sglue',
  24. }
  25. c_source_names = {
  26. 'slog_': 'sokol_log.c',
  27. 'sg_': 'sokol_gfx.c',
  28. 'sapp_': 'sokol_app.c',
  29. 'sapp_sg': 'sokol_glue.c',
  30. 'stm_': 'sokol_time.c',
  31. 'saudio_': 'sokol_audio.c',
  32. 'sgl_': 'sokol_gl.c',
  33. 'sdtx_': 'sokol_debugtext.c',
  34. 'sshape_': 'sokol_shape.c',
  35. 'sglue_': 'sokol_glue.c',
  36. }
  37. ignores = [
  38. 'sdtx_printf',
  39. 'sdtx_vprintf',
  40. 'sg_install_trace_hooks',
  41. 'sg_trace_hooks',
  42. ]
  43. overrides = {
  44. # `any` is treated specially in C3.
  45. 'any': '_any',
  46. # Constants must be uppercase - lowercase `x` is not allowed.
  47. 'SG_PIXELFORMAT_ASTC_4x4_RGBA': 'SG_PIXELFORMAT_ASTC_4X4_RGBA',
  48. 'SG_PIXELFORMAT_ASTC_4x4_SRGBA': 'SG_PIXELFORMAT_ASTC_4X4_SRGBA',
  49. }
  50. prim_types = {
  51. 'int': 'CInt',
  52. # TODO: Check whether we should translate to `CBool` instead?
  53. 'bool': 'bool',
  54. 'char': 'char',
  55. 'int8_t': 'ichar',
  56. 'uint8_t': 'char',
  57. 'int16_t': 'short',
  58. 'uint16_t': 'ushort',
  59. 'int32_t': 'int',
  60. 'uint32_t': 'uint',
  61. 'int64_t': 'long',
  62. 'uint64_t': 'ulong',
  63. 'float': 'float',
  64. 'double': 'double',
  65. 'uintptr_t': 'uptr',
  66. 'intptr_t': 'iptr',
  67. 'size_t': 'usz'
  68. }
  69. prim_defaults = {
  70. 'int': '0',
  71. 'bool': 'false',
  72. 'int8_t': '0',
  73. 'uint8_t': '0',
  74. 'int16_t': '0',
  75. 'uint16_t': '0',
  76. 'int32_t': '0',
  77. 'uint32_t': '0',
  78. 'int64_t': '0',
  79. 'uint64_t': '0',
  80. 'float': '0.0',
  81. 'double': '0.0',
  82. 'uintptr_t': '0',
  83. 'intptr_t': '0',
  84. 'size_t': '0'
  85. }
  86. special_constant_types = {
  87. "SG_INVALID_ID": "uint",
  88. "SAPP_MODIFIER_SHIFT": "uint",
  89. "SAPP_MODIFIER_CTRL": "uint",
  90. "SAPP_MODIFIER_ALT": "uint",
  91. "SAPP_MODIFIER_SUPER": "uint",
  92. "SAPP_MODIFIER_LMB": "uint",
  93. "SAPP_MODIFIER_RMB": "uint",
  94. "SAPP_MODIFIER_MMB": "uint",
  95. }
  96. # Aliases for function pointers.
  97. # Function pointers must be aliased - we can't use them directly inside structs,
  98. # instead we have to create an alias and use the alias in the struct.
  99. aliases = {
  100. # C type -> (Alias name, Right hand side of alias).
  101. "void (*)(void *)":
  102. ("DataCb", "fn void(void*)"),
  103. "void *(*)(size_t, void *)":
  104. ("AllocCb", "fn void*(usz, void*)"),
  105. "void (*)(void *, void *)":
  106. ("FreeCb", "fn void*(usz, void*)"),
  107. "void (*)(const char *, uint32_t, uint32_t, const char *, uint32_t, const char *, void *)":
  108. ("LogCb", "fn void(ZString, uint, uint, ZString, uint, ZString, void*)"),
  109. "void (*)(void)":
  110. ("Cb", "fn void()"),
  111. "void (*)(const sapp_event *)":
  112. ("EventCb", "fn void(Event*)"),
  113. "void (*)(const sapp_event *, void *)":
  114. ("EventDataCb", "fn void(Event*, void*)"),
  115. "void (*)(const sapp_html5_fetch_response *)":
  116. ("ResponseCb", "fn void(Html5FetchResponse*)"),
  117. "void (*)(float *, int, int)":
  118. ("StreamCb", "fn void(float*, CInt, CInt)"),
  119. "void (*)(float *, int, int, void *)":
  120. ("StreamDataCb", "fn void(float*, CInt, CInt, void*)"),
  121. }
  122. struct_types = []
  123. enum_types = []
  124. enum_items = {}
  125. # Which alias were used in current module.
  126. # At the end of module we emit only used aliases.
  127. used_aliases = [] # We shouldn't use `set()` because the order differs among runs.
  128. out_lines = ''
  129. def reset_globals():
  130. global struct_types
  131. global enum_types
  132. global enum_items
  133. global used_aliases
  134. global out_lines
  135. struct_types = []
  136. enum_types = []
  137. enum_items = {}
  138. used_aliases = []
  139. out_lines = ''
  140. def l(s):
  141. global out_lines
  142. out_lines += s + '\n'
  143. def check_override(name, default=None):
  144. if name in overrides:
  145. return overrides[name]
  146. elif default is None:
  147. return name
  148. else:
  149. return default
  150. def check_ignore(name):
  151. return name in ignores
  152. # PREFIX_BLA_BLUB to BLA_BLUB, prefix_bla_blub to bla_blub
  153. def as_snake_case(s, prefix):
  154. outp = s
  155. if outp.lower().startswith(prefix):
  156. outp = outp[len(prefix):]
  157. return outp
  158. def get_c3_module_path(c_prefix):
  159. return f'{module_root}'
  160. def get_csource_path(c_prefix):
  161. return f'{c_root}/{c_source_names[c_prefix]}'
  162. def make_c3_module_directory(c_prefix):
  163. path = get_c3_module_path(c_prefix)
  164. if not os.path.isdir(path):
  165. os.makedirs(path)
  166. def as_prim_type(s):
  167. return prim_types[s]
  168. def as_upper_snake_case(s, prefix):
  169. outp = s.lower()
  170. if outp.startswith(prefix):
  171. outp = outp[len(prefix):]
  172. return outp.upper()
  173. def as_module_name_for_enum_type(enum_name, prefix):
  174. parts = enum_name.lower().split('_')
  175. parent_module = module_names[prefix]
  176. if parts[-1] == 't':
  177. # Ignore '_t' suffix.
  178. module = "_".join(parts[1:-1])
  179. else:
  180. module = "_".join(parts[1:])
  181. return f"sokol::{parent_module}::{module}"
  182. def as_parent_module_name_for_enum_type(enum_name, prefix):
  183. parent_module = module_names[prefix]
  184. return f"sokol::{parent_module}"
  185. # prefix_bla_blub(_t) => (dep::)BlaBlub
  186. def as_struct_or_enum_type(s, prefix):
  187. parts = s.lower().split('_')
  188. outp = '' if s.startswith(prefix) else f'sokol::{module_names[parts[0]+"_"]}::'
  189. for part in parts[1:]:
  190. # ignore '_t' type postfix
  191. if part != 't':
  192. outp += part.capitalize()
  193. return outp
  194. # PREFIX_ENUM_BLA_BLUB => BLA_BLUB, _PREFIX_ENUM_BLA_BLUB => BLA_BLUB
  195. def as_enum_item_name(s):
  196. outp = s.lstrip('_')
  197. parts = outp.split('_')[2:]
  198. outp = '_'.join(parts)
  199. if outp[0].isdigit():
  200. if outp in ["2D", "3D"]:
  201. outp = "TYPE_" + outp
  202. else:
  203. outp = 'NUM_' + outp
  204. return outp
  205. def is_prim_type(s):
  206. return s in prim_types
  207. def is_int_type(s):
  208. return s == "int"
  209. def is_struct_type(s):
  210. return s in struct_types
  211. def is_enum_type(s):
  212. return s in enum_types
  213. def is_const_prim_ptr(s):
  214. for prim_type in prim_types:
  215. if s == f"const {prim_type} *":
  216. return True
  217. return False
  218. def is_prim_ptr(s):
  219. for prim_type in prim_types:
  220. if s == f"{prim_type} *":
  221. return True
  222. return False
  223. def is_const_struct_ptr(s):
  224. for struct_type in struct_types:
  225. if s == f"const {struct_type} *":
  226. return True
  227. return False
  228. def type_default_value(s):
  229. return prim_defaults[s]
  230. def map_type(type, prefix, sub_type):
  231. if sub_type not in ['c_arg', 'struct_field']:
  232. sys.exit(f"Error: map_type(): unknown sub_type '{sub_type}")
  233. if type == "void":
  234. return ""
  235. elif is_prim_type(type):
  236. return as_prim_type(type)
  237. elif is_struct_type(type):
  238. return as_struct_or_enum_type(type, prefix)
  239. elif is_enum_type(type):
  240. return as_struct_or_enum_type(type, prefix)
  241. elif util.is_void_ptr(type):
  242. return "void*"
  243. elif util.is_const_void_ptr(type):
  244. return "void*"
  245. elif util.is_string_ptr(type):
  246. return "ZString"
  247. elif is_const_struct_ptr(type):
  248. return f"{as_struct_or_enum_type(util.extract_ptr_type(type), prefix)}*"
  249. elif is_prim_ptr(type):
  250. return f"{as_prim_type(util.extract_ptr_type(type))}*"
  251. elif is_const_prim_ptr(type):
  252. return f"{as_prim_type(util.extract_ptr_type(type))}*"
  253. elif util.is_1d_array_type(type):
  254. array_type = util.extract_array_type(type)
  255. array_sizes = util.extract_array_sizes(type)
  256. return f"{map_type(array_type, prefix, sub_type)}[{array_sizes[0]}]"
  257. elif util.is_2d_array_type(type):
  258. array_type = util.extract_array_type(type)
  259. array_sizes = util.extract_array_sizes(type)
  260. # TODO: Check if the dimensions are in correct order.
  261. return f"{map_type(array_type, prefix, sub_type)}[{array_sizes[0]}][{array_sizes[1]}]"
  262. elif util.is_func_ptr(type):
  263. if type in aliases:
  264. alias_name, _ = aliases[type]
  265. if type not in used_aliases:
  266. used_aliases.append(type)
  267. return alias_name
  268. else:
  269. sys.exit(f"Error map_type(): missing alias for function pointer '{type}'")
  270. else:
  271. sys.exit(f"Error map_type(): unknown type '{type}'")
  272. def funcdecl_args_c(decl, prefix):
  273. s = ''
  274. func_name = decl['name']
  275. for param_decl in decl['params']:
  276. if s != '':
  277. s += ', '
  278. param_name = param_decl['name']
  279. param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
  280. s += f"{map_type(param_type, prefix, 'c_arg')} {param_name}"
  281. return s
  282. def funcdecl_result_c(decl, prefix):
  283. func_name = decl['name']
  284. decl_type = decl['type']
  285. res_c_type = decl_type[:decl_type.index('(')].strip()
  286. return map_type(check_override(f'{func_name}.RESULT', default=res_c_type), prefix, 'c_arg')
  287. def gen_c_imports(inp, c_prefix, prefix):
  288. prefix = inp['prefix']
  289. for decl in inp['decls']:
  290. if decl['kind'] == 'func' and not decl['is_dep'] and not check_ignore(decl['name']):
  291. args = funcdecl_args_c(decl, prefix)
  292. res_type = funcdecl_result_c(decl, prefix)
  293. res_str = 'void' if res_type == '' else res_type
  294. l(f'extern fn {res_str} {check_override(as_snake_case(decl["name"], c_prefix))}({args}) @extern("{decl["name"]}");')
  295. l('')
  296. def gen_consts(decl, prefix):
  297. for item in decl["items"]:
  298. #
  299. # TODO: What type should these constants have? Currently giving all `usz`
  300. # unless specifically overridden by `special_constant_types`
  301. #
  302. item_name = check_override(item["name"])
  303. tpe = "usz"
  304. if item_name in special_constant_types:
  305. tpe = special_constant_types[item_name]
  306. l(f"const {tpe} {as_upper_snake_case(item_name, prefix)} = {item['value']};")
  307. l('')
  308. def gen_struct(decl, prefix):
  309. c_struct_name = check_override(decl['name'])
  310. struct_name = as_struct_or_enum_type(c_struct_name, prefix)
  311. l(f'struct {struct_name}')
  312. l('{')
  313. for field in decl['fields']:
  314. field_name = check_override(field['name'])
  315. field_type = map_type(check_override(f'{c_struct_name}.{field_name}', default=field['type']), prefix, 'struct_field')
  316. l(f' {field_type} {field_name};')
  317. l('}')
  318. l('')
  319. def gen_enum(decl, prefix):
  320. enum_name = check_override(decl['name'])
  321. tpe = "int"
  322. if any(as_enum_item_name(check_override(item['name'])) == 'FORCE_U32' for item in decl['items']):
  323. tpe = "uint"
  324. l(f'typedef {as_struct_or_enum_type(enum_name, prefix)} = {tpe};')
  325. # Constants are in submodule.
  326. l(f'module {as_module_name_for_enum_type(enum_name, prefix)};')
  327. value = "-1"
  328. for item in decl['items']:
  329. item_name = as_enum_item_name(check_override(item['name']))
  330. if item_name != 'FORCE_U32':
  331. if 'value' in item:
  332. value = item['value']
  333. else:
  334. value = str(int(value) + 1)
  335. l(f"const {as_struct_or_enum_type(enum_name, prefix)} {item_name} = {value};")
  336. l(f'module {as_parent_module_name_for_enum_type(enum_name, prefix)};')
  337. # After reopening the original module all dependencies must be reimported.
  338. l(f'import sokol;')
  339. l('')
  340. def gen_imports(dep_prefixes):
  341. l(f'import sokol;')
  342. l('')
  343. def gen_function_pointer_aliases():
  344. for type in used_aliases:
  345. alias_name, right_hand_side = aliases[type]
  346. l(f'alias {alias_name} = {right_hand_side};')
  347. l('')
  348. def gen_module(inp, c_prefix, dep_prefixes):
  349. pre_parse(inp)
  350. l('// machine generated, do not edit')
  351. l('')
  352. l(f"module sokol::{module_names[c_prefix]};")
  353. gen_imports(dep_prefixes)
  354. prefix = inp['prefix']
  355. gen_c_imports(inp, c_prefix, prefix)
  356. for decl in inp['decls']:
  357. if not decl['is_dep']:
  358. kind = decl['kind']
  359. if kind == 'consts':
  360. gen_consts(decl, prefix)
  361. elif not check_ignore(decl['name']):
  362. if kind == 'struct':
  363. gen_struct(decl, prefix)
  364. elif kind == 'enum':
  365. gen_enum(decl, prefix)
  366. gen_function_pointer_aliases()
  367. def pre_parse(inp):
  368. global struct_types
  369. global enum_types
  370. for decl in inp['decls']:
  371. kind = decl['kind']
  372. if kind == 'struct':
  373. struct_types.append(decl['name'])
  374. elif kind == 'enum':
  375. enum_name = decl['name']
  376. enum_types.append(enum_name)
  377. enum_items[enum_name] = []
  378. for item in decl['items']:
  379. enum_items[enum_name].append(as_enum_item_name(item['name']))
  380. def prepare():
  381. print('=== Generating C3 bindings:')
  382. if not os.path.isdir(module_root):
  383. os.makedirs(module_root)
  384. if not os.path.isdir(c_root):
  385. os.makedirs(c_root)
  386. def gen(c_header_path, c_prefix, dep_c_prefixes):
  387. if not c_prefix in module_names:
  388. print(f' >> warning: skipping generation for {c_prefix} prefix...')
  389. return
  390. reset_globals()
  391. make_c3_module_directory(c_prefix)
  392. print(f' {c_header_path} => {module_names[c_prefix]}')
  393. shutil.copyfile(c_header_path, f'{c_root}/{os.path.basename(c_header_path)}')
  394. csource_path = get_csource_path(c_prefix)
  395. module_name = module_names[c_prefix]
  396. ir = gen_ir.gen(c_header_path, csource_path, module_name, c_prefix, dep_c_prefixes)
  397. gen_module(ir, c_prefix, dep_c_prefixes)
  398. with open(f"{module_root}/{ir['module']}.c3", 'w', newline='\n') as f_outp:
  399. f_outp.write(out_lines)