#!/usr/bin/env python3 """Generate ts_registry_generated.c with registry array and getter functions.""" import glob import importlib.util import os import re import sys _spec = importlib.util.spec_from_file_location( "ts_parser", os.path.join(os.path.dirname(os.path.abspath(__file__)), "ts_parser.py"), ) if _spec and _spec.loader: ts_parser = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(ts_parser) parse_all = ts_parser.parse_all else: print("Error: could not load ts_parser", file=sys.stderr) sys.exit(1) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT = os.path.join(ROOT, "lib", "ts_registry_generated.c") def _resolve_params_size(params_size, name): """Resolve params_size to a compile-time constant. If params_size is '0', keep it. If it's sizeof(ts_X_params_t), check if the struct is visible in ts.h. If not, compute size from definition. """ if params_size == "0": return "0" header_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "ts.h") try: with open(header_path, encoding="utf-8") as hf: hcontent = hf.read() if re.search(r"ts_" + re.escape(name) + r"_params_t", hcontent): return params_size except FileNotFoundError: pass # Struct not in header, compute size from definition. # Both fp_t (double) and uint_t (uint64_t) are 8 bytes on 64-bit. # This is a fallback; sizeof() in the generated C code is preferred. lib_dir = os.path.join(ROOT, "lib") for cfile in glob.glob(os.path.join(lib_dir, "*.c")): try: with open(cfile, encoding="utf-8") as f: content = f.read() except OSError: continue struct_m = re.search( r"typedef\s+struct\s*\{([^}]+)\}\s+ts_" + re.escape(name) + r"_params_t", content, ) if struct_m: body = struct_m.group(1) n_fp = len(re.findall(r"fp_t\s+", body)) n_uint = len(re.findall(r"uint_t\s+", body)) n_total = n_fp + n_uint if n_total == 0: return "0" # Assume 8-byte alignment (valid for fp_t=double, uint_t=uint64_t) return str(n_total * 8) return "0" def main(): funcs = parse_all(detect_x_access=False) if not funcs: print("Warning: no ts_parameter declarations found.", file=sys.stderr) sys.exit(1) with open(OUT, "w", encoding="utf-8") as f: f.write("/* Auto-generated function registry. Do not edit. */\n\n") f.write('#include "ts.h"\n\n') f.write(f"static const uint_t _ts_fn_count = {len(funcs)};\n\n") f.write("static const ts_fn_info_t _ts_registry[] = {\n") for fm in funcs: ps = _resolve_params_size(fm.params_size, fm.name) f.write( f" {{ .n_inputs = {fm.x_n}, .n_outputs = {fm.y_n}, " f".n_constants = {fm.c_n}, .params_size = {ps} }},\n" ) f.write("};\n\n") f.write("static const char * const _ts_fn_names[] = {\n") for fm in funcs: f.write(f' "{fm.name}",\n') f.write("};\n\n") # Getter functions f.write("uint_t ts_fn_count( void ) { return _ts_fn_count; }\n\n") f.write("const ts_fn_info_t* ts_registry_get( uint_t i )\n") f.write("{\n") f.write(" return i < _ts_fn_count ? &_ts_registry[i] : NULL;\n") f.write("}\n\n") f.write("const char* ts_fn_name_get( uint_t i )\n") f.write("{\n") f.write(" return i < _ts_fn_count ? _ts_fn_names[i] : NULL;\n") f.write("}\n") print(f"Generated {len(funcs)} entries -> {OUT}") if __name__ == "__main__": main()