gen_zig.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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 gen_ir
  10. import json, re, os, shutil
  11. module_names = {
  12. 'sg_': 'gfx',
  13. 'sapp_': 'app',
  14. 'stm_': 'time',
  15. 'saudio_': 'audio',
  16. 'sgl_': 'gl',
  17. 'sdtx_': 'debugtext',
  18. 'sshape_': 'shape',
  19. }
  20. c_source_paths = {
  21. 'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c',
  22. 'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c',
  23. 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c',
  24. 'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c',
  25. 'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c',
  26. 'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c',
  27. 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c',
  28. }
  29. name_ignores = [
  30. 'sdtx_printf',
  31. 'sdtx_vprintf',
  32. 'sg_install_trace_hooks',
  33. 'sg_trace_hooks',
  34. ]
  35. name_overrides = {
  36. 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
  37. 'sgl_deg': 'sgl_as_degrees',
  38. 'sgl_rad': 'sgl_as_radians'
  39. }
  40. # NOTE: syntax for function results: "func_name.RESULT"
  41. type_overrides = {
  42. 'sg_context_desc.color_format': 'int',
  43. 'sg_context_desc.depth_format': 'int',
  44. 'sg_apply_uniforms.ub_index': 'uint32_t',
  45. 'sg_draw.base_element': 'uint32_t',
  46. 'sg_draw.num_elements': 'uint32_t',
  47. 'sg_draw.num_instances': 'uint32_t',
  48. 'sshape_element_range_t.base_element': 'uint32_t',
  49. 'sshape_element_range_t.num_elements': 'uint32_t',
  50. 'sdtx_font.font_index': 'uint32_t',
  51. }
  52. prim_types = {
  53. 'int': 'i32',
  54. 'bool': 'bool',
  55. 'char': 'u8',
  56. 'int8_t': 'i8',
  57. 'uint8_t': 'u8',
  58. 'int16_t': 'i16',
  59. 'uint16_t': 'u16',
  60. 'int32_t': 'i32',
  61. 'uint32_t': 'u32',
  62. 'int64_t': 'i64',
  63. 'uint64_t': 'u64',
  64. 'float': 'f32',
  65. 'double': 'f64',
  66. 'uintptr_t': 'usize',
  67. 'intptr_t': 'isize',
  68. 'size_t': 'usize'
  69. }
  70. prim_defaults = {
  71. 'int': '0',
  72. 'bool': 'false',
  73. 'int8_t': '0',
  74. 'uint8_t': '0',
  75. 'int16_t': '0',
  76. 'uint16_t': '0',
  77. 'int32_t': '0',
  78. 'uint32_t': '0',
  79. 'int64_t': '0',
  80. 'uint64_t': '0',
  81. 'float': '0.0',
  82. 'double': '0.0',
  83. 'uintptr_t': '0',
  84. 'intptr_t': '0',
  85. 'size_t': '0'
  86. }
  87. struct_types = []
  88. enum_types = []
  89. enum_items = {}
  90. out_lines = ''
  91. def reset_globals():
  92. global struct_types
  93. global enum_types
  94. global enum_items
  95. global out_lines
  96. struct_types = []
  97. enum_types = []
  98. enum_items = {}
  99. out_lines = ''
  100. re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
  101. re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
  102. def l(s):
  103. global out_lines
  104. out_lines += s + '\n'
  105. def as_zig_prim_type(s):
  106. return prim_types[s]
  107. # prefix_bla_blub(_t) => (dep.)BlaBlub
  108. def as_zig_struct_type(s, prefix):
  109. parts = s.lower().split('_')
  110. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  111. for part in parts[1:]:
  112. if (part != 't'):
  113. outp += part.capitalize()
  114. return outp
  115. # prefix_bla_blub(_t) => (dep.)BlaBlub
  116. def as_zig_enum_type(s, prefix):
  117. parts = s.lower().split('_')
  118. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  119. for part in parts[1:]:
  120. if (part != 't'):
  121. outp += part.capitalize()
  122. return outp
  123. def check_type_override(func_or_struct_name, field_or_arg_name, orig_type):
  124. s = f"{func_or_struct_name}.{field_or_arg_name}"
  125. if s in type_overrides:
  126. return type_overrides[s]
  127. else:
  128. return orig_type
  129. def check_name_override(name):
  130. if name in name_overrides:
  131. return name_overrides[name]
  132. else:
  133. return name
  134. def check_name_ignore(name):
  135. return name in name_ignores
  136. # PREFIX_BLA_BLUB to bla_blub
  137. def as_snake_case(s, prefix):
  138. outp = s.lower()
  139. if outp.startswith(prefix):
  140. outp = outp[len(prefix):]
  141. return outp
  142. # prefix_bla_blub => blaBlub
  143. def as_camel_case(s):
  144. parts = s.lower().split('_')[1:]
  145. outp = parts[0]
  146. for part in parts[1:]:
  147. outp += part.capitalize()
  148. return outp
  149. # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
  150. def as_enum_item_name(s):
  151. outp = s
  152. if outp.startswith('_'):
  153. outp = outp[1:]
  154. parts = outp.split('_')[2:]
  155. outp = '_'.join(parts)
  156. if outp[0].isdigit():
  157. outp = '_' + outp
  158. return outp
  159. def enum_default_item(enum_name):
  160. return enum_items[enum_name][0]
  161. def is_prim_type(s):
  162. return s in prim_types
  163. def is_struct_type(s):
  164. return s in struct_types
  165. def is_enum_type(s):
  166. return s in enum_types
  167. def is_string_ptr(s):
  168. return s == "const char *"
  169. def is_const_void_ptr(s):
  170. return s == "const void *"
  171. def is_void_ptr(s):
  172. return s == "void *"
  173. def is_const_prim_ptr(s):
  174. for prim_type in prim_types:
  175. if s == f"const {prim_type} *":
  176. return True
  177. return False
  178. def is_prim_ptr(s):
  179. for prim_type in prim_types:
  180. if s == f"{prim_type} *":
  181. return True
  182. return False
  183. def is_const_struct_ptr(s):
  184. for struct_type in struct_types:
  185. if s == f"const {struct_type} *":
  186. return True
  187. return False
  188. def is_func_ptr(s):
  189. return '(*)' in s
  190. def is_1d_array_type(s):
  191. return re_1d_array.match(s)
  192. def is_2d_array_type(s):
  193. return re_2d_array.match(s)
  194. def type_default_value(s):
  195. return prim_defaults[s]
  196. def extract_array_type(s):
  197. return s[:s.index('[')].strip()
  198. def extract_array_nums(s):
  199. return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
  200. def extract_ptr_type(s):
  201. tokens = s.split()
  202. if tokens[0] == 'const':
  203. return tokens[1]
  204. else:
  205. return tokens[0]
  206. def as_extern_c_arg_type(arg_type, prefix):
  207. if arg_type == "void":
  208. return "void"
  209. elif is_prim_type(arg_type):
  210. return as_zig_prim_type(arg_type)
  211. elif is_struct_type(arg_type):
  212. return as_zig_struct_type(arg_type, prefix)
  213. elif is_enum_type(arg_type):
  214. return as_zig_enum_type(arg_type, prefix)
  215. elif is_void_ptr(arg_type):
  216. return "?*c_void"
  217. elif is_const_void_ptr(arg_type):
  218. return "?*const c_void"
  219. elif is_string_ptr(arg_type):
  220. return "[*c]const u8"
  221. elif is_const_struct_ptr(arg_type):
  222. return f"[*c]const {as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
  223. elif is_prim_ptr(arg_type):
  224. return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}"
  225. elif is_const_prim_ptr(arg_type):
  226. return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}"
  227. else:
  228. return '??? (as_extern_c_arg_type)'
  229. def as_zig_arg_type(arg_prefix, arg_type, prefix):
  230. # NOTE: if arg_prefix is None, the result is used as return value
  231. pre = "" if arg_prefix is None else arg_prefix
  232. if arg_type == "void":
  233. if arg_prefix is None:
  234. return "void"
  235. else:
  236. return ""
  237. elif is_prim_type(arg_type):
  238. return pre + as_zig_prim_type(arg_type)
  239. elif is_struct_type(arg_type):
  240. return pre + as_zig_struct_type(arg_type, prefix)
  241. elif is_enum_type(arg_type):
  242. return pre + as_zig_enum_type(arg_type, prefix)
  243. elif is_void_ptr(arg_type):
  244. return pre + "?*c_void"
  245. elif is_const_void_ptr(arg_type):
  246. return pre + "?*const c_void"
  247. elif is_string_ptr(arg_type):
  248. return pre + "[:0]const u8"
  249. elif is_const_struct_ptr(arg_type):
  250. # not a bug, pass const structs by value
  251. return pre + f"{as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
  252. elif is_prim_ptr(arg_type):
  253. return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}"
  254. elif is_const_prim_ptr(arg_type):
  255. return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}"
  256. else:
  257. return arg_prefix + "??? (as_zig_arg_type)"
  258. # get C-style arguments of a function pointer as string
  259. def funcptr_args_c(field_type, prefix):
  260. tokens = field_type[field_type.index('(*)')+4:-1].split(',')
  261. s = ""
  262. for token in tokens:
  263. arg_type = token.strip()
  264. if s != "":
  265. s += ", "
  266. c_arg = as_extern_c_arg_type(arg_type, prefix)
  267. if (c_arg == "void"):
  268. return ""
  269. else:
  270. s += c_arg
  271. return s
  272. # get C-style result of a function pointer as string
  273. def funcptr_res_c(field_type):
  274. res_type = field_type[:field_type.index('(*)')].strip()
  275. if res_type == 'void':
  276. return 'void'
  277. elif is_const_void_ptr(res_type):
  278. return '?*const c_void'
  279. else:
  280. return '???'
  281. def funcdecl_args_c(decl, prefix):
  282. s = ""
  283. func_name = decl['name']
  284. for param_decl in decl['params']:
  285. if s != "":
  286. s += ", "
  287. param_name = param_decl['name']
  288. param_type = check_type_override(func_name, param_name, param_decl['type'])
  289. s += as_extern_c_arg_type(param_type, prefix)
  290. return s
  291. def funcdecl_args_zig(decl, prefix):
  292. s = ""
  293. func_name = decl['name']
  294. for param_decl in decl['params']:
  295. if s != "":
  296. s += ", "
  297. param_name = param_decl['name']
  298. param_type = check_type_override(func_name, param_name, param_decl['type'])
  299. s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}"
  300. return s
  301. def funcdecl_result_c(decl, prefix):
  302. func_name = decl['name']
  303. decl_type = decl['type']
  304. result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
  305. return as_extern_c_arg_type(result_type, prefix)
  306. def funcdecl_result_zig(decl, prefix):
  307. func_name = decl['name']
  308. decl_type = decl['type']
  309. result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
  310. zig_res_type = as_zig_arg_type(None, result_type, prefix)
  311. if zig_res_type == "":
  312. zig_res_type = "void"
  313. return zig_res_type
  314. def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True):
  315. struct_name = decl['name']
  316. zig_type = struct_name if use_raw_name else as_zig_struct_type(struct_name, prefix)
  317. l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{")
  318. for field in decl['fields']:
  319. field_name = field['name']
  320. field_type = field['type']
  321. field_type = check_type_override(struct_name, field_name, field_type)
  322. if is_prim_type(field_type):
  323. l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
  324. elif is_struct_type(field_type):
  325. l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{ }},")
  326. elif is_enum_type(field_type):
  327. l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},")
  328. elif is_string_ptr(field_type):
  329. l(f" {field_name}: [*c]const u8 = null,")
  330. elif is_const_void_ptr(field_type):
  331. l(f" {field_name}: ?*const c_void = null,")
  332. elif is_void_ptr(field_type):
  333. l(f" {field_name}: ?*c_void = null,")
  334. elif is_const_prim_ptr(field_type):
  335. l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,")
  336. elif is_func_ptr(field_type):
  337. if callconvc_funcptrs:
  338. l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_res_c(field_type)} = null,")
  339. else:
  340. l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) {funcptr_res_c(field_type)} = null,")
  341. elif is_1d_array_type(field_type):
  342. array_type = extract_array_type(field_type)
  343. array_nums = extract_array_nums(field_type)
  344. if is_prim_type(array_type) or is_struct_type(array_type):
  345. if is_prim_type(array_type):
  346. zig_type = as_zig_prim_type(array_type)
  347. def_val = type_default_value(array_type)
  348. elif is_struct_type(array_type):
  349. zig_type = as_zig_struct_type(array_type, prefix)
  350. def_val = '.{}'
  351. elif is_enum_type(array_type):
  352. zig_type = as_zig_enum_type(array_type, prefix)
  353. def_val = '.{}'
  354. else:
  355. zig_type = '??? (array type)'
  356. def_val = '???'
  357. t0 = f"[{array_nums[0]}]{zig_type}"
  358. t0_slice = f"[]const {zig_type}"
  359. t1 = f"[_]{zig_type}"
  360. l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},")
  361. elif is_const_void_ptr(array_type):
  362. l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},")
  363. else:
  364. l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]")
  365. elif is_2d_array_type(field_type):
  366. array_type = extract_array_type(field_type)
  367. array_nums = extract_array_nums(field_type)
  368. if is_prim_type(array_type):
  369. zig_type = as_zig_prim_type(array_type)
  370. def_val = type_default_value(array_type)
  371. elif is_struct_type(array_type):
  372. zig_type = as_zig_struct_type(array_type, prefix)
  373. def_val = ".{ }"
  374. else:
  375. zig_type = "???"
  376. def_val = "???"
  377. t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}"
  378. l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_nums[1]}}}**{array_nums[0]},")
  379. else:
  380. l(f"// FIXME: {field_name}: {field_type};")
  381. l("};")
  382. def gen_consts(decl, prefix):
  383. for item in decl['items']:
  384. l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};")
  385. def gen_enum(decl, prefix):
  386. l(f"pub const {as_zig_enum_type(decl['name'], prefix)} = extern enum(i32) {{")
  387. for item in decl['items']:
  388. item_name = as_enum_item_name(item['name'])
  389. if item_name != "FORCE_U32":
  390. if 'value' in item:
  391. l(f" {item_name} = {item['value']},")
  392. else:
  393. l(f" {item_name},")
  394. l("};")
  395. def gen_func_c(decl, prefix):
  396. l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};")
  397. def gen_func_zig(decl, prefix):
  398. c_func_name = decl['name']
  399. zig_func_name = as_camel_case(check_name_override(decl['name']))
  400. zig_res_type = funcdecl_result_zig(decl, prefix)
  401. l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
  402. if zig_res_type != 'void':
  403. s = f" return {c_func_name}("
  404. else:
  405. s = f" {c_func_name}("
  406. for i, param_decl in enumerate(decl['params']):
  407. if i > 0:
  408. s += ", "
  409. arg_name = param_decl['name']
  410. arg_type = param_decl['type']
  411. if is_const_struct_ptr(arg_type):
  412. s += f"&{arg_name}"
  413. elif is_string_ptr(arg_type):
  414. s += f"@ptrCast([*c]const u8,{arg_name})"
  415. else:
  416. s += arg_name
  417. s += ");"
  418. l(s)
  419. l("}")
  420. def pre_parse(inp):
  421. global struct_types
  422. global enum_types
  423. for decl in inp['decls']:
  424. kind = decl['kind']
  425. if kind == 'struct':
  426. struct_types.append(decl['name'])
  427. elif kind == 'enum':
  428. enum_name = decl['name']
  429. enum_types.append(enum_name)
  430. enum_items[enum_name] = []
  431. for item in decl['items']:
  432. enum_items[enum_name].append(as_enum_item_name(item['name']))
  433. def gen_imports(inp, dep_prefixes):
  434. for dep_prefix in dep_prefixes:
  435. dep_module_name = module_names[dep_prefix]
  436. l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");')
  437. l('')
  438. def gen_helpers(inp):
  439. if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
  440. l('// helper function to convert "anything" to a Range struct')
  441. l('pub fn asRange(val: anytype) Range {')
  442. l(' const type_info = @typeInfo(@TypeOf(val));')
  443. l(' switch (type_info) {')
  444. l(' .Pointer => {')
  445. l(' switch (type_info.Pointer.size) {')
  446. l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },')
  447. l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },')
  448. l(' else => @compileError("FIXME: Pointer type!"),')
  449. l(' }')
  450. l(' },')
  451. l(' .Struct, .Array => {')
  452. l(' return .{ .ptr = &val, .size = @sizeOf(@TypeOf(val)) };')
  453. l(' },')
  454. l(' else => {')
  455. l(' @compileError("Cannot convert to range!");')
  456. l(' }')
  457. l(' }')
  458. l('}')
  459. l('')
  460. if inp['prefix'] == 'sdtx_':
  461. l('// std.fmt compatible Writer')
  462. l('pub const Writer = struct {')
  463. l(' pub const Error = error { };')
  464. l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {')
  465. l(' for (bytes) |byte| {')
  466. l(' putc(byte);')
  467. l(' }')
  468. l(' }')
  469. l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {')
  470. l(' var i: u64 = 0;')
  471. l(' while (i < n): (i += 1) {')
  472. l(' putc(byte);')
  473. l(' }')
  474. l(' }')
  475. l('};')
  476. l('// std.fmt-style formatted print')
  477. l('pub fn print(comptime fmt: anytype, args: anytype) void {')
  478. l(' var writer: Writer = .{};')
  479. l(' @import("std").fmt.format(writer, fmt, args) catch {};')
  480. l('}')
  481. l('')
  482. def gen_module(inp, dep_prefixes):
  483. l('// machine generated, do not edit')
  484. l('')
  485. gen_imports(inp, dep_prefixes)
  486. gen_helpers(inp)
  487. pre_parse(inp)
  488. prefix = inp['prefix']
  489. for decl in inp['decls']:
  490. if not decl['is_dep']:
  491. kind = decl['kind']
  492. if kind == 'consts':
  493. gen_consts(decl, prefix)
  494. elif not check_name_ignore(decl['name']):
  495. if kind == 'struct':
  496. gen_struct(decl, prefix)
  497. elif kind == 'enum':
  498. gen_enum(decl, prefix)
  499. elif kind == 'func':
  500. gen_func_c(decl, prefix)
  501. gen_func_zig(decl, prefix)
  502. def prepare():
  503. print('Generating zig bindings:')
  504. if not os.path.isdir('sokol-zig/src/sokol'):
  505. os.makedirs('sokol-zig/src/sokol')
  506. if not os.path.isdir('sokol-zig/src/sokol/c'):
  507. os.makedirs('sokol-zig/src/sokol/c')
  508. def gen(c_header_path, c_prefix, dep_c_prefixes):
  509. module_name = module_names[c_prefix]
  510. c_source_path = c_source_paths[c_prefix]
  511. print(f' {c_header_path} => {module_name}')
  512. reset_globals()
  513. shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}')
  514. ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
  515. gen_module(ir, dep_c_prefixes)
  516. output_path = f"sokol-zig/src/sokol/{ir['module']}.zig"
  517. with open(output_path, 'w', newline='\n') as f_outp:
  518. f_outp.write(out_lines)