aboutsummaryrefslogtreecommitdiff
path: root/bindgen
diff options
context:
space:
mode:
authorAndre Weissflog <floooh@gmail.com>2022-07-14 20:12:05 +0200
committerAndre Weissflog <floooh@gmail.com>2022-07-14 20:12:05 +0200
commitd19dfeee05c676110c379ed8aace75e1cffb48ff (patch)
tree4eb44727c5d4d074e14a37677572702a001eddbe /bindgen
parentfbe8a11be350e6f926c91c960fd009aa3dcbcbcf (diff)
gen_odin.py: write C function imports
Diffstat (limited to 'bindgen')
-rw-r--r--bindgen/gen_odin.py248
1 files changed, 246 insertions, 2 deletions
diff --git a/bindgen/gen_odin.py b/bindgen/gen_odin.py
index 18a9ef32..deaacf36 100644
--- a/bindgen/gen_odin.py
+++ b/bindgen/gen_odin.py
@@ -30,16 +30,98 @@ c_source_names = {
'sshape_': 'sokol_shape.c',
}
+ignores = [
+ 'sdtx_printf',
+ 'sdtx_vprintf',
+ 'sg_install_trace_hooks',
+ 'sg_trace_hooks',
+]
+
+# NOTE: syntax for function results: "func_name.RESULT"
+overrides = {
+ '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',
+ 'sg_draw.base_element': 'uint32_t',
+ 'sg_draw.num_elements': 'uint32_t',
+ 'sg_draw.num_instances': 'uint32_t',
+ 'sshape_element_range_t.base_element': 'uint32_t',
+ 'sshape_element_range_t.num_elements': 'uint32_t',
+ 'sdtx_font.font_index': 'uint32_t',
+}
+
+prim_types = {
+ 'int': 'i32',
+ 'bool': 'b8',
+ 'char': 'u8',
+ 'int8_t': 'i8',
+ 'uint8_t': 'u8',
+ 'int16_t': 'i16',
+ 'uint16_t': 'u16',
+ 'int32_t': 'i32',
+ 'uint32_t': 'u32',
+ 'int64_t': 'i64',
+ 'uint64_t': 'u64',
+ 'float': 'f32',
+ 'double': 'f64',
+ 'uintptr_t': 'u64',
+ 'intptr_t': 'i64',
+ 'size_t': 'u64'
+}
+
+prim_defaults = {
+ 'int': '0',
+ 'bool': 'false',
+ 'int8_t': '0',
+ 'uint8_t': '0',
+ 'int16_t': '0',
+ 'uint16_t': '0',
+ 'int32_t': '0',
+ 'uint32_t': '0',
+ 'int64_t': '0',
+ 'uint64_t': '0',
+ 'float': '0.0',
+ 'double': '0.0',
+ 'uintptr_t': '0',
+ 'intptr_t': '0',
+ 'size_t': '0'
+}
+
+re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
+re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
+
+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 = ''
def l(s):
global out_lines
out_lines += s + '\n'
+def check_override(name, default=None):
+ if name in overrides:
+ return overrides[name]
+ elif default is None:
+ return name
+ else:
+ return default
+
+def check_ignore(name):
+ return name in ignores
+
def get_odin_module_path(c_prefix):
return f'{module_root}/{module_names[c_prefix]}'
@@ -51,18 +133,180 @@ def make_odin_module_directory(c_prefix):
if not os.path.isdir(path):
os.makedirs(path)
+def as_prim_type(s):
+ return prim_types[s]
+
+# prefix_bla_blub(_t) => (dep.)Bla_Blub
+def as_struct_or_enum_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()
+ outp += '_'
+ outp = outp[:-1]
+ return outp
+
+# PREFIX_ENUM_BLA_BLUB => BLA_BLUB, _PREFIX_ENUM_BLA_BLUB => BLA_BLUB
+def as_enum_item_name(s):
+ outp = s.lstrip('_')
+ parts = outp.split('_')[2:]
+ outp = '_'.join(parts)
+ if outp[0].isdigit():
+ outp = '_' + 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
+
+def is_struct_type(s):
+ return s in struct_types
+
+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} *":
+ return True
+ return False
+
+def is_prim_ptr(s):
+ for prim_type in prim_types:
+ if s == f"{prim_type} *":
+ return True
+ return False
+
+def is_const_struct_ptr(s):
+ for struct_type in struct_types:
+ if s == f"const {struct_type} *":
+ return True
+ return False
+
+def is_func_ptr(s):
+ return '(*)' in s
+
+def is_1d_array_type(s):
+ return re_1d_array.match(s) is not None
+
+def is_2d_array_type(s):
+ return re_2d_array.match(s) is not None
+
+def type_default_value(s):
+ return prim_defaults[s]
+
+def extract_array_type(s):
+ return s[:s.index('[')].strip()
+
+def extract_array_sizes(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_prim_type(arg_type)
+ elif is_struct_type(arg_type):
+ return as_struct_or_enum_type(arg_type, prefix)
+ elif is_enum_type(arg_type):
+ return as_struct_or_enum_type(arg_type, prefix)
+ elif is_void_ptr(arg_type):
+ return "rawptr"
+ elif is_const_void_ptr(arg_type):
+ return "rawptr"
+ elif is_string_ptr(arg_type):
+ return "cstring"
+ elif is_const_struct_ptr(arg_type):
+ return f"^{as_struct_or_enum_type(extract_ptr_type(arg_type), prefix)}"
+ elif is_prim_ptr(arg_type):
+ return f"^{as_prim_type(extract_ptr_type(arg_type))}"
+ elif is_const_prim_ptr(arg_type):
+ return f"^{as_prim_type(extract_ptr_type(arg_type))}"
+ else:
+ sys.exit(f"Error as_extern_c_arg_type(): {arg_type}")
+
+def funcdecl_args_c(decl, prefix):
+ s = ''
+ func_name = decl['name']
+ for param_decl in decl['params']:
+ if s != '':
+ s += ', '
+ param_name = param_decl['name']
+ param_type = check_override(f'{func_name}.{param_name}', default=param_decl['type'])
+ s += as_extern_c_arg_type(param_type, prefix)
+ return s
+
+def funcdecl_result_c(decl, prefix):
+ func_name = decl['name']
+ decl_type = decl['type']
+ res_c_type = decl_type[:decl_type.index('(')].strip()
+ if (res_c_type == 'void'):
+ return ''
+ else:
+ result_type = check_override(f'{func_name}.RESULT', default=res_c_type)
+ return f'-> {as_extern_c_arg_type(result_type, prefix)}'
+
+def gen_c_imports(inp):
+ l(f'// FIXME: foreign import...\n')
+ l('@(default_calling_convention="c")')
+ l(f"foreign sokol_{inp['module']}_clib {{")
+ prefix = inp['prefix']
+ for decl in inp['decls']:
+ if decl['kind'] == 'func' and not decl['is_dep'] and not check_ignore(decl['name']):
+ args = funcdecl_args_c(decl, prefix)
+ ret = funcdecl_result_c(decl, prefix)
+ l(f" {decl['name']} :: proc({args}) {ret} ---")
+ l('}')
+
def gen_module(inp, dep_prefixes):
+ pre_parse(inp)
l('// machine generated, do not edit')
l('')
+ l(f"package sokol_{inp['module']}\n")
+ gen_c_imports(inp)
+
+def pre_parse(inp):
+ global struct_types
+ global enum_types
+ for decl in inp['decls']:
+ kind = decl['kind']
+ if kind == 'struct':
+ struct_types.append(decl['name'])
+ 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 prepare():
- print('Generating Odin bindings:')
+ print('=== Generating Odin bindings:')
if not os.path.isdir(f'{module_root}/c'):
os.makedirs(f'{module_root}/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...')
+ print(f' >> warning: skipping generation for {c_prefix} prefix...')
return
reset_globals()
make_odin_module_directory(c_prefix)