summaryrefslogtreecommitdiffstats
path: root/scripts/gen_registry.py
diff options
context:
space:
mode:
authorEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
committerEthan Werner <mail@ethanwerner.com>2026-07-01 09:42:39 +0400
commit07e18043145f3d91c8ea7ec3ad41b3c905585277 (patch)
tree2bdeae46973f4bdfce263679a4008ac768011f28 /scripts/gen_registry.py
initial commit: ts time series analysis library v0.1.0
- 354 financial and mathematical time-series indicators - Uniform C11 API with consistent function signatures - 349 passing tests with -Werror - Static and shared library builds - pkg-config integration - Python bindings - Fuzzing and sanitizer support - Security hardening flags
Diffstat (limited to 'scripts/gen_registry.py')
-rw-r--r--scripts/gen_registry.py103
1 files changed, 103 insertions, 0 deletions
diff --git a/scripts/gen_registry.py b/scripts/gen_registry.py
new file mode 100644
index 0000000..e80759e
--- /dev/null
+++ b/scripts/gen_registry.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+"""Generate ts_registry_generated.c with registry array and getter functions."""
+
+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
+ import glob
+
+ 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))
+ return str(n_fp * 8 + n_uint * 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()