gen_nim.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. #-------------------------------------------------------------------------------
  2. # Read output of gen_json.py and generate Zig language bindings.
  3. #
  4. # Nim coding style:
  5. # - types and constants are PascalCase
  6. # - functions, parameters, and fields are camelCase
  7. #-------------------------------------------------------------------------------
  8. import gen_ir
  9. import json, re, os, shutil
  10. module_names = {
  11. 'sg_': 'gfx',
  12. 'sapp_': 'app',
  13. 'stm_': 'time',
  14. 'saudio_': 'audio',
  15. 'sgl_': 'gl',
  16. 'sdtx_': 'debugtext',
  17. 'sshape_': 'shape',
  18. }
  19. c_source_paths = {
  20. 'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c',
  21. 'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c',
  22. 'stm_': 'sokol-nim/src/sokol/c/sokol_time.c',
  23. 'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c',
  24. 'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c',
  25. 'sdtx_': 'sokol-nim/src/sokol/c/sokol_debugtext.c',
  26. 'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c',
  27. }
  28. func_name_ignores = [
  29. 'sdtx_printf',
  30. 'sdtx_vprintf',
  31. ]
  32. func_name_overrides = {
  33. 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
  34. 'sgl_deg': 'sgl_as_degrees',
  35. 'sgl_rad': 'sgl_as_radians',
  36. }
  37. struct_field_type_overrides = {
  38. 'sg_context_desc.color_format': 'int',
  39. 'sg_context_desc.depth_format': 'int',
  40. }
  41. prim_types = {
  42. 'int': 'int32',
  43. 'bool': 'bool',
  44. 'char': 'char',
  45. 'int8_t': 'int8',
  46. 'uint8_t': 'uint8',
  47. 'int16_t': 'int16',
  48. 'uint16_t': 'uint16',
  49. 'int32_t': 'int32',
  50. 'uint32_t': 'uint32',
  51. 'int64_t': 'int64',
  52. 'uint64_t': 'uint64',
  53. 'float': 'float32',
  54. 'double': 'float64',
  55. 'uintptr_t': 'uint',
  56. 'intptr_t': 'int',
  57. 'size_t': 'int',
  58. }
  59. prim_defaults = {
  60. 'int': '0',
  61. 'bool': 'false',
  62. 'int8_t': '0',
  63. 'uint8_t': '0',
  64. 'int16_t': '0',
  65. 'uint16_t': '0',
  66. 'int32_t': '0',
  67. 'uint32_t': '0',
  68. 'int64_t': '0',
  69. 'uint64_t': '0',
  70. 'float': '0.0',
  71. 'double': '0.0',
  72. 'uintptr_t': '0',
  73. 'intptr_t': '0',
  74. 'size_t': '0'
  75. }
  76. struct_types = []
  77. enum_types = []
  78. enum_items = {}
  79. out_lines = ''
  80. def reset_globals():
  81. global struct_types
  82. global enum_types
  83. global enum_items
  84. global out_lines
  85. struct_types = []
  86. enum_types = []
  87. enum_items = {}
  88. out_lines = ''
  89. re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
  90. re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
  91. def l(s):
  92. global out_lines
  93. out_lines += s + '\n'
  94. def as_nim_prim_type(s):
  95. return prim_types[s]
  96. # prefix_bla_blub(_t) => (dep.)BlaBlub
  97. def as_nim_struct_type(s, prefix):
  98. parts = s.lower().split('_')
  99. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  100. for part in parts[1:]:
  101. if (part != 't'):
  102. outp += part.capitalize()
  103. return outp
  104. # prefix_bla_blub(_t) => (dep.)BlaBlub
  105. def as_nim_enum_type(s, prefix):
  106. parts = s.lower().split('_')
  107. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  108. for part in parts[1:]:
  109. if (part != 't'):
  110. outp += part.capitalize()
  111. return outp
  112. # prefix_bla_blub(_t) => (dep.)BlaBlub
  113. def as_nim_const_type(s, prefix):
  114. parts = s.lower().split('_')
  115. outp = '' if s.startswith(prefix) else f'{parts[0]}.'
  116. for part in parts[1:]:
  117. if (part != 't'):
  118. outp += part.capitalize()
  119. return outp
  120. def check_struct_field_type_override(struct_name, field_name, orig_type):
  121. s = f"{struct_name}.{field_name}"
  122. if s in struct_field_type_overrides:
  123. return struct_field_type_overrides[s]
  124. else:
  125. return orig_type
  126. def check_func_name_ignore(func_name):
  127. return func_name in func_name_ignores
  128. def check_func_name_override(func_name):
  129. if func_name in func_name_overrides:
  130. return func_name_overrides[func_name]
  131. else:
  132. return func_name
  133. def trim_prefix(s, prefix):
  134. outp = s;
  135. if outp.lower().startswith(prefix.lower()):
  136. outp = outp[len(prefix):]
  137. return outp
  138. # PREFIX_BLA_BLUB to bla_blub
  139. def as_snake_case(s, prefix = ""):
  140. return trim_prefix(s, prefix).lower()
  141. # prefix_bla_blub => blaBlub
  142. def as_camel_case(s, prefix = ""):
  143. parts = trim_prefix(s, prefix).lower().split('_')
  144. outp = parts[0]
  145. for part in parts[1:]:
  146. outp += part.capitalize()
  147. return outp
  148. # prefix_bla_blub => BlaBlub
  149. def as_pascal_case(s, prefix):
  150. parts = trim_prefix(s, prefix).lower().split('_')
  151. outp = ""
  152. for part in parts:
  153. outp += part.capitalize()
  154. return outp
  155. # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
  156. def as_enum_item_name(s):
  157. outp = s
  158. if outp.startswith('_'):
  159. outp = outp[1:]
  160. parts = outp.lower().split('_')[2:]
  161. outp = ""
  162. for part in parts:
  163. outp += part.capitalize()
  164. if outp[0].isdigit():
  165. outp = 'N' + outp
  166. return outp
  167. def enum_default_item(enum_name):
  168. return enum_items[enum_name][0]
  169. def is_prim_type(s):
  170. return s in prim_types
  171. def is_struct_type(s):
  172. return s in struct_types
  173. def is_enum_type(s):
  174. return s in enum_types
  175. def is_string_ptr(s):
  176. return s == "const char *"
  177. def is_const_void_ptr(s):
  178. return s == "const void *"
  179. def is_void_ptr(s):
  180. return s == "void *"
  181. def is_const_prim_ptr(s):
  182. for prim_type in prim_types:
  183. if s == f"const {prim_type} *":
  184. return True
  185. return False
  186. def is_prim_ptr(s):
  187. for prim_type in prim_types:
  188. if s == f"{prim_type} *":
  189. return True
  190. return False
  191. def is_const_struct_ptr(s):
  192. for struct_type in struct_types:
  193. if s == f"const {struct_type} *":
  194. return True
  195. return False
  196. def is_func_ptr(s):
  197. return '(*)' in s
  198. def is_1d_array_type(s):
  199. return re_1d_array.match(s)
  200. def is_2d_array_type(s):
  201. return re_2d_array.match(s)
  202. def type_default_value(s):
  203. return prim_defaults[s]
  204. def extract_array_type(s):
  205. return s[:s.index('[')].strip()
  206. def extract_array_nums(s):
  207. return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
  208. def extract_ptr_type(s):
  209. tokens = s.split()
  210. if tokens[0] == 'const':
  211. return tokens[1]
  212. else:
  213. return tokens[0]
  214. def as_extern_c_arg_type(arg_type, prefix):
  215. if arg_type == "void":
  216. return "void"
  217. elif is_prim_type(arg_type):
  218. return as_nim_prim_type(arg_type)
  219. elif is_struct_type(arg_type):
  220. return as_nim_struct_type(arg_type, prefix)
  221. elif is_enum_type(arg_type):
  222. return as_nim_enum_type(arg_type, prefix)
  223. elif is_void_ptr(arg_type):
  224. return "pointer"
  225. elif is_const_void_ptr(arg_type):
  226. return "pointer"
  227. elif is_string_ptr(arg_type):
  228. return "cstring"
  229. elif is_const_struct_ptr(arg_type):
  230. return f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
  231. elif is_prim_ptr(arg_type):
  232. return f"[*c] {as_nim_prim_type(extract_ptr_type(arg_type))}"
  233. elif is_const_prim_ptr(arg_type):
  234. return f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
  235. else:
  236. return '??? (as_extern_c_arg_type)'
  237. def as_nim_arg_type(arg_prefix, arg_type, prefix):
  238. # NOTE: if arg_prefix is None, the result is used as return value
  239. pre = "" if arg_prefix is None else arg_prefix
  240. if arg_type == "void":
  241. if arg_prefix is None:
  242. return "void"
  243. else:
  244. return ""
  245. elif is_prim_type(arg_type):
  246. return pre + as_nim_prim_type(arg_type)
  247. elif is_struct_type(arg_type):
  248. return pre + as_nim_struct_type(arg_type, prefix)
  249. elif is_enum_type(arg_type):
  250. return pre + as_nim_enum_type(arg_type, prefix)
  251. elif is_void_ptr(arg_type):
  252. return pre + "pointer"
  253. elif is_const_void_ptr(arg_type):
  254. return pre + "pointer"
  255. elif is_string_ptr(arg_type):
  256. return pre + "cstring"
  257. elif is_const_struct_ptr(arg_type):
  258. return pre + f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
  259. elif is_prim_ptr(arg_type):
  260. return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
  261. elif is_const_prim_ptr(arg_type):
  262. return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
  263. else:
  264. return arg_prefix + "??? (as_nim_arg_type)"
  265. # get C-style arguments of a function pointer as string
  266. def funcptr_args_c(field_type, prefix):
  267. tokens = field_type[field_type.index('(*)')+4:-1].split(',')
  268. s = ""
  269. n = 0
  270. for token in tokens:
  271. n += 1
  272. arg_type = token.strip()
  273. if s != "":
  274. s += ", "
  275. c_arg = f"a{n}:" + as_extern_c_arg_type(arg_type, prefix)
  276. if (c_arg == "void"):
  277. return ""
  278. else:
  279. s += c_arg
  280. if s == "a1:void":
  281. s = ""
  282. return s
  283. # get C-style result of a function pointer as string
  284. def funcptr_res_c(field_type):
  285. res_type = field_type[:field_type.index('(*)')].strip()
  286. if res_type == 'void':
  287. return ''
  288. elif is_const_void_ptr(res_type):
  289. return ':pointer'
  290. else:
  291. return '???'
  292. def funcdecl_args_c(decl, prefix):
  293. s = ""
  294. for param_decl in decl['params']:
  295. if s != "":
  296. s += ", "
  297. arg_type = param_decl['type']
  298. s += as_extern_c_arg_type(arg_type, prefix)
  299. return s
  300. def funcdecl_args_nim(decl, prefix):
  301. s = ""
  302. for param_decl in decl['params']:
  303. if s != "":
  304. s += ", "
  305. arg_name = param_decl['name']
  306. arg_type = param_decl['type']
  307. s += f"{as_nim_arg_type(f'{arg_name}:', arg_type, prefix)}"
  308. return s
  309. def funcdecl_res_c(decl, prefix):
  310. decl_type = decl['type']
  311. res_type = decl_type[:decl_type.index('(')].strip()
  312. return as_extern_c_arg_type(res_type, prefix)
  313. def funcdecl_res_nim(decl, prefix):
  314. decl_type = decl['type']
  315. res_type = decl_type[:decl_type.index('(')].strip()
  316. nim_res_type = as_nim_arg_type(None, res_type, prefix)
  317. if nim_res_type == "":
  318. nim_res_type = "void"
  319. return nim_res_type
  320. def gen_struct(decl, prefix, use_raw_name=False):
  321. struct_name = decl['name']
  322. nim_type = struct_name if use_raw_name else as_nim_struct_type(struct_name, prefix)
  323. l(f"type {nim_type}* = object")
  324. isPublic = True
  325. for field in decl['fields']:
  326. field_name = field['name']
  327. if field_name == "__pad":
  328. # FIXME: these should be guarded by SOKOL_ZIG_BINDINGS, but aren't?
  329. continue
  330. isPublic = not field_name.startswith("_")
  331. field_name = as_camel_case(field_name, "_")
  332. if field_name == "ptr":
  333. field_name = "source"
  334. if field_name == "ref":
  335. field_name = "`ref`"
  336. if field_name == "type":
  337. field_name = "`type`"
  338. if isPublic:
  339. field_name += "*"
  340. field_type = field['type']
  341. field_type = check_struct_field_type_override(struct_name, field_name, field_type)
  342. if is_prim_type(field_type):
  343. l(f" {field_name}:{as_nim_prim_type(field_type)}")
  344. elif is_struct_type(field_type):
  345. l(f" {field_name}:{as_nim_struct_type(field_type, prefix)}")
  346. elif is_enum_type(field_type):
  347. l(f" {field_name}:{as_nim_enum_type(field_type, prefix)}")
  348. elif is_string_ptr(field_type):
  349. l(f" {field_name}:cstring")
  350. elif is_const_void_ptr(field_type):
  351. l(f" {field_name}:pointer")
  352. elif is_void_ptr(field_type):
  353. l(f" {field_name}:pointer")
  354. elif is_const_prim_ptr(field_type):
  355. l(f" {field_name}:ptr {as_nim_prim_type(extract_ptr_type(field_type))}")
  356. elif is_func_ptr(field_type):
  357. l(f" {field_name}:proc({funcptr_args_c(field_type, prefix)}){funcptr_res_c(field_type)} {{.cdecl.}}")
  358. elif is_1d_array_type(field_type):
  359. array_type = extract_array_type(field_type)
  360. array_nums = extract_array_nums(field_type)
  361. if is_prim_type(array_type) or is_struct_type(array_type):
  362. if is_prim_type(array_type):
  363. nim_type = as_nim_prim_type(array_type)
  364. elif is_struct_type(array_type):
  365. nim_type = as_nim_struct_type(array_type, prefix)
  366. elif is_enum_type(array_type):
  367. nim_type = as_nim_enum_type(array_type, prefix)
  368. else:
  369. nim_type = '??? (array type)'
  370. t0 = f"array[{array_nums[0]}, {nim_type}]"
  371. t0_slice = f"[]const {nim_type}"
  372. t1 = f"[_]{nim_type}"
  373. l(f" {field_name}:{t0}")
  374. elif is_const_void_ptr(array_type):
  375. l(f" {field_name}:array[{array_nums[0]}, pointer]")
  376. else:
  377. l(f"// FIXME: ??? array {field_name}:{field_type} => {array_type} [{array_nums[0]}]")
  378. elif is_2d_array_type(field_type):
  379. array_type = extract_array_type(field_type)
  380. array_nums = extract_array_nums(field_type)
  381. if is_prim_type(array_type):
  382. nim_type = as_nim_prim_type(array_type)
  383. def_val = type_default_value(array_type)
  384. elif is_struct_type(array_type):
  385. nim_type = as_nim_struct_type(array_type, prefix)
  386. def_val = ".{ }"
  387. else:
  388. nim_type = "???"
  389. def_val = "???"
  390. t0 = f"array[{array_nums[0]}, array[{array_nums[1]}, {nim_type}]]"
  391. l(f" {field_name}:{t0}")
  392. else:
  393. l(f"// FIXME: {field_name}:{field_type};")
  394. l("")
  395. def gen_consts(decl, prefix):
  396. l("const")
  397. for item in decl['items']:
  398. l(f" {trim_prefix(item['name'], prefix)}* = {item['value']}")
  399. l("")
  400. def gen_enum(decl, prefix):
  401. item_names_by_value = {}
  402. value = -1
  403. hasForceU32 = False
  404. hasExplicitValues = False
  405. for item in decl['items']:
  406. itemName = item['name']
  407. if itemName.endswith("_FORCE_U32"):
  408. hasForceU32 = True
  409. elif itemName.endswith("_NUM"):
  410. continue
  411. else:
  412. if 'value' in item:
  413. hasExplicitValues = True
  414. value = int(item['value'])
  415. else:
  416. value += 1
  417. item_names_by_value[value] = as_enum_item_name(item['name']);
  418. if hasForceU32:
  419. l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure, size:4.}} = enum")
  420. else:
  421. l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure.}} = enum")
  422. if hasExplicitValues:
  423. # Nim requires explicit enum values to be declared in ascending order
  424. for value in sorted(item_names_by_value):
  425. name = item_names_by_value[value]
  426. l(f" {name} = {value},")
  427. else:
  428. for name in item_names_by_value.values():
  429. l(f" {name},")
  430. l("")
  431. def gen_func_nim(decl, prefix):
  432. c_func_name = decl['name']
  433. nim_func_name = as_camel_case(decl['name'], prefix)
  434. nim_res_type = funcdecl_res_nim(decl, prefix)
  435. l(f"proc {nim_func_name}*({funcdecl_args_nim(decl, prefix)}):{funcdecl_res_nim(decl, prefix)} {{.cdecl, importc:\"{decl['name']}\".}}")
  436. l("")
  437. def pre_parse(inp):
  438. global struct_types
  439. global enum_types
  440. for decl in inp['decls']:
  441. kind = decl['kind']
  442. if kind == 'struct':
  443. struct_types.append(decl['name'])
  444. elif kind == 'enum':
  445. enum_name = decl['name']
  446. enum_types.append(enum_name)
  447. enum_items[enum_name] = []
  448. for item in decl['items']:
  449. enum_items[enum_name].append(as_enum_item_name(item['name']))
  450. def gen_imports(inp, dep_prefixes):
  451. for dep_prefix in dep_prefixes:
  452. dep_module_name = module_names[dep_prefix]
  453. l(f'import {dep_module_name}')
  454. l('')
  455. def gen_module(inp, dep_prefixes):
  456. l('## machine generated, do not edit')
  457. l('')
  458. gen_imports(inp, dep_prefixes)
  459. pre_parse(inp)
  460. prefix = inp['prefix']
  461. for decl in inp['decls']:
  462. if not decl['is_dep']:
  463. kind = decl['kind']
  464. if kind == 'consts':
  465. gen_consts(decl, prefix)
  466. elif kind == 'enum':
  467. gen_enum(decl, prefix)
  468. elif kind == 'struct':
  469. gen_struct(decl, prefix)
  470. elif kind == 'func':
  471. if not check_func_name_ignore(decl['name']):
  472. gen_func_nim(decl, prefix)
  473. def prepare():
  474. print('Generating nim bindings:')
  475. if not os.path.isdir('sokol-nim/src/sokol'):
  476. os.makedirs('sokol-nim/src/sokol')
  477. if not os.path.isdir('sokol-nim/src/sokol/c'):
  478. os.makedirs('sokol-nim/src/sokol/c')
  479. def gen(c_header_path, c_prefix, dep_c_prefixes):
  480. global out_lines
  481. module_name = module_names[c_prefix]
  482. c_source_path = c_source_paths[c_prefix]
  483. print(f' {c_header_path} => {module_name}')
  484. reset_globals()
  485. shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}')
  486. ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
  487. gen_module(ir, dep_c_prefixes)
  488. output_path = f"sokol-nim/src/sokol/{ir['module']}.nim"
  489. ## some changes for readability
  490. out_lines = out_lines.replace("PixelformatInfo", "PixelFormatInfo")
  491. out_lines = out_lines.replace(" Dontcare,", " DontCare,")
  492. out_lines = out_lines.replace(" Vertexbuffer,", " VertexBuffer,")
  493. out_lines = out_lines.replace(" Indexbuffer,", " IndexBuffer,")
  494. out_lines = out_lines.replace(" N2d,", " Plane,")
  495. out_lines = out_lines.replace(" N3d,", " Volume,")
  496. out_lines = out_lines.replace(" Vs,", " Vertex,")
  497. out_lines = out_lines.replace(" Fs,", " Fragment,")
  498. ## include extensions in generated code
  499. l("# Nim-specific API extensions")
  500. l(f"include nim/{ir['module']}")
  501. with open(output_path, 'w', newline='\n') as f_outp:
  502. f_outp.write(out_lines)