From 503eb703590e712b9a351545b726265b4c1e681b Mon Sep 17 00:00:00 2001 From: gamer147 Date: Mon, 6 Jul 2026 13:01:20 -0400 Subject: [PATCH] feat(opcodes): linter (dangling-ref, confidence-ceiling, vocabulary) Co-Authored-By: Claude Opus 4.8 (1M context) --- tools/opcodes_model.py | 29 +++++++++++++++++++++ tools/test_opcodes.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/tools/opcodes_model.py b/tools/opcodes_model.py index 26d18f4..d6ea358 100644 --- a/tools/opcodes_model.py +++ b/tools/opcodes_model.py @@ -67,6 +67,35 @@ def load(path) -> Model: ) return Model(meta=data.get("meta", {}), opcodes=ops) +def lint(model: Model) -> tuple[list[str], list[str]]: + """Return (errors, warnings). Errors: bad vocabulary, dangling depends_on. + Warnings: confidence exceeds the minimum confidence among its dependencies.""" + errors: list[str] = [] + warnings: list[str] = [] + ops = model.opcodes + for op, oc in sorted(ops.items()): + s = oc.semantics + if not s: + continue + tag = f"0x{op:x}" + if s.category not in CATEGORIES: + errors.append(f"{tag}: bad category {s.category!r}") + if s.source not in SOURCES: + errors.append(f"{tag}: bad source {s.source!r}") + if s.confidence not in CONFIDENCE: + errors.append(f"{tag}: bad confidence {s.confidence!r}") + for dep in s.depends_on: + if dep not in ops: + errors.append(f"{tag}: depends_on missing opcode 0x{dep:x}") + if s.confidence in CONFIDENCE: + dep_confs = [CONFIDENCE[ops[d].semantics.confidence] + for d in s.depends_on + if d in ops and ops[d].semantics + and ops[d].semantics.confidence in CONFIDENCE] + if dep_confs and CONFIDENCE[s.confidence] > min(dep_confs): + warnings.append(f"{tag}: confidence {s.confidence!r} exceeds dependency ceiling") + return errors, warnings + def dependents(model: Model) -> dict[int, list[int]]: """Reverse of depends_on: op -> [ops whose semantics depend on it].""" rev: dict[int, list[int]] = {op: [] for op in model.opcodes} diff --git a/tools/test_opcodes.py b/tools/test_opcodes.py index b67befe..3c186de 100644 --- a/tools/test_opcodes.py +++ b/tools/test_opcodes.py @@ -58,8 +58,67 @@ def test_load(): rev = M.dependents(m) check(rev.get(0x1f4) == [0x90], "dependents: 0x1f4 depended on by 0x90") +DANGLING = ''' +[[opcode]] +op = 0x10 +label = "x" +argc = 0 +[opcode.semantics] +name = "a" +category = "compute" +source = "inference" +confidence = "low" +depends_on = [0x99] +''' + +CEILING = ''' +[[opcode]] +op = 0x10 +label = "x" +argc = 0 +[opcode.semantics] +name = "low-op" +category = "compute" +source = "kelebek" +confidence = "low" +[[opcode]] +op = 0x11 +label = "y" +argc = 0 +[opcode.semantics] +name = "high-op" +category = "compute" +source = "inference" +confidence = "high" +depends_on = [0x10] +''' + +BADVOCAB = ''' +[[opcode]] +op = 0x10 +label = "x" +argc = 0 +[opcode.semantics] +name = "a" +category = "bogus" +source = "inference" +confidence = "low" +''' + +def test_lint(): + e, w = M.lint(M.load(write_tmp(DANGLING))) + check(any("0x99" in m for m in e), "dangling depends_on is an error") + e, w = M.lint(M.load(write_tmp(CEILING))) + check(any("0x11" in m for m in w), "confidence-ceiling violation is a warning") + check(e == [], "confidence-ceiling case has no errors") + e, w = M.lint(M.load(write_tmp(BADVOCAB))) + check(any("category" in m for m in e), "unknown category is an error") + e, w = M.lint(M.load(write_tmp(FIXTURE))) + check(e == [], "clean fixture has no lint errors") + def main(): test_load() + test_lint() print("FAILURES:", len(FAILS)) return 1 if FAILS else 0