Updated ozz-animation to version 0.14.1.

This commit is contained in:
Martin Felis
2023-04-15 00:07:29 +02:00
parent eb70c06c57
commit 72bcf8a21b
29 changed files with 1389 additions and 1071 deletions
+2
View File
@@ -4,3 +4,5 @@
__pycache__/
sokol-nim/
sokol-zig/
sokol-odin/
sokol-rust/
+17 -7
View File
@@ -1,8 +1,10 @@
import os, gen_nim, gen_zig
import os, gen_nim, gen_zig, gen_odin, gen_rust
tasks = [
[ '../sokol_log.h', 'slog_', [] ],
[ '../sokol_gfx.h', 'sg_', [] ],
[ '../sokol_app.h', 'sapp_', [] ],
[ '../sokol_glue.h', 'sapp_sg', ['sg_'] ],
[ '../sokol_time.h', 'stm_', [] ],
[ '../sokol_audio.h', 'saudio_', [] ],
[ '../util/sokol_gl.h', 'sgl_', ['sg_'] ],
@@ -10,18 +12,26 @@ tasks = [
[ '../util/sokol_shape.h', 'sshape_', ['sg_'] ],
]
# Odin
gen_odin.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_odin.gen(c_header_path, main_prefix, dep_prefixes)
# Nim
gen_nim.prepare()
for task in tasks:
c_header_path = task[0]
main_prefix = task[1]
dep_prefixes = task[2]
[c_header_path, main_prefix, dep_prefixes] = task
gen_nim.gen(c_header_path, main_prefix, dep_prefixes)
# Zig
gen_zig.prepare()
for task in tasks:
c_header_path = task[0]
main_prefix = task[1]
dep_prefixes = task[2]
[c_header_path, main_prefix, dep_prefixes] = task
gen_zig.gen(c_header_path, main_prefix, dep_prefixes)
# Rust
gen_rust.prepare()
for task in tasks:
[c_header_path, main_prefix, dep_prefixes] = task
gen_rust.gen(c_header_path, main_prefix, dep_prefixes)
+7 -5
View File
@@ -59,11 +59,11 @@ def parse_enum(decl):
if 'inner' in item_decl:
const_expr = item_decl['inner'][0]
if const_expr['kind'] != 'ConstantExpr':
sys.exit(f"ERROR: Enum values must be a ConstantExpr ({decl['name']})")
if const_expr['valueCategory'] != 'rvalue':
sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' ({decl['name']})")
sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({decl['name']})")
sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
item['value'] = const_expr['inner'][0]['value']
if needs_value and 'value' not in item:
sys.exit(f"ERROR: anonymous enum items require an explicit value")
@@ -79,7 +79,7 @@ def parse_func(decl):
if 'inner' in decl:
for param in decl['inner']:
if param['kind'] != 'ParmVarDecl':
print(f"warning: ignoring func {decl['name']} (unsupported parameter type)")
print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)")
return None
outp_param = {}
outp_param['name'] = param['name']
@@ -119,4 +119,6 @@ def gen(header_path, source_path, module, main_prefix, dep_prefixes):
outp_decl['is_dep'] = is_dep
outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
outp['decls'].append(outp_decl)
with open(f'{module}.json', 'w') as f:
f.write(json.dumps(outp, indent=2));
return outp
+383 -334
View File
@@ -1,16 +1,19 @@
#-------------------------------------------------------------------------------
# Read output of gen_json.py and generate Zig language bindings.
# Generate Nim bindings
#
# Nim coding style:
# - types and constants are PascalCase
# - functions, parameters, and fields are camelCase
# - type identifiers are PascalCase, everything else is camelCase
# - reference: https://nim-lang.org/docs/nep1.html
#-------------------------------------------------------------------------------
import gen_ir
import json, re, os, shutil
import gen_util as util
import os, shutil, sys
module_names = {
'slog_': 'log',
'sg_': 'gfx',
'sapp_': 'app',
'sapp_sg': 'glue',
'stm_': 'time',
'saudio_': 'audio',
'sgl_': 'gl',
@@ -19,8 +22,10 @@ module_names = {
}
c_source_paths = {
'slog_': 'sokol-nim/src/sokol/c/sokol_log.c',
'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c',
'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c',
'sapp_sg': 'sokol-nim/src/sokol/c/sokol_glue.c',
'stm_': 'sokol-nim/src/sokol/c/sokol_time.c',
'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c',
'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c',
@@ -28,20 +33,57 @@ c_source_paths = {
'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c',
}
func_name_ignores = [
c_callbacks = [
'slog_func',
]
ignores = [
'sdtx_printf',
'sdtx_vprintf',
]
func_name_overrides = {
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians',
}
struct_field_type_overrides = {
overrides = {
'sgl_error': 'sgl_get_error',
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians',
'sg_context_desc.color_format': 'int',
'sg_context_desc.depth_format': 'int',
'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
'SG_BUFFERTYPE_VERTEXBUFFER': 'SG_BUFFERTYPE_VERTEX_BUFFER',
'SG_BUFFERTYPE_INDEXBUFFER': 'SG_BUFFERTYPE_INDEX_BUFFER',
'SG_ACTION_DONTCARE': 'SG_ACTION_DONT_CARE',
'ptr': 'addr', # range ptr
'func': 'fn',
'slog_func': 'fn',
}
enumPrefixOverrides = {
# sokol_gfx.h
'PIXELFORMAT': 'pixelFormat',
'RESOURCESTATE': 'resourceState',
'BUFFERTYPE': 'bufferType',
'INDEXTYPE': 'indexType',
'IMAGETYPE': 'imageType',
'SAMPLERTYPE': 'samplerType',
'CUBEFACE': 'cubeFace',
'SHADERSTAGE': 'shaderStage',
'PRIMITIVETYPE': 'primitiveType',
'BORDERCOLOR': 'borderColor',
'VERTEXFORMAT': 'vertexFormat',
'VERTEXSTEP': 'vertexStep',
'UNIFORMTYPE': 'uniformType',
'UNIFORMLAYOUT': 'uniformLayout',
'CULLMODE': 'cullMode',
'FACEWINDING': 'faceWinding',
'COMPAREFUNC': 'compareFunc',
'STENCILOP': 'stencilOp',
'BLENDFACTOR': 'blendFactor',
'BLENDOP': 'blendOp',
'COLORMASK': 'colorMask',
# sokol_app.h
'EVENTTYPE': 'eventType',
'KEYCODE': 'keyCode',
'MOUSEBUTTON': 'mouseButton',
}
prim_types = {
@@ -60,7 +102,7 @@ prim_types = {
'double': 'float64',
'uintptr_t': 'uint',
'intptr_t': 'int',
'size_t': 'int',
'size_t': 'int', # not a bug, Nim's sizeof() returns int
}
prim_defaults = {
@@ -74,31 +116,62 @@ prim_defaults = {
'uint32_t': '0',
'int64_t': '0',
'uint64_t': '0',
'float': '0.0',
'float': '0.0f',
'double': '0.0',
'uintptr_t': '0',
'intptr_t': '0',
'size_t': '0'
}
common_prim_types = """
array
untyped typed void
bool byte char
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64
float float32 float64
string
cchar cint csize_t
cfloat cdouble
cstring
pointer
""".split()
keywords = """
addr and as asm
bind block break
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func
if import in include interface is isnot iterator
let
macro method mixin mod
nil not notin
object of or out
proc ptr
raise ref return
shl shr static
template try tuple type
using
var
when while
xor
yield
""".split() + common_prim_types
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
def reset_globals():
global struct_types
global enum_types
global enum_items
global out_lines
struct_types = []
enum_types = []
enum_items = {}
out_lines = ''
re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
def l(s):
global out_lines
out_lines += s + '\n'
@@ -107,90 +180,66 @@ def as_nim_prim_type(s):
return prim_types[s]
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_nim_struct_type(s, prefix):
def as_nim_type_name(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
dep = parts[0] + '_'
outp = ''
if not s.startswith(prefix) and dep in module_names:
outp = module_names[dep] + '.'
for part in parts[1:]:
# ignore '_t' type postfix
if (part != 't'):
outp += part.capitalize()
return outp
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_nim_enum_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
if (part != 't'):
outp += part.capitalize()
return outp
# prefix_bla_blub(_t) => (dep.)BlaBlub
def as_nim_const_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
if (part != 't'):
outp += part.capitalize()
return outp
def check_struct_field_type_override(struct_name, field_name, orig_type):
s = f"{struct_name}.{field_name}"
if s in struct_field_type_overrides:
return struct_field_type_overrides[s]
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return orig_type
return default
def check_func_name_ignore(func_name):
return func_name in func_name_ignores
def check_ignore(name):
return name in ignores
def check_func_name_override(func_name):
if func_name in func_name_overrides:
return func_name_overrides[func_name]
def is_power_of_two(val):
return val == 0 or val & (val - 1) == 0
def wrap_keywords(s):
if s in keywords:
return f'`{s}`'
else:
return func_name
def trim_prefix(s, prefix):
outp = s;
if outp.lower().startswith(prefix.lower()):
outp = outp[len(prefix):]
return outp
# PREFIX_BLA_BLUB to bla_blub
def as_snake_case(s, prefix = ""):
return trim_prefix(s, prefix).lower()
return s
# prefix_bla_blub => blaBlub
def as_camel_case(s, prefix = ""):
parts = trim_prefix(s, prefix).lower().split('_')
def as_camel_case(s, prefix, wrap=True):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
parts = outp.lstrip('_').split('_')
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
if wrap:
outp = wrap_keywords(outp)
return outp
# prefix_bla_blub => BlaBlub
def as_pascal_case(s, prefix):
parts = trim_prefix(s, prefix).lower().split('_')
outp = ""
for part in parts:
# PREFIX_ENUM_BLA_BLO => blaBlo
def as_enum_item_name(s, wrap=True):
outp = s.lstrip('_')
parts = outp.split('_')[1:]
if parts[0] in enumPrefixOverrides:
parts[0] = enumPrefixOverrides[parts[0]]
else:
parts[0] = parts[0].lower()
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
if wrap:
outp = wrap_keywords(outp)
return outp
# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
def as_enum_item_name(s):
outp = s
if outp.startswith('_'):
outp = outp[1:]
parts = outp.lower().split('_')[2:]
outp = ""
for part in parts:
outp += part.capitalize()
if outp[0].isdigit():
outp = 'N' + outp
return outp
def enum_default_item(enum_name):
return enum_items[enum_name][0]
def is_prim_type(s):
return s in prim_types
@@ -200,15 +249,6 @@ def is_struct_type(s):
def is_enum_type(s):
return s in enum_types
def is_string_ptr(s):
return s == "const char *"
def is_const_void_ptr(s):
return s == "const void *"
def is_void_ptr(s):
return s == "void *"
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
@@ -227,266 +267,208 @@ def is_const_struct_ptr(s):
return True
return False
def is_func_ptr(s):
return '(*)' in s
def is_1d_array_type(s):
return re_1d_array.match(s)
def is_2d_array_type(s):
return re_2d_array.match(s)
def type_default_value(s):
return prim_defaults[s]
def extract_array_type(s):
return s[:s.index('[')].strip()
def extract_array_nums(s):
return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
def extract_ptr_type(s):
tokens = s.split()
if tokens[0] == 'const':
return tokens[1]
else:
return tokens[0]
def as_extern_c_arg_type(arg_type, prefix):
if arg_type == "void":
return "void"
elif is_prim_type(arg_type):
return as_nim_prim_type(arg_type)
elif is_struct_type(arg_type):
return as_nim_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return as_nim_enum_type(arg_type, prefix)
elif is_void_ptr(arg_type):
return "pointer"
elif is_const_void_ptr(arg_type):
return "pointer"
elif is_string_ptr(arg_type):
return "cstring"
elif is_const_struct_ptr(arg_type):
return f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return f"[*c] {as_nim_prim_type(extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
else:
return '??? (as_extern_c_arg_type)'
def as_nim_arg_type(arg_prefix, arg_type, prefix):
# NOTE: if arg_prefix is None, the result is used as return value
pre = "" if arg_prefix is None else arg_prefix
if arg_type == "void":
if arg_prefix is None:
return "void"
else:
return ""
elif is_prim_type(arg_type):
return pre + as_nim_prim_type(arg_type)
elif is_struct_type(arg_type):
return pre + as_nim_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return pre + as_nim_enum_type(arg_type, prefix)
elif is_void_ptr(arg_type):
return pre + "pointer"
elif is_const_void_ptr(arg_type):
return pre + "pointer"
elif is_string_ptr(arg_type):
return pre + "cstring"
elif is_const_struct_ptr(arg_type):
return pre + f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
else:
return arg_prefix + "??? (as_nim_arg_type)"
# get C-style arguments of a function pointer as string
def funcptr_args_c(field_type, prefix):
def funcptr_args(field_type, prefix):
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
s = ""
n = 0
for token in tokens:
n += 1
arg_type = token.strip()
arg_ctype = token.strip()
if s != "":
s += ", "
c_arg = f"a{n}:" + as_extern_c_arg_type(arg_type, prefix)
if (c_arg == "void"):
return ""
else:
s += c_arg
arg_nimtype = as_nim_type(arg_ctype, prefix)
if arg_nimtype == "":
return "" # fun(void)
s += f"a{n}:{arg_nimtype}"
if s == "a1:void":
s = ""
return s
# get C-style result of a function pointer as string
def funcptr_res_c(field_type):
res_type = field_type[:field_type.index('(*)')].strip()
if res_type == 'void':
return ''
elif is_const_void_ptr(res_type):
return ':pointer'
else:
return '???'
def funcptr_result(field_type, prefix):
ctype = field_type[:field_type.index('(*)')].strip()
return as_nim_type(ctype, prefix)
def funcdecl_args_c(decl, prefix):
s = ""
for param_decl in decl['params']:
if s != "":
s += ", "
arg_type = param_decl['type']
s += as_extern_c_arg_type(arg_type, prefix)
return s
def funcdecl_args_nim(decl, prefix):
s = ""
for param_decl in decl['params']:
if s != "":
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
s += f"{as_nim_arg_type(f'{arg_name}:', arg_type, prefix)}"
return s
def funcdecl_res_c(decl, prefix):
decl_type = decl['type']
res_type = decl_type[:decl_type.index('(')].strip()
return as_extern_c_arg_type(res_type, prefix)
def funcdecl_res_nim(decl, prefix):
decl_type = decl['type']
res_type = decl_type[:decl_type.index('(')].strip()
nim_res_type = as_nim_arg_type(None, res_type, prefix)
if nim_res_type == "":
nim_res_type = "void"
return nim_res_type
def gen_struct(decl, prefix, use_raw_name=False):
struct_name = decl['name']
nim_type = struct_name if use_raw_name else as_nim_struct_type(struct_name, prefix)
l(f"type {nim_type}* = object")
isPublic = True
for field in decl['fields']:
field_name = field['name']
if field_name == "__pad":
# FIXME: these should be guarded by SOKOL_ZIG_BINDINGS, but aren't?
continue
isPublic = not field_name.startswith("_")
field_name = as_camel_case(field_name, "_")
if field_name == "ptr":
field_name = "source"
if field_name == "ref":
field_name = "`ref`"
if field_name == "type":
field_name = "`type`"
if isPublic:
field_name += "*"
field_type = field['type']
field_type = check_struct_field_type_override(struct_name, field_name, field_type)
if is_prim_type(field_type):
l(f" {field_name}:{as_nim_prim_type(field_type)}")
elif is_struct_type(field_type):
l(f" {field_name}:{as_nim_struct_type(field_type, prefix)}")
elif is_enum_type(field_type):
l(f" {field_name}:{as_nim_enum_type(field_type, prefix)}")
elif is_string_ptr(field_type):
l(f" {field_name}:cstring")
elif is_const_void_ptr(field_type):
l(f" {field_name}:pointer")
elif is_void_ptr(field_type):
l(f" {field_name}:pointer")
elif is_const_prim_ptr(field_type):
l(f" {field_name}:ptr {as_nim_prim_type(extract_ptr_type(field_type))}")
elif is_func_ptr(field_type):
l(f" {field_name}:proc({funcptr_args_c(field_type, prefix)}){funcptr_res_c(field_type)} {{.cdecl.}}")
elif is_1d_array_type(field_type):
array_type = extract_array_type(field_type)
array_nums = extract_array_nums(field_type)
if is_prim_type(array_type) or is_struct_type(array_type):
if is_prim_type(array_type):
nim_type = as_nim_prim_type(array_type)
elif is_struct_type(array_type):
nim_type = as_nim_struct_type(array_type, prefix)
elif is_enum_type(array_type):
nim_type = as_nim_enum_type(array_type, prefix)
else:
nim_type = '??? (array type)'
t0 = f"array[{array_nums[0]}, {nim_type}]"
t0_slice = f"[]const {nim_type}"
t1 = f"[_]{nim_type}"
l(f" {field_name}:{t0}")
elif is_const_void_ptr(array_type):
l(f" {field_name}:array[{array_nums[0]}, pointer]")
else:
l(f"// FIXME: ??? array {field_name}:{field_type} => {array_type} [{array_nums[0]}]")
elif is_2d_array_type(field_type):
array_type = extract_array_type(field_type)
array_nums = extract_array_nums(field_type)
if is_prim_type(array_type):
nim_type = as_nim_prim_type(array_type)
def_val = type_default_value(array_type)
elif is_struct_type(array_type):
nim_type = as_nim_struct_type(array_type, prefix)
def_val = ".{ }"
else:
nim_type = "???"
def_val = "???"
t0 = f"array[{array_nums[0]}, array[{array_nums[1]}, {nim_type}]]"
l(f" {field_name}:{t0}")
def as_nim_type(ctype, prefix, struct_ptr_as_value=False):
if ctype == "void":
return ""
elif is_prim_type(ctype):
return as_nim_prim_type(ctype)
elif is_struct_type(ctype):
return as_nim_type_name(ctype, prefix)
elif is_enum_type(ctype):
return as_nim_type_name(ctype, prefix)
elif util.is_string_ptr(ctype):
return "cstring"
elif util.is_void_ptr(ctype) or util.is_const_void_ptr(ctype):
return "pointer"
elif is_const_struct_ptr(ctype):
nim_type = as_nim_type(util.extract_ptr_type(ctype), prefix)
if struct_ptr_as_value:
return f"{nim_type}"
else:
l(f"// FIXME: {field_name}:{field_type};")
return f"ptr {nim_type}"
elif is_prim_ptr(ctype) or is_const_prim_ptr(ctype):
return f"ptr {as_nim_type(util.extract_ptr_type(ctype), prefix)}"
elif util.is_func_ptr(ctype):
args = funcptr_args(ctype, prefix)
res = funcptr_result(ctype, prefix)
if res != "":
res = ":" + res
return f"proc({args}){res} {{.cdecl.}}"
elif util.is_1d_array_type(ctype):
array_ctype = util.extract_array_type(ctype)
array_sizes = util.extract_array_sizes(ctype)
return f'array[{array_sizes[0]}, {as_nim_type(array_ctype, prefix)}]'
elif util.is_2d_array_type(ctype):
array_ctype = util.extract_array_type(ctype)
array_sizes = util.extract_array_sizes(ctype)
return f'array[{array_sizes[0]}, array[{array_sizes[1]}, {as_nim_type(array_ctype, prefix)}]]'
else:
sys.exit(f"ERROR as_nim_type: {ctype}")
def as_nim_struct_name(struct_decl, prefix):
struct_name = check_override(struct_decl['name'])
nim_type = f'{as_nim_type_name(struct_name, prefix)}'
return nim_type
def as_nim_field_name(field_decl, prefix, check_private=True):
field_name = as_camel_case(check_override(field_decl['name']), prefix)
if check_private:
is_private = field_decl['name'].startswith('_')
if not is_private:
field_name += "*"
return field_name
def as_nim_field_type(struct_decl, field_decl, prefix):
return as_nim_type(check_override(f"{struct_decl['name']}.{field_decl['name']}", default=field_decl['type']), prefix)
def gen_struct(decl, prefix):
l(f"type {as_nim_struct_name(decl, prefix)}* = object")
for field in decl['fields']:
l(f" {as_nim_field_name(field, prefix)}:{as_nim_field_type(decl, field, prefix)}")
l("")
def gen_consts(decl, prefix):
l("const")
for item in decl['items']:
l(f" {trim_prefix(item['name'], prefix)}* = {item['value']}")
item_name = check_override(item['name'])
l(f" {as_camel_case(item_name, prefix)}* = {item['value']}")
l("")
def gen_enum(decl, prefix):
item_names_by_value = {}
value = -1
hasForceU32 = False
hasExplicitValues = False
has_explicit_values = False
for item in decl['items']:
itemName = item['name']
if itemName.endswith("_FORCE_U32"):
hasForceU32 = True
elif itemName.endswith("_NUM"):
item_name = check_override(item['name'])
if item_name.endswith("_NUM") or item_name.endswith("_FORCE_U32"):
continue
else:
if 'value' in item:
hasExplicitValues = True
has_explicit_values = True
value = int(item['value'])
else:
value += 1
item_names_by_value[value] = as_enum_item_name(item['name']);
if hasForceU32:
l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure, size:4.}} = enum")
else:
l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure.}} = enum")
if hasExplicitValues:
item_names_by_value[value] = as_enum_item_name(item_name)
enum_name_nim = as_nim_type_name(decl['name'], prefix)
l('type')
l(f" {enum_name_nim}* {{.size:sizeof(int32).}} = enum")
if has_explicit_values:
# Nim requires explicit enum values to be declared in ascending order
for value in sorted(item_names_by_value):
name = item_names_by_value[value]
l(f" {name} = {value},")
l(f" {name} = {value},")
else:
for name in item_names_by_value.values():
l(f" {name},")
l(f" {name},")
l("")
# returns C prototype compatible function args (with pointers)
def funcdecl_args_c(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
arg_name = param_decl['name']
arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix)}"
return s
# returns Nim function args (pass structs by value)
def funcdecl_args_nim(decl, prefix):
s = ""
func_name = decl['name']
for param_decl in decl['params']:
if s != "":
s += ", "
arg_name = param_decl['name']
arg_type = check_override(f'{func_name}.{arg_name}', default=param_decl['type'])
s += f"{as_camel_case(arg_name, prefix)}:{as_nim_type(arg_type, prefix, struct_ptr_as_value=True)}"
return s
def funcdecl_result(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
nim_res_type = as_nim_type(result_type, prefix)
if nim_res_type == "":
nim_res_type = "void"
return nim_res_type
def gen_func_nim(decl, prefix):
c_func_name = decl['name']
nim_func_name = as_camel_case(decl['name'], prefix)
nim_res_type = funcdecl_res_nim(decl, prefix)
l(f"proc {nim_func_name}*({funcdecl_args_nim(decl, prefix)}):{funcdecl_res_nim(decl, prefix)} {{.cdecl, importc:\"{decl['name']}\".}}")
nim_func_name = as_camel_case(check_override(c_func_name), prefix, wrap=False)
nim_res_type = funcdecl_result(decl, prefix)
if c_func_name in c_callbacks:
l(f"proc {nim_func_name}*({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
else:
l(f"proc c_{nim_func_name}({funcdecl_args_c(decl, prefix)}):{nim_res_type} {{.cdecl, importc:\"{c_func_name}\".}}")
l(f"proc {wrap_keywords(nim_func_name)}*({funcdecl_args_nim(decl, prefix)}):{nim_res_type} =")
s = f" c_{nim_func_name}("
for i, param_decl in enumerate(decl['params']):
if i > 0:
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
if is_const_struct_ptr(arg_type):
s += f"unsafeAddr({arg_name})"
else:
s += arg_name
s += ")"
l(s)
l("")
def gen_array_converters(decl, prefix):
for field in decl['fields']:
if util.is_array_type(field['type']):
array_type = util.extract_array_type(field['type'])
array_sizes = util.extract_array_sizes(field['type'])
struct_name = as_nim_struct_name(decl, prefix)
field_name = as_nim_field_name(field, prefix, check_private=False)
array_base_type = as_nim_type(array_type, prefix)
if util.is_1d_array_type(field['type']):
n = array_sizes[0]
l(f'converter to{struct_name}{field_name}*[N:static[int]](items: array[N, {array_base_type}]): array[{n}, {array_base_type}] =')
l(f' static: assert(N < {n})')
l(f' for index,item in items.pairs: result[index]=item')
l('')
elif util.is_2d_array_type(field['type']):
x = array_sizes[1]
y = array_sizes[0]
l(f'converter to{struct_name}{field_name}*[Y:static[int], X:static[int]](items: array[Y, array[X, {array_base_type}]]): array[{y}, array[{x}, {array_base_type}]] =')
l(f' static: assert(X < {x})')
l(f' static: assert(Y < {y})')
l(f' for indexY,itemY in items.pairs:')
l(f' for indexX, itemX in itemY.pairs:')
l(f' result[indexY][indexX] = itemX')
l('')
else:
sys.exit('Unsupported converter array dimension (> 2)!')
def pre_parse(inp):
global struct_types
global enum_types
@@ -497,15 +479,92 @@ def pre_parse(inp):
elif kind == 'enum':
enum_name = decl['name']
enum_types.append(enum_name)
enum_items[enum_name] = []
for item in decl['items']:
enum_items[enum_name].append(as_enum_item_name(item['name']))
def gen_imports(inp, dep_prefixes):
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
l(f'import {dep_module_name}')
l('')
def gen_extra(inp):
if inp['prefix'] in ['sg_']:
# FIXME: remove when sokol-shdc has been integrated!
l('when defined gl:')
l(' const gl* = true')
l(' const d3d11* = false')
l(' const metal* = false')
l('elif defined windows:')
l(' const gl* = false')
l(' const d3d11* = true')
l(' const metal* = false')
l('elif defined macosx:')
l(' const gl* = false')
l(' const d3d11* = false')
l(' const metal* = true')
l('elif defined linux:')
l(' const gl* = true')
l(' const d3d11* = false')
l(' const metal* = false')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['sg_', 'sapp_']:
l('when defined windows:')
l(' when not defined vcc:')
l(' {.passl:"-lkernel32 -luser32 -lshell32 -lgdi32".}')
l(' when defined gl:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' else:')
l(' {.passc:"-DSOKOL_D3D11".}')
l(' when not defined vcc:')
l(' {.passl:"-ld3d11 -ldxgi".}')
l('elif defined macosx:')
l(' {.passc:"-x objective-c".}')
l(' {.passl:"-framework Cocoa -framework QuartzCore".}')
l(' when defined gl:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' {.passl:"-framework OpenGL".}')
l(' else:')
l(' {.passc:"-DSOKOL_METAL".}')
l(' {.passl:"-framework Metal -framework MetalKit".}')
l('elif defined linux:')
l(' {.passc:"-DSOKOL_GLCORE33".}')
l(' {.passl:"-lX11 -lXi -lXcursor -lGL -lm -ldl -lpthread".}')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['saudio_']:
l('when defined windows:')
l(' when not defined vcc:')
l(' {.passl:"-lkernel32 -lole32".}')
l('elif defined macosx:')
l(' {.passl:"-framework AudioToolbox".}')
l('elif defined linux:')
l(' {.passl:"-lasound -lm -lpthread".}')
l('else:')
l(' error("unsupported platform")')
l('')
if inp['prefix'] in ['sg_']:
l('## Convert a 4-element tuple of numbers to a gfx.Color')
l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber,A:SomeNumber](rgba: tuple [r:R,g:G,b:B,a:A]):Color =')
l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:rgba.a.float32)')
l('')
l('## Convert a 3-element tuple of numbers to a gfx.Color')
l('converter toColor*[R:SomeNumber,G:SomeNumber,B:SomeNumber](rgba: tuple [r:R,g:G,b:B]):Color =')
l(' Color(r:rgba.r.float32, g:rgba.g.float32, b:rgba.b.float32, a:1.float32)')
l('')
# NOTE: this simplistic to_Range() converter has various issues, some of them dangerous:
# - doesn't work as expected for slice types
# - it's very easy to create a range that points to invalid memory
# (so far observed for stack-allocated structs <= 16 bytes)
#if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
# l('# helper function to convert "anything" into a Range')
# l('converter to_Range*[T](source: T): Range =')
# l(' Range(addr: source.unsafeAddr, size: source.sizeof.uint)')
# l('')
c_source_path = '/'.join(c_source_paths[inp['prefix']].split('/')[3:])
l('{.passc:"-DSOKOL_NIM_IMPL".}')
l(f'{{.compile:"{c_source_path}".}}')
def gen_module(inp, dep_prefixes):
l('## machine generated, do not edit')
@@ -518,22 +577,27 @@ def gen_module(inp, dep_prefixes):
kind = decl['kind']
if kind == 'consts':
gen_consts(decl, prefix)
elif kind == 'enum':
gen_enum(decl, prefix)
elif kind == 'struct':
gen_struct(decl, prefix)
elif kind == 'func':
if not check_func_name_ignore(decl['name']):
elif not check_ignore(decl['name']):
if kind == 'struct':
gen_struct(decl, prefix)
gen_array_converters(decl, prefix)
elif kind == 'enum':
gen_enum(decl, prefix)
elif kind == 'func':
gen_func_nim(decl, prefix)
gen_extra(inp)
def prepare():
print('Generating nim bindings:')
print('=== Generating Nim bindings:')
if not os.path.isdir('sokol-nim/src/sokol'):
os.makedirs('sokol-nim/src/sokol')
if not os.path.isdir('sokol-nim/src/sokol/c'):
os.makedirs('sokol-nim/src/sokol/c')
def gen(c_header_path, c_prefix, dep_c_prefixes):
if not c_prefix in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
global out_lines
module_name = module_names[c_prefix]
c_source_path = c_source_paths[c_prefix]
@@ -543,20 +607,5 @@ def gen(c_header_path, c_prefix, dep_c_prefixes):
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
gen_module(ir, dep_c_prefixes)
output_path = f"sokol-nim/src/sokol/{ir['module']}.nim"
## some changes for readability
out_lines = out_lines.replace("PixelformatInfo", "PixelFormatInfo")
out_lines = out_lines.replace(" Dontcare,", " DontCare,")
out_lines = out_lines.replace(" Vertexbuffer,", " VertexBuffer,")
out_lines = out_lines.replace(" Indexbuffer,", " IndexBuffer,")
out_lines = out_lines.replace(" N2d,", " Plane,")
out_lines = out_lines.replace(" N3d,", " Volume,")
out_lines = out_lines.replace(" Vs,", " Vertex,")
out_lines = out_lines.replace(" Fs,", " Fragment,")
## include extensions in generated code
l("# Nim-specific API extensions")
l(f"include nim/{ir['module']}")
with open(output_path, 'w', newline='\n') as f_outp:
f_outp.write(out_lines)
+138 -170
View File
@@ -1,5 +1,5 @@
#-------------------------------------------------------------------------------
# Read output of gen_json.py and generate Zig language bindings.
# Generate Zig bindings.
#
# Zig coding style:
# - types are PascalCase
@@ -7,9 +7,12 @@
# - otherwise snake_case
#-------------------------------------------------------------------------------
import gen_ir
import json, re, os, shutil
import os, shutil, sys
import gen_util as util
module_names = {
'slog_': 'log',
'sg_': 'gfx',
'sapp_': 'app',
'stm_': 'time',
@@ -20,6 +23,7 @@ module_names = {
}
c_source_paths = {
'slog_': 'sokol-zig/src/sokol/c/sokol_log.c',
'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c',
'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c',
'stm_': 'sokol-zig/src/sokol/c/sokol_time.c',
@@ -29,21 +33,23 @@ c_source_paths = {
'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c',
}
name_ignores = [
ignores = [
'sdtx_printf',
'sdtx_vprintf',
'sg_install_trace_hooks',
'sg_trace_hooks',
]
name_overrides = {
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians'
}
# functions that need to be exposed as 'raw' C callbacks without a Zig wrapper function
c_callbacks = [
'slog_func'
]
# NOTE: syntax for function results: "func_name.RESULT"
type_overrides = {
overrides = {
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
'sgl_deg': 'sgl_as_degrees',
'sgl_rad': 'sgl_as_radians',
'sg_context_desc.color_format': 'int',
'sg_context_desc.depth_format': 'int',
'sg_apply_uniforms.ub_index': 'uint32_t',
@@ -53,6 +59,7 @@ type_overrides = {
'sshape_element_range_t.base_element': 'uint32_t',
'sshape_element_range_t.num_elements': 'uint32_t',
'sdtx_font.font_index': 'uint32_t',
'SGL_NO_ERROR': 'SGL_ERROR_NO_ERROR',
}
prim_types = {
@@ -92,6 +99,7 @@ prim_defaults = {
'size_t': '0'
}
struct_types = []
enum_types = []
enum_items = {}
@@ -107,9 +115,6 @@ def reset_globals():
enum_items = {}
out_lines = ''
re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
def l(s):
global out_lines
out_lines += s + '\n'
@@ -122,6 +127,7 @@ def as_zig_struct_type(s, prefix):
parts = s.lower().split('_')
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
for part in parts[1:]:
# ignore '_t' type postfix
if (part != 't'):
outp += part.capitalize()
return outp
@@ -135,42 +141,20 @@ def as_zig_enum_type(s, prefix):
outp += part.capitalize()
return outp
def check_type_override(func_or_struct_name, field_or_arg_name, orig_type):
s = f"{func_or_struct_name}.{field_or_arg_name}"
if s in type_overrides:
return type_overrides[s]
else:
return orig_type
def check_name_override(name):
if name in name_overrides:
return name_overrides[name]
else:
def check_override(name, default=None):
if name in overrides:
return overrides[name]
elif default is None:
return name
else:
return default
def check_name_ignore(name):
return name in name_ignores
# PREFIX_BLA_BLUB to bla_blub
def as_snake_case(s, prefix):
outp = s.lower()
if outp.startswith(prefix):
outp = outp[len(prefix):]
return outp
# prefix_bla_blub => blaBlub
def as_camel_case(s):
parts = s.lower().split('_')[1:]
outp = parts[0]
for part in parts[1:]:
outp += part.capitalize()
return outp
def check_ignore(name):
return name in ignores
# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
def as_enum_item_name(s):
outp = s
if outp.startswith('_'):
outp = outp[1:]
outp = s.lstrip('_')
parts = outp.split('_')[2:]
outp = '_'.join(parts)
if outp[0].isdigit():
@@ -189,15 +173,6 @@ def is_struct_type(s):
def is_enum_type(s):
return s in enum_types
def is_string_ptr(s):
return s == "const char *"
def is_const_void_ptr(s):
return s == "const void *"
def is_void_ptr(s):
return s == "void *"
def is_const_prim_ptr(s):
for prim_type in prim_types:
if s == f"const {prim_type} *":
@@ -216,32 +191,10 @@ def is_const_struct_ptr(s):
return True
return False
def is_func_ptr(s):
return '(*)' in s
def is_1d_array_type(s):
return re_1d_array.match(s)
def is_2d_array_type(s):
return re_2d_array.match(s)
def type_default_value(s):
return prim_defaults[s]
def extract_array_type(s):
return s[:s.index('[')].strip()
def extract_array_nums(s):
return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
def extract_ptr_type(s):
tokens = s.split()
if tokens[0] == 'const':
return tokens[1]
else:
return tokens[0]
def as_extern_c_arg_type(arg_type, prefix):
def as_c_arg_type(arg_type, prefix):
if arg_type == "void":
return "void"
elif is_prim_type(arg_type):
@@ -250,20 +203,20 @@ def as_extern_c_arg_type(arg_type, prefix):
return as_zig_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return as_zig_enum_type(arg_type, prefix)
elif is_void_ptr(arg_type):
return "?*c_void"
elif is_const_void_ptr(arg_type):
return "?*const c_void"
elif is_string_ptr(arg_type):
elif util.is_void_ptr(arg_type):
return "?*anyopaque"
elif util.is_const_void_ptr(arg_type):
return "?*const anyopaque"
elif util.is_string_ptr(arg_type):
return "[*c]const u8"
elif is_const_struct_ptr(arg_type):
return f"[*c]const {as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
return f"[*c]const {as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}"
return f"[*c] {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}"
return f"[*c]const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
else:
return '??? (as_extern_c_arg_type)'
sys.exit(f"Error as_c_arg_type(): {arg_type}")
def as_zig_arg_type(arg_prefix, arg_type, prefix):
# NOTE: if arg_prefix is None, the result is used as return value
@@ -279,21 +232,24 @@ def as_zig_arg_type(arg_prefix, arg_type, prefix):
return pre + as_zig_struct_type(arg_type, prefix)
elif is_enum_type(arg_type):
return pre + as_zig_enum_type(arg_type, prefix)
elif is_void_ptr(arg_type):
return pre + "?*c_void"
elif is_const_void_ptr(arg_type):
return pre + "?*const c_void"
elif is_string_ptr(arg_type):
elif util.is_void_ptr(arg_type):
return pre + "?*anyopaque"
elif util.is_const_void_ptr(arg_type):
return pre + "?*const anyopaque"
elif util.is_string_ptr(arg_type):
return pre + "[:0]const u8"
elif is_const_struct_ptr(arg_type):
# not a bug, pass const structs by value
return pre + f"{as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
return pre + f"{as_zig_struct_type(util.extract_ptr_type(arg_type), prefix)}"
elif is_prim_ptr(arg_type):
return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}"
return pre + f"* {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
elif is_const_prim_ptr(arg_type):
return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}"
return pre + f"*const {as_zig_prim_type(util.extract_ptr_type(arg_type))}"
else:
return arg_prefix + "??? (as_zig_arg_type)"
sys.exit(f"ERROR as_zig_arg_type(): {arg_type}")
def is_zig_string(zig_type):
return zig_type == "[:0]const u8"
# get C-style arguments of a function pointer as string
def funcptr_args_c(field_type, prefix):
@@ -303,22 +259,24 @@ def funcptr_args_c(field_type, prefix):
arg_type = token.strip()
if s != "":
s += ", "
c_arg = as_extern_c_arg_type(arg_type, prefix)
if (c_arg == "void"):
c_arg = as_c_arg_type(arg_type, prefix)
if c_arg == "void":
return ""
else:
s += c_arg
return s
# get C-style result of a function pointer as string
def funcptr_res_c(field_type):
def funcptr_result_c(field_type):
res_type = field_type[:field_type.index('(*)')].strip()
if res_type == 'void':
return 'void'
elif is_const_void_ptr(res_type):
return '?*const c_void'
elif util.is_const_void_ptr(res_type):
return '?*const anyopaque'
elif util.is_void_ptr(res_type):
return '?*anyopaque'
else:
return '???'
sys.exit(f"ERROR funcptr_result_c(): {field_type}")
def funcdecl_args_c(decl, prefix):
s = ""
@@ -327,8 +285,8 @@ def funcdecl_args_c(decl, prefix):
if s != "":
s += ", "
param_name = param_decl['name']
param_type = check_type_override(func_name, param_name, param_decl['type'])
s += as_extern_c_arg_type(param_type, prefix)
param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
s += as_c_arg_type(param_type, prefix)
return s
def funcdecl_args_zig(decl, prefix):
@@ -338,55 +296,49 @@ def funcdecl_args_zig(decl, prefix):
if s != "":
s += ", "
param_name = param_decl['name']
param_type = check_type_override(func_name, param_name, param_decl['type'])
param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}"
return s
def funcdecl_result_c(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
return as_extern_c_arg_type(result_type, prefix)
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
return as_c_arg_type(result_type, prefix)
def funcdecl_result_zig(decl, prefix):
func_name = decl['name']
decl_type = decl['type']
result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
result_type = check_override(f'{func_name}.RESULT', default=decl_type[:decl_type.index('(')].strip())
zig_res_type = as_zig_arg_type(None, result_type, prefix)
if zig_res_type == "":
zig_res_type = "void"
return zig_res_type
def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True):
struct_name = decl['name']
zig_type = struct_name if use_raw_name else as_zig_struct_type(struct_name, prefix)
l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{")
def gen_struct(decl, prefix):
struct_name = check_override(decl['name'])
zig_type = as_zig_struct_type(struct_name, prefix)
l(f"pub const {zig_type} = extern struct {{")
for field in decl['fields']:
field_name = field['name']
field_type = field['type']
field_type = check_type_override(struct_name, field_name, field_type)
field_name = check_override(field['name'])
field_type = check_override(f'{struct_name}.{field_name}', default=field['type'])
if is_prim_type(field_type):
l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
elif is_struct_type(field_type):
l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{ }},")
elif is_enum_type(field_type):
l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},")
elif is_string_ptr(field_type):
elif util.is_string_ptr(field_type):
l(f" {field_name}: [*c]const u8 = null,")
elif is_const_void_ptr(field_type):
l(f" {field_name}: ?*const c_void = null,")
elif is_void_ptr(field_type):
l(f" {field_name}: ?*c_void = null,")
elif util.is_const_void_ptr(field_type):
l(f" {field_name}: ?*const anyopaque = null,")
elif util.is_void_ptr(field_type):
l(f" {field_name}: ?*anyopaque = null,")
elif is_const_prim_ptr(field_type):
l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,")
elif is_func_ptr(field_type):
if callconvc_funcptrs:
l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_res_c(field_type)} = null,")
else:
l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) {funcptr_res_c(field_type)} = null,")
elif is_1d_array_type(field_type):
array_type = extract_array_type(field_type)
array_nums = extract_array_nums(field_type)
l(f" {field_name}: ?[*]const {as_zig_prim_type(util.extract_ptr_type(field_type))} = null,")
elif util.is_func_ptr(field_type):
l(f" {field_name}: ?*const fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_result_c(field_type)} = null,")
elif util.is_1d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type) or is_struct_type(array_type):
if is_prim_type(array_type):
zig_type = as_zig_prim_type(array_type)
@@ -398,19 +350,17 @@ def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_
zig_type = as_zig_enum_type(array_type, prefix)
def_val = '.{}'
else:
zig_type = '??? (array type)'
def_val = '???'
t0 = f"[{array_nums[0]}]{zig_type}"
t0_slice = f"[]const {zig_type}"
sys.exit(f"ERROR gen_struct is_1d_array_type: {array_type}")
t0 = f"[{array_sizes[0]}]{zig_type}"
t1 = f"[_]{zig_type}"
l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},")
elif is_const_void_ptr(array_type):
l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},")
l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_sizes[0]},")
elif util.is_const_void_ptr(array_type):
l(f" {field_name}: [{array_sizes[0]}]?*const anyopaque = [_]?*const anyopaque {{ null }} ** {array_sizes[0]},")
else:
l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]")
elif is_2d_array_type(field_type):
array_type = extract_array_type(field_type)
array_nums = extract_array_nums(field_type)
sys.exit(f"ERROR gen_struct: array {field_name}: {field_type} => {array_type} [{array_sizes[0]}]")
elif util.is_2d_array_type(field_type):
array_type = util.extract_array_type(field_type)
array_sizes = util.extract_array_sizes(field_type)
if is_prim_type(array_type):
zig_type = as_zig_prim_type(array_type)
def_val = type_default_value(array_type)
@@ -418,22 +368,23 @@ def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_
zig_type = as_zig_struct_type(array_type, prefix)
def_val = ".{ }"
else:
zig_type = "???"
def_val = "???"
t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}"
l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_nums[1]}}}**{array_nums[0]},")
sys.exit(f"ERROR gen_struct is_2d_array_type: {array_type}")
t0 = f"[{array_sizes[0]}][{array_sizes[1]}]{zig_type}"
l(f" {field_name}: {t0} = [_][{array_sizes[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_sizes[1]}}}**{array_sizes[0]},")
else:
l(f"// FIXME: {field_name}: {field_type};")
sys.exit(f"ERROR gen_struct: {field_name}: {field_type};")
l("};")
def gen_consts(decl, prefix):
for item in decl['items']:
l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};")
item_name = check_override(item['name'])
l(f"pub const {util.as_lower_snake_case(item_name, prefix)} = {item['value']};")
def gen_enum(decl, prefix):
l(f"pub const {as_zig_enum_type(decl['name'], prefix)} = enum(i32) {{")
enum_name = check_override(decl['name'])
l(f"pub const {as_zig_enum_type(enum_name, prefix)} = enum(i32) {{")
for item in decl['items']:
item_name = as_enum_item_name(item['name'])
item_name = as_enum_item_name(check_override(item['name']))
if item_name != "FORCE_U32":
if 'value' in item:
l(f" {item_name} = {item['value']},")
@@ -446,27 +397,36 @@ def gen_func_c(decl, prefix):
def gen_func_zig(decl, prefix):
c_func_name = decl['name']
zig_func_name = as_camel_case(check_name_override(decl['name']))
zig_res_type = funcdecl_result_zig(decl, prefix)
l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
if zig_res_type != 'void':
s = f" return {c_func_name}("
zig_func_name = util.as_lower_camel_case(check_override(decl['name']), prefix)
if c_func_name in c_callbacks:
# a simple forwarded C callback function
l(f"pub const {zig_func_name} = {c_func_name};")
else:
s = f" {c_func_name}("
for i, param_decl in enumerate(decl['params']):
if i > 0:
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
if is_const_struct_ptr(arg_type):
s += f"&{arg_name}"
elif is_string_ptr(arg_type):
s += f"@ptrCast([*c]const u8,{arg_name})"
zig_res_type = funcdecl_result_zig(decl, prefix)
l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
if is_zig_string(zig_res_type):
# special case: convert C string to Zig string slice
s = f" return cStrToZig({c_func_name}("
elif zig_res_type != 'void':
s = f" return {c_func_name}("
else:
s += arg_name
s += ");"
l(s)
l("}")
s = f" {c_func_name}("
for i, param_decl in enumerate(decl['params']):
if i > 0:
s += ", "
arg_name = param_decl['name']
arg_type = param_decl['type']
if is_const_struct_ptr(arg_type):
s += f"&{arg_name}"
elif util.is_string_ptr(arg_type):
s += f"@ptrCast([*c]const u8,{arg_name})"
else:
s += arg_name
if is_zig_string(zig_res_type):
s += ")"
s += ");"
l(s)
l("}")
def pre_parse(inp):
global struct_types
@@ -483,12 +443,17 @@ def pre_parse(inp):
enum_items[enum_name].append(as_enum_item_name(item['name']))
def gen_imports(inp, dep_prefixes):
l('const builtin = @import("builtin");')
for dep_prefix in dep_prefixes:
dep_module_name = module_names[dep_prefix]
l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");')
l('')
l('')
def gen_helpers(inp):
l('// helper function to convert a C string to a Zig string slice')
l('fn cStrToZig(c_str: [*c]const u8) [:0]const u8 {')
l(' return @import("std").mem.span(c_str);')
l('}')
if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
l('// helper function to convert "anything" to a Range struct')
l('pub fn asRange(val: anytype) Range {')
@@ -502,7 +467,7 @@ def gen_helpers(inp):
l(' }')
l(' },')
l(' .Struct, .Array => {')
l(' return .{ .ptr = &val, .size = @sizeOf(@TypeOf(val)) };')
l(' @compileError("Structs and arrays must be passed as pointers to asRange");')
l(' },')
l(' else => {')
l(' @compileError("Cannot convert to range!");')
@@ -547,7 +512,7 @@ def gen_module(inp, dep_prefixes):
kind = decl['kind']
if kind == 'consts':
gen_consts(decl, prefix)
elif not check_name_ignore(decl['name']):
elif not check_ignore(decl['name']):
if kind == 'struct':
gen_struct(decl, prefix)
elif kind == 'enum':
@@ -557,13 +522,16 @@ def gen_module(inp, dep_prefixes):
gen_func_zig(decl, prefix)
def prepare():
print('Generating zig bindings:')
print('=== Generating Zig bindings:')
if not os.path.isdir('sokol-zig/src/sokol'):
os.makedirs('sokol-zig/src/sokol')
if not os.path.isdir('sokol-zig/src/sokol/c'):
os.makedirs('sokol-zig/src/sokol/c')
def gen(c_header_path, c_prefix, dep_c_prefixes):
if not c_prefix in module_names:
print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
module_name = module_names[c_prefix]
c_source_path = c_source_paths[c_prefix]
print(f' {c_header_path} => {module_name}')