From 07e18043145f3d91c8ea7ec3ad41b3c905585277 Mon Sep 17 00:00:00 2001 From: Ethan Werner Date: Wed, 1 Jul 2026 09:42:39 +0400 Subject: 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 --- scripts/gen_registry.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 scripts/gen_registry.py (limited to 'scripts/gen_registry.py') 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() -- cgit v1.3.1