gen_zig.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #-------------------------------------------------------------------------------
  2. # Read output of gen_json.py and generate Zig language bindings.
  3. #
  4. # Zig coding style:
  5. # - types are PascalCase
  6. # - functions are camelCase
  7. # - otherwise snake_case
  8. #-------------------------------------------------------------------------------
  9. import json, re, os, shutil
  10. struct_types = []
  11. enum_types = []
  12. enum_items = {}
  13. out_lines = ''
  14. def reset_globals():
  15. global struct_types
  16. global enum_types
  17. global enum_items
  18. global out_lines
  19. struct_types = []
  20. enum_types = []
  21. enum_items = {}
  22. out_lines = ''
  23. re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
  24. re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
  25. prim_types = {
  26. 'int': 'i32',
  27. 'bool': 'bool',
  28. 'int8_t': 'i8',
  29. 'uint8_t': 'u8',
  30. 'int16_t': 'i16',
  31. 'uint16_t': 'u16',
  32. 'int32_t': 'i32',
  33. 'uint32_t': 'u32',
  34. 'int64_t': 'i64',
  35. 'uint64_t': 'u64',
  36. 'float': 'f32',
  37. 'double': 'f64',
  38. 'uintptr_t': 'usize',
  39. 'intptr_t': 'isize'
  40. }
  41. prim_defaults = {
  42. 'int': '0',
  43. 'bool': 'false',
  44. 'int8_t': '0',
  45. 'uint8_t': '0',
  46. 'int16_t': '0',
  47. 'uint16_t': '0',
  48. 'int32_t': '0',
  49. 'uint32_t': '0',
  50. 'int64_t': '0',
  51. 'uint64_t': '0',
  52. 'float': '0.0',
  53. 'double': '0.0',
  54. 'uintptr_t': '0',
  55. 'intptr_t': '0'
  56. }
  57. struct_field_type_overrides = {
  58. 'sg_context_desc.color_format': 'int',
  59. 'sg_context_desc.depth_format': 'int',
  60. }
  61. def l(s):
  62. global out_lines
  63. out_lines += s + '\n'
  64. def as_zig_prim_type(s):
  65. return prim_types[s]
  66. def check_struct_field_type_override(struct_name, field_name, orig_type):
  67. s = f"{struct_name}.{field_name}"
  68. if s in struct_field_type_overrides:
  69. return struct_field_type_overrides[s]
  70. else:
  71. return orig_type
  72. # PREFIX_BLA_BLUB to bla_blub
  73. def as_snake_case(s, prefix):
  74. outp = s.lower()
  75. if outp.startswith(prefix):
  76. outp = outp[len(prefix):]
  77. return outp
  78. # prefix_bla_blub => BlaBlub
  79. def as_title_case(s):
  80. parts = s.lower().split('_')[1:]
  81. outp = ''
  82. for part in parts:
  83. outp += part.capitalize()
  84. return outp
  85. # prefix_bla_blub => blaBlub
  86. def as_camel_case(s):
  87. parts = s.lower().split('_')[1:]
  88. outp = parts[0]
  89. for part in parts[1:]:
  90. outp += part.capitalize()
  91. return outp
  92. # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
  93. def as_enum_item_name(s):
  94. outp = s
  95. if outp.startswith('_'):
  96. outp = outp[1:]
  97. parts = outp.split('_')[2:]
  98. outp = '_'.join(parts)
  99. if outp[0].isdigit():
  100. outp = '_' + outp
  101. return outp
  102. def enum_default_item(enum_name):
  103. return enum_items[enum_name][0]
  104. def is_prim_type(s):
  105. return s in prim_types
  106. def is_struct_type(s):
  107. return s in struct_types
  108. def is_enum_type(s):
  109. return s in enum_types
  110. def is_string_ptr(s):
  111. return s == "const char *"
  112. def is_const_void_ptr(s):
  113. return s == "const void *"
  114. def is_void_ptr(s):
  115. return s == "void *"
  116. def is_const_prim_ptr(s):
  117. for prim_type in prim_types:
  118. if s == f"const {prim_type} *":
  119. return True
  120. return False
  121. def is_prim_ptr(s):
  122. for prim_type in prim_types:
  123. if s == f"{prim_type} *":
  124. return True
  125. return False
  126. def is_const_struct_ptr(s):
  127. for struct_type in struct_types:
  128. if s == f"const {struct_type} *":
  129. return True
  130. return False
  131. def is_func_ptr(s):
  132. return '(*)' in s
  133. def is_1d_array_type(s):
  134. return re_1d_array.match(s)
  135. def is_2d_array_type(s):
  136. return re_2d_array.match(s)
  137. def type_default_value(s):
  138. return prim_defaults[s]
  139. def extract_array_type(s):
  140. return s[:s.index('[')].strip()
  141. def extract_array_nums(s):
  142. return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
  143. def extract_ptr_type(s):
  144. tokens = s.split()
  145. if tokens[0] == 'const':
  146. return tokens[1]
  147. else:
  148. return tokens[0]
  149. def as_extern_c_arg_type(arg_type):
  150. if arg_type == "void":
  151. return "void"
  152. elif is_prim_type(arg_type):
  153. return as_zig_prim_type(arg_type)
  154. elif is_struct_type(arg_type):
  155. return as_title_case(arg_type)
  156. elif is_enum_type(arg_type):
  157. return as_title_case(arg_type)
  158. elif is_void_ptr(arg_type):
  159. return "?*c_void"
  160. elif is_const_void_ptr(arg_type):
  161. return "?*const c_void"
  162. elif is_string_ptr(arg_type):
  163. return "[*c]const u8"
  164. elif is_const_struct_ptr(arg_type):
  165. return f"[*c]const {as_title_case(extract_ptr_type(arg_type))}"
  166. elif is_prim_ptr(arg_type):
  167. return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}"
  168. elif is_const_prim_ptr(arg_type):
  169. return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}"
  170. else:
  171. return '??? (as_extern_c_arg_type)'
  172. def as_zig_arg_type(arg_prefix, arg_type):
  173. # NOTE: if arg_prefix is None, the result is used as return value
  174. pre = "" if arg_prefix is None else arg_prefix
  175. if arg_type == "void":
  176. if arg_prefix is None:
  177. return "void"
  178. else:
  179. return ""
  180. elif is_prim_type(arg_type):
  181. return pre + as_zig_prim_type(arg_type)
  182. elif is_struct_type(arg_type):
  183. return pre + as_title_case(arg_type)
  184. elif is_enum_type(arg_type):
  185. return pre + as_title_case(arg_type)
  186. elif is_void_ptr(arg_type):
  187. return pre + "?*c_void"
  188. elif is_const_void_ptr(arg_type):
  189. return pre + "?*const c_void"
  190. elif is_string_ptr(arg_type):
  191. return pre + "[]const u8"
  192. elif is_const_struct_ptr(arg_type):
  193. # not a bug, pass const structs by value
  194. return pre + f"{as_title_case(extract_ptr_type(arg_type))}"
  195. elif is_prim_ptr(arg_type):
  196. return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}"
  197. elif is_const_prim_ptr(arg_type):
  198. return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}"
  199. else:
  200. return arg_prefix + "??? (as_zig_arg_type)"
  201. # get C-style arguments of a function pointer as string
  202. def funcptr_args_c(field_type):
  203. tokens = field_type[field_type.index('(*)')+4:-1].split(',')
  204. s = ""
  205. for token in tokens:
  206. arg_type = token.strip();
  207. if s != "":
  208. s += ", "
  209. c_arg = as_extern_c_arg_type(arg_type)
  210. if (c_arg == "void"):
  211. return ""
  212. else:
  213. s += c_arg
  214. return s
  215. # get C-style result of a function pointer as string
  216. def funcptr_res_c(field_type):
  217. res_type = field_type[:field_type.index('(*)')].strip()
  218. if res_type == 'void':
  219. return 'void'
  220. elif is_const_void_ptr(res_type):
  221. return '?*const c_void'
  222. else:
  223. return '???'
  224. def funcdecl_args_c(decl):
  225. s = ""
  226. for param_decl in decl['params']:
  227. if s != "":
  228. s += ", "
  229. arg_type = param_decl['type']
  230. s += as_extern_c_arg_type(arg_type)
  231. return s
  232. def funcdecl_args_zig(decl):
  233. s = ""
  234. for param_decl in decl['params']:
  235. if s != "":
  236. s += ", "
  237. arg_name = param_decl['name']
  238. arg_type = param_decl['type']
  239. s += f"{as_zig_arg_type(f'{arg_name}: ', arg_type)}"
  240. return s
  241. def funcdecl_res_c(decl):
  242. decl_type = decl['type']
  243. res_type = decl_type[:decl_type.index('(')].strip()
  244. return as_extern_c_arg_type(res_type)
  245. def funcdecl_res_zig(decl):
  246. decl_type = decl['type']
  247. res_type = decl_type[:decl_type.index('(')].strip()
  248. zig_res_type = as_zig_arg_type(None, res_type)
  249. if zig_res_type == "":
  250. zig_res_type = "void"
  251. return zig_res_type
  252. def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True):
  253. struct_name = decl['name']
  254. zig_type = struct_name if use_raw_name else as_title_case(struct_name)
  255. l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{")
  256. #l(f" pub fn init(options: anytype) {zig_type} {{ var item: {zig_type} = .{{ }}; init_with(&item, options); return item; }}")
  257. for field in decl['fields']:
  258. field_name = field['name']
  259. field_type = field['type']
  260. field_type = check_struct_field_type_override(struct_name, field_name, field_type)
  261. if is_prim_type(field_type):
  262. l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
  263. elif is_struct_type(field_type):
  264. l(f" {field_name}: {as_title_case(field_type)} = .{{ }},")
  265. elif is_enum_type(field_type):
  266. l(f" {field_name}: {as_title_case(field_type)} = .{enum_default_item(field_type)},")
  267. elif is_string_ptr(field_type):
  268. l(f" {field_name}: [*c]const u8 = null,")
  269. elif is_const_void_ptr(field_type):
  270. l(f" {field_name}: ?*const c_void = null,")
  271. elif is_void_ptr(field_type):
  272. l(f" {field_name}: ?*c_void = null,")
  273. elif is_const_prim_ptr(field_type):
  274. l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,")
  275. elif is_func_ptr(field_type):
  276. if callconvc_funcptrs:
  277. l(f" {field_name}: ?fn({funcptr_args_c(field_type)}) callconv(.C) {funcptr_res_c(field_type)} = null,")
  278. else:
  279. l(f" {field_name}: ?fn({funcptr_args_c(field_type)}) {funcptr_res_c(field_type)} = null,")
  280. elif is_1d_array_type(field_type):
  281. array_type = extract_array_type(field_type)
  282. array_nums = extract_array_nums(field_type)
  283. if is_prim_type(array_type) or is_struct_type(array_type):
  284. if is_prim_type(array_type):
  285. zig_type = as_zig_prim_type(array_type)
  286. def_val = type_default_value(array_type)
  287. else:
  288. zig_type = as_title_case(array_type)
  289. def_val = ".{}"
  290. t0 = f"[{array_nums[0]}]{zig_type}"
  291. t0_slice = f"[]const {zig_type}"
  292. t1 = f"[_]{zig_type}"
  293. l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},")
  294. elif is_const_void_ptr(array_type):
  295. l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},")
  296. else:
  297. l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]")
  298. elif is_2d_array_type(field_type):
  299. array_type = extract_array_type(field_type)
  300. array_nums = extract_array_nums(field_type)
  301. if is_prim_type(array_type):
  302. l(f"// FIXME: 2D array with primitive type: {field_name}")
  303. elif is_struct_type(array_type):
  304. zig_type = as_title_case(array_type)
  305. t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}"
  306. l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ .{{ }} }}**{array_nums[1]}}}**{array_nums[0]},")
  307. else:
  308. l(f"// FIXME: {field_name}: {field_type};")
  309. l("};")
  310. def gen_consts(decl, prefix):
  311. for item in decl['items']:
  312. l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};")
  313. def gen_enum(decl, prefix):
  314. l(f"pub const {as_title_case(decl['name'])} = extern enum(i32) {{")
  315. for item in decl['items']:
  316. item_name = as_enum_item_name(item['name'])
  317. if item_name != "FORCE_U32":
  318. if 'value' in item:
  319. l(f" {item_name} = {item['value']},")
  320. else:
  321. l(f" {item_name},")
  322. l("};")
  323. def gen_func_c(decl, prefix):
  324. l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl)}) {funcdecl_res_c(decl)};")
  325. def gen_func_zig(decl, prefix):
  326. c_func_name = decl['name']
  327. zig_func_name = as_camel_case(decl['name'])
  328. zig_res_type = funcdecl_res_zig(decl)
  329. l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl)}) {funcdecl_res_zig(decl)} {{")
  330. if zig_res_type != 'void':
  331. s = f" return {c_func_name}("
  332. else:
  333. s = f" {c_func_name}("
  334. for i, param_decl in enumerate(decl['params']):
  335. if i > 0:
  336. s += ", "
  337. arg_name = param_decl['name']
  338. arg_type = param_decl['type']
  339. if is_const_struct_ptr(arg_type):
  340. s += "&" + arg_name
  341. else:
  342. s += arg_name
  343. s += ");"
  344. l(s)
  345. l("}")
  346. def pre_parse(inp):
  347. global struct_types
  348. global enum_types
  349. for decl in inp['decls']:
  350. kind = decl['kind']
  351. if kind == 'struct':
  352. struct_types.append(decl['name'])
  353. elif kind == 'enum':
  354. enum_name = decl['name']
  355. enum_types.append(enum_name)
  356. enum_items[enum_name] = []
  357. for item in decl['items']:
  358. enum_items[enum_name].append(as_enum_item_name(item['name']))
  359. def gen_module(inp):
  360. l('// machine generated, do not edit')
  361. l('')
  362. pre_parse(inp)
  363. prefix = inp['prefix']
  364. for decl in inp['decls']:
  365. kind = decl['kind']
  366. if kind == 'struct':
  367. gen_struct(decl, prefix)
  368. elif kind == 'consts':
  369. gen_consts(decl, prefix)
  370. elif kind == 'enum':
  371. gen_enum(decl, prefix)
  372. elif kind == 'func':
  373. gen_func_c(decl, prefix)
  374. gen_func_zig(decl, prefix)
  375. def prepare():
  376. if not os.path.isdir('sokol-zig/src/sokol'):
  377. os.makedirs('sokol-zig/src/sokol')
  378. def gen(c_header_path, input_ir):
  379. reset_globals()
  380. gen_module(input_ir)
  381. shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/{os.path.basename(c_header_path)}')
  382. output_path = f"sokol-zig/src/sokol/{input_ir['module']}.zig"
  383. with open(output_path, 'w', newline='\n') as f_outp:
  384. f_outp.write(out_lines)