gen_d.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. #-------------------------------------------------------------------------------
  2. # Generate D bindings.
  3. #
  4. # D coding style:
  5. # - types are PascalCase
  6. # - functions are camelCase
  7. # - otherwise snake_case
  8. #-------------------------------------------------------------------------------
  9. import gen_ir
  10. import os
  11. import shutil
  12. import sys
  13. import gen_util as util
  14. module_names = {
  15. 'slog_': 'log',
  16. 'sg_': 'gfx',
  17. 'sapp_': 'app',
  18. 'stm_': 'time',
  19. 'saudio_': 'audio',
  20. 'sgl_': 'gl',
  21. 'sdtx_': 'debugtext',
  22. 'sshape_': 'shape',
  23. 'sglue_': 'glue',
  24. 'sfetch_': 'fetch',
  25. 'simgui_': 'imgui',
  26. }
  27. c_source_paths = {
  28. 'slog_': 'sokol-d/src/sokol/c/sokol_log.c',
  29. 'sg_': 'sokol-d/src/sokol/c/sokol_gfx.c',
  30. 'sapp_': 'sokol-d/src/sokol/c/sokol_app.c',
  31. 'stm_': 'sokol-d/src/sokol/c/sokol_time.c',
  32. 'saudio_': 'sokol-d/src/sokol/c/sokol_audio.c',
  33. 'sgl_': 'sokol-d/src/sokol/c/sokol_gl.c',
  34. 'sdtx_': 'sokol-d/src/sokol/c/sokol_debugtext.c',
  35. 'sshape_': 'sokol-d/src/sokol/c/sokol_shape.c',
  36. 'sglue_': 'sokol-d/src/sokol/c/sokol_glue.c',
  37. 'sfetch_': 'sokol-d/src/sokol/c/sokol_fetch.c',
  38. 'simgui_': 'sokol-d/src/sokol/c/sokol_imgui.c',
  39. }
  40. ignores = [
  41. 'sdtx_printf',
  42. 'sdtx_vprintf',
  43. ]
  44. # functions that need to be exposed as 'raw' C callbacks without a Dlang wrapper function
  45. c_callbacks = [
  46. 'slog_func'
  47. ]
  48. # NOTE: syntax for function results: "func_name.RESULT"
  49. overrides = {
  50. 'ref': '_ref',
  51. 'sgl_error': 'sgl_get_error', # 'error' is reserved in Dlang
  52. 'sgl_deg': 'sgl_as_degrees',
  53. 'sgl_rad': 'sgl_as_radians',
  54. 'sg_context_desc.color_format': 'int',
  55. 'sg_context_desc.depth_format': 'int',
  56. 'sg_apply_uniforms.ub_index': 'uint32_t',
  57. 'sg_draw.base_element': 'uint32_t',
  58. 'sg_draw.num_elements': 'uint32_t',
  59. 'sg_draw.num_instances': 'uint32_t',
  60. 'sshape_element_range_t.base_element': 'uint32_t',
  61. 'sshape_element_range_t.num_elements': 'uint32_t',
  62. 'sdtx_font.font_index': 'uint32_t',
  63. 'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
  64. 'sfetch_continue': 'continue_fetching', # 'continue' is reserved in D
  65. }
  66. prim_types = {
  67. "int": "int",
  68. "bool": "bool",
  69. "char": "char",
  70. "int8_t": "byte",
  71. "uint8_t": "ubyte",
  72. "int16_t": "short",
  73. "uint16_t": "ushort",
  74. "int32_t": "int",
  75. "uint32_t": "uint",
  76. "int64_t": "long",
  77. "uint64_t": "ulong",
  78. "float": "float",
  79. "double": "double",
  80. "uintptr_t": "ulong",
  81. "intptr_t": "long",
  82. "size_t": "size_t",
  83. }
  84. prim_defaults = {
  85. 'int': '0',
  86. 'bool': 'false',
  87. 'int8_t': '0',
  88. 'uint8_t': '0',
  89. 'int16_t': '0',
  90. 'uint16_t': '0',
  91. 'int32_t': '0',
  92. 'uint32_t': '0',
  93. 'int64_t': '0',
  94. 'uint64_t': '0',
  95. 'float': '0.0f',
  96. 'double': '0.0',
  97. 'uintptr_t': '0',
  98. 'intptr_t': '0',
  99. 'size_t': '0'
  100. }
  101. struct_types = []
  102. enum_types = []
  103. enum_items = {}
  104. out_lines = ''
  105. def reset_globals():
  106. global struct_types
  107. global enum_types
  108. global enum_items
  109. global out_lines
  110. struct_types = []
  111. enum_types = []
  112. enum_items = {}
  113. out_lines = ''
  114. def l(s):
  115. global out_lines
  116. out_lines += s + '\n'
  117. def as_d_prim_type(s):
  118. return prim_types[s]
  119. # prefix_bla_blub(_t) => (dep.)BlaBlub
  120. def as_d_struct_type(s, prefix):
  121. parts = s.lower().split('_')
  122. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  123. for part in parts[1:]:
  124. # ignore '_t' type postfix
  125. if (part != 't'):
  126. outp += part.capitalize()
  127. return outp
  128. # prefix_bla_blub(_t) => (dep.)BlaBlub
  129. def as_d_enum_type(s, prefix):
  130. parts = s.lower().split('_')
  131. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  132. for part in parts[1:]:
  133. if (part != 't'):
  134. outp += part.capitalize()
  135. return outp
  136. def check_override(name, default=None):
  137. if name in overrides:
  138. return overrides[name]
  139. elif default is None:
  140. return name
  141. else:
  142. return default
  143. def check_ignore(name):
  144. return name in ignores
  145. # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
  146. def as_enum_item_name(s):
  147. outp = s.lstrip('_')
  148. parts = outp.split('_')[2:]
  149. outp = '_'.join(parts)
  150. outp = outp.capitalize()
  151. if outp[0].isdigit():
  152. outp = '_' + outp.capitalize()
  153. return outp
  154. def enum_default_item(enum_name):
  155. return enum_items[enum_name][0]
  156. def is_prim_type(s):
  157. return s in prim_types
  158. def is_struct_type(s):
  159. return s in struct_types
  160. def is_enum_type(s):
  161. return s in enum_types
  162. def is_const_prim_ptr(s):
  163. for prim_type in prim_types:
  164. if s == f"const {prim_type} *":
  165. return True
  166. return False
  167. def is_prim_ptr(s):
  168. for prim_type in prim_types:
  169. if s == f"{prim_type} *":
  170. return True
  171. return False
  172. def is_const_struct_ptr(s):
  173. for struct_type in struct_types:
  174. if s == f"const {struct_type} *":
  175. return True
  176. return False
  177. def is_struct_ptr(s):
  178. for struct_type in struct_types:
  179. if s == f"{struct_type} *":
  180. return True
  181. return False
  182. def type_default_value(s):
  183. return prim_defaults[s]
  184. def as_c_arg_type(arg_type, prefix):
  185. if arg_type == "void":
  186. return "void"
  187. elif is_prim_type(arg_type):
  188. return as_d_prim_type(arg_type)
  189. elif is_struct_type(arg_type):
  190. return as_d_struct_type(arg_type, prefix)
  191. elif is_enum_type(arg_type):
  192. return as_d_enum_type(arg_type, prefix)
  193. elif util.is_void_ptr(arg_type):
  194. return "void*"
  195. elif util.is_const_void_ptr(arg_type):
  196. return "const(void)*"
  197. elif util.is_string_ptr(arg_type):
  198. return "const(char)*"
  199. elif is_const_struct_ptr(arg_type):
  200. return f"const {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)} *"
  201. elif is_prim_ptr(arg_type):
  202. return f"{as_d_prim_type(util.extract_ptr_type(arg_type))} *"
  203. elif is_const_prim_ptr(arg_type):
  204. return f"const {as_d_prim_type(util.extract_ptr_type(arg_type))} *"
  205. else:
  206. sys.exit(f"Error as_c_arg_type(): {arg_type}")
  207. def as_d_arg_type(arg_prefix, arg_type, prefix):
  208. # NOTE: if arg_prefix is None, the result is used as return value
  209. pre = "" if arg_prefix is None else arg_prefix
  210. if arg_type == "void":
  211. if arg_prefix is None:
  212. return "void"
  213. else:
  214. return ""
  215. elif is_prim_type(arg_type):
  216. return as_d_prim_type(arg_type) + pre
  217. elif is_struct_type(arg_type):
  218. return as_d_struct_type(arg_type, prefix) + pre
  219. elif is_enum_type(arg_type):
  220. return as_d_enum_type(arg_type, prefix) + pre
  221. elif util.is_void_ptr(arg_type):
  222. return "scope void*" + pre
  223. elif util.is_const_void_ptr(arg_type):
  224. return "scope const(void)*" + pre
  225. elif util.is_string_ptr(arg_type):
  226. return "scope const(char)*" + pre
  227. elif is_struct_ptr(arg_type):
  228. return f"scope ref {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)}" + pre
  229. elif is_const_struct_ptr(arg_type):
  230. return f"scope ref {as_d_struct_type(util.extract_ptr_type(arg_type), prefix)}" + pre
  231. elif is_prim_ptr(arg_type):
  232. return f"scope {as_d_prim_type(util.extract_ptr_type(arg_type))} *" + pre
  233. elif is_const_prim_ptr(arg_type):
  234. return f"scope const {as_d_prim_type(util.extract_ptr_type(arg_type))} *" + pre
  235. else:
  236. sys.exit(f"ERROR as_d_arg_type(): {arg_type}")
  237. def is_d_string(d_type):
  238. return d_type == "string"
  239. # get C-style arguments of a function pointer as string
  240. def funcptr_args_c(field_type, prefix):
  241. tokens = field_type[field_type.index('(*)')+4:-1].split(',')
  242. s = ""
  243. for token in tokens:
  244. arg_type = token.strip()
  245. if s != "":
  246. s += ", "
  247. c_arg = as_c_arg_type(arg_type, prefix)
  248. if c_arg == "void":
  249. return ""
  250. else:
  251. s += c_arg
  252. return s
  253. # get C-style result of a function pointer as string
  254. def funcptr_result_c(field_type):
  255. res_type = field_type[:field_type.index('(*)')].strip()
  256. if res_type == 'void':
  257. return 'void'
  258. elif is_prim_type(res_type):
  259. return as_d_prim_type(res_type)
  260. elif util.is_const_void_ptr(res_type):
  261. return 'const(void)*'
  262. elif util.is_void_ptr(res_type):
  263. return 'void*'
  264. else:
  265. sys.exit(f"ERROR funcptr_result_c(): {field_type}")
  266. def funcdecl_args_c(decl, prefix):
  267. s = ""
  268. func_name = decl['name']
  269. for param_decl in decl['params']:
  270. if s != "":
  271. s += ", "
  272. param_name = param_decl['name']
  273. param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
  274. s += as_c_arg_type(param_type, prefix)
  275. return s
  276. def funcdecl_args_d(decl, prefix):
  277. s = ""
  278. func_name = decl['name']
  279. for param_decl in decl['params']:
  280. if s != "":
  281. s += ", "
  282. param_name = param_decl['name']
  283. param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
  284. s += f"{as_d_arg_type(f' {param_name}', param_type, prefix)}"
  285. return s
  286. def funcdecl_result_c(decl, prefix):
  287. func_name = decl['name']
  288. decl_type = decl['type']
  289. result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
  290. return as_c_arg_type(result_type, prefix)
  291. def funcdecl_result_d(decl, prefix):
  292. func_name = decl['name']
  293. decl_type = decl['type']
  294. result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
  295. d_res_type = as_d_arg_type(None, result_type, prefix)
  296. if is_d_string(d_res_type):
  297. d_res_type = "string"
  298. return d_res_type
  299. def gen_struct(decl, prefix):
  300. struct_name = check_override(decl['name'])
  301. d_type = as_d_struct_type(struct_name, prefix)
  302. l(f"extern(C)\nstruct {d_type} {{")
  303. for field in decl['fields']:
  304. field_name = check_override(field['name'])
  305. field_type = check_override(f'{struct_name}.{field_name}', default=field['type'])
  306. if is_prim_type(field_type):
  307. l(f" {as_d_prim_type(field_type)} {field_name} = {type_default_value(field_type)};")
  308. elif is_struct_type(field_type):
  309. l(f" {as_d_struct_type(field_type, prefix)} {field_name};")
  310. elif is_enum_type(field_type):
  311. l(f" {as_d_enum_type(field_type, prefix)} {field_name};")
  312. elif util.is_string_ptr(field_type):
  313. l(f" const(char)* {field_name} = null;")
  314. elif util.is_const_void_ptr(field_type):
  315. l(f" const(void)* {field_name} = null;")
  316. elif util.is_void_ptr(field_type):
  317. l(f" void* {field_name} = null;")
  318. elif is_const_prim_ptr(field_type):
  319. l(f" const {as_d_prim_type(util.extract_ptr_type(field_type))} = null;")
  320. elif util.is_func_ptr(field_type):
  321. l(f" extern(C) {funcptr_result_c(field_type)} function({funcptr_args_c(field_type, prefix)}) {field_name} = null;")
  322. elif util.is_1d_array_type(field_type):
  323. array_type = util.extract_array_type(field_type)
  324. array_sizes = util.extract_array_sizes(field_type)
  325. if is_prim_type(array_type) or is_struct_type(array_type):
  326. if is_prim_type(array_type):
  327. d_type = as_d_prim_type(array_type)
  328. def_val = type_default_value(array_type)
  329. elif is_struct_type(array_type):
  330. d_type = as_d_struct_type(array_type, prefix)
  331. def_val = ''
  332. elif is_enum_type(array_type):
  333. d_type = as_d_enum_type(array_type, prefix)
  334. def_val = ''
  335. else:
  336. sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
  337. t0 = f"{d_type}[{array_sizes[0]}]"
  338. t1 = f"{d_type}[]"
  339. if def_val != '':
  340. l(f" {t0} {field_name} = {def_val};")
  341. else:
  342. l(f" {t0} {field_name};")
  343. elif util.is_const_void_ptr(array_type):
  344. l(f" const(void)*[{array_sizes[0]}] {field_name} = null;")
  345. else:
  346. sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]")
  347. elif util.is_2d_array_type(field_type):
  348. array_type = util.extract_array_type(field_type)
  349. array_sizes = util.extract_array_sizes(field_type)
  350. if is_prim_type(array_type):
  351. d_type = as_d_prim_type(array_type)
  352. def_val = type_default_value(array_type)
  353. elif is_struct_type(array_type):
  354. d_type = as_d_struct_type(array_type, prefix)
  355. def_val = ''
  356. else:
  357. sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
  358. t0 = f"{d_type}[{array_sizes[0]}][{array_sizes[1]}]"
  359. if def_val != '':
  360. l(f" {t0} {field_name} = {def_val};")
  361. else:
  362. l(f" {t0} {field_name};")
  363. else:
  364. sys.exit(f"ERROR gen_struct: {field_type} {field_name};")
  365. l("}")
  366. def gen_consts(decl, prefix):
  367. for item in decl['items']:
  368. item_name = check_override(item['name'])
  369. l(f"enum {util.as_lower_snake_case(item_name, prefix)} = {item['value']};")
  370. def gen_enum(decl, prefix):
  371. enum_name = check_override(decl['name'])
  372. l(f"enum {as_d_enum_type(enum_name, prefix)} {{")
  373. for item in decl['items']:
  374. item_name = as_enum_item_name(check_override(item['name']))
  375. if item_name != "Force_u32":
  376. if 'value' in item:
  377. l(f" {item_name} = {item['value']},")
  378. else:
  379. l(f" {item_name},")
  380. l("}")
  381. def gen_func_c(decl, prefix):
  382. l(f"extern(C) {funcdecl_result_c(decl, prefix)} {decl['name']}({funcdecl_args_c(decl, prefix)}) @system @nogc nothrow;")
  383. def gen_func_d(decl, prefix):
  384. c_func_name = decl['name']
  385. d_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix)
  386. if c_func_name in c_callbacks:
  387. # a simple forwarded C callback function
  388. l(f"alias {d_func_name} = {c_func_name};")
  389. else:
  390. d_res_type = funcdecl_result_d(decl, prefix)
  391. l(f"{d_res_type} {d_func_name}({funcdecl_args_d(decl, prefix)}) @trusted @nogc nothrow {{")
  392. if d_res_type != 'void':
  393. s = f" return {c_func_name}("
  394. else:
  395. s = f" {c_func_name}("
  396. for i, param_decl in enumerate(decl['params']):
  397. if i > 0:
  398. s += ", "
  399. arg_name = param_decl['name']
  400. arg_type = param_decl['type']
  401. if is_const_struct_ptr(arg_type):
  402. s += f"&{arg_name}"
  403. elif util.is_string_ptr(arg_type):
  404. s += f"{arg_name}"
  405. else:
  406. s += arg_name
  407. if is_d_string(d_res_type):
  408. s += ")"
  409. s += ");"
  410. l(s)
  411. l("}")
  412. def pre_parse(inp):
  413. global struct_types
  414. global enum_types
  415. for decl in inp['decls']:
  416. kind = decl['kind']
  417. if kind == 'struct':
  418. struct_types.append(decl['name'])
  419. elif kind == 'enum':
  420. enum_name = decl['name']
  421. enum_types.append(enum_name)
  422. enum_items[enum_name] = []
  423. for item in decl['items']:
  424. enum_items[enum_name].append(as_enum_item_name(item['name']))
  425. def gen_imports(inp, dep_prefixes):
  426. for dep_prefix in dep_prefixes:
  427. dep_module_name = module_names[dep_prefix]
  428. l(f'import {dep_prefix[:-1]} = sokol.{dep_module_name};')
  429. l('')
  430. def gen_module(inp, dep_prefixes):
  431. l('// machine generated, do not edit')
  432. l('')
  433. l(f'module sokol.{inp["module"]};')
  434. gen_imports(inp, dep_prefixes)
  435. pre_parse(inp)
  436. prefix = inp['prefix']
  437. for decl in inp['decls']:
  438. if not decl['is_dep']:
  439. kind = decl['kind']
  440. if kind == 'consts':
  441. gen_consts(decl, prefix)
  442. elif not check_ignore(decl['name']):
  443. if kind == 'struct':
  444. gen_struct(decl, prefix)
  445. elif kind == 'enum':
  446. gen_enum(decl, prefix)
  447. elif kind == 'func':
  448. gen_func_c(decl, prefix)
  449. gen_func_d(decl, prefix)
  450. def prepare():
  451. print('=== Generating d bindings:')
  452. if not os.path.isdir('sokol-d/src/sokol'):
  453. os.makedirs('sokol-d/src/sokol')
  454. if not os.path.isdir('sokol-d/src/sokol/c'):
  455. os.makedirs('sokol-d/src/sokol/c')
  456. def gen(c_header_path, c_prefix, dep_c_prefixes):
  457. if not c_prefix in module_names:
  458. print(f' >> warning: skipping generation for {c_prefix} prefix...')
  459. return
  460. module_name = module_names[c_prefix]
  461. c_source_path = c_source_paths[c_prefix]
  462. print(f' {c_header_path} => {module_name}')
  463. reset_globals()
  464. shutil.copyfile(c_header_path, f'sokol-d/src/sokol/c/{os.path.basename(c_header_path)}')
  465. ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
  466. gen_module(ir, dep_c_prefixes)
  467. output_path = f"sokol-d/src/sokol/{ir['module']}.d"
  468. with open(output_path, 'w', newline='\n') as f_outp:
  469. f_outp.write(out_lines)