#!/usr/bin/env python3 """gen_engine_decls.py — the canonical-decls header (Phase 16, Drew's "lever 1", cookbook §14c-c). Declare EVERY engine function (from its definition signature) and EVERY data symbol ONCE, canonically, in src/shared/engine_decls.h. The overlay .c includes it; harvest drafts then strip ALL their own declarations -> a draft can NEVER conflict with engine_core.h's definitions or another draft's guess. This dissolves the dominant whole-binary gate-failure (`conflicting types for func_X`) on call-heavy functions, which m2c declares with a guessed signature (and without `extern`, so sig_unify misses them). python3 tools/gen_engine_decls.py # regenerate src/shared/engine_decls.h """ import os, re REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ECORE = os.path.join(REPO, "src/shared/engine_core.h") OVC = os.path.join(REPO, "src/ov_SC01_077/ov_SC01_077.c") OUT = os.path.join(REPO, "src/shared/engine_decls.h") # a function DEFINITION signature: ` func_XXXX() {` (in an engine_core.h macro or inline) DEF_RE = re.compile(r"\b([A-Za-z_][\w \*]*\bfunc_[0-9A-Fa-f]+\s*\([^){;]*\))\s*\{") DATA_RE = re.compile(r"extern\s+([A-Za-z_][\w\s\*]*?\bD_[0-9A-Fa-f]+\s*(?:\[\s*\])?)\s*;") SCALARS = {"void", "u8", "u16", "u32", "s8", "s16", "s32", "u64", "s64", "f32", "f64", "char", "int", "short", "long", "unsigned", "signed", "float", "double", "M2C_UNK", "M2C_UNK8", "M2C_UNK16", "M2C_UNK32", "M2C_UNK64"} def known_structs(): """struct names declared in engine_types.h (forward decls + defs).""" names = set() p = os.path.join(REPO, "src/shared/engine_types.h") if os.path.exists(p): for m in re.finditer(r"\b(?:struct|union)\s+([A-Za-z_]\w*)", open(p).read()): names.add(m.group(1)) return names def resolvable(sig, structs): """True iff every type token in the signature is a scalar or a known shared struct/union.""" s = re.sub(r"\bfunc_[0-9A-Fa-f]+\b", "", sig) # drop the function name s = re.sub(r"\b[a-z_]\w*\s*(?=[,)])", "", s) # drop param names (lowercase, before , or )) for m in re.finditer(r"\b(struct|union)\s+([A-Za-z_]\w*)", s): if m.group(2) not in structs: return False s2 = re.sub(r"\b(struct|union)\s+\w+", "", s) # remove resolved struct refs for tok in re.findall(r"\b([A-Za-z_]\w*)\b", s2): if tok not in SCALARS: return False return True def def_sigs(path, structs): """addr -> canonical prototype string, from every RESOLVABLE function DEFINITION in `path`.""" out = {} if not os.path.exists(path): return out for m in DEF_RE.finditer(open(path).read()): sig = re.sub(r"\s+", " ", m.group(1)).strip().rstrip("\\").strip() if not resolvable(sig, structs): continue a = int(re.search(r"func_([0-9A-Fa-f]+)", sig).group(1), 16) out.setdefault(a, sig) # first definition wins (engine_core.h is authoritative) return out def data_decls(paths): out = {} for p in paths: if not os.path.exists(p): continue for m in DATA_RE.finditer(open(p).read()): decl = re.sub(r"\s+", " ", m.group(1)).strip() sym = re.search(r"D_[0-9A-Fa-f]+", decl).group(0) out.setdefault(sym, decl) return out def main(): structs = known_structs() funcs = {} funcs.update(def_sigs(ECORE, structs)) # engine_core.h macro defs (authoritative) for a, s in def_sigs(OVC, structs).items(): # + inline 077-local defs funcs.setdefault(a, s) data = {k: v for k, v in data_decls([ECORE, OVC]).items() if resolvable(v, structs)} lines = ["#ifndef BFM_ENGINE_DECLS_H", "#define BFM_ENGINE_DECLS_H", "/* src/shared/engine_decls.h — GENERATED by tools/gen_engine_decls.py (Phase 16 lever 1).", " * Canonical declaration of every engine function + data symbol, so harvest drafts strip", " * their own decls and can never `conflicting types`. Regenerate after new matches land. */", '#include "common.h"', '#include "engine_types.h"', ""] lines.append(f"/* {len(funcs)} function prototypes */") for a in sorted(funcs): lines.append(funcs[a] + ";") lines.append("") lines.append(f"/* {len(data)} data symbols */") for sym in sorted(data): lines.append(f"extern {data[sym]};") lines += ["", "#endif"] open(OUT, "w").write("\n".join(lines) + "\n") print(f"wrote {OUT}: {len(funcs)} func protos + {len(data)} data decls") if __name__ == "__main__": main()