summaryrefslogtreecommitdiffstats
path: root/scripts/gen_registry.py
blob: dbe6f04ba148388dc59428e1e5f6d0a7fdcfe2c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/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()