feat(opcodes): linter (dangling-ref, confidence-ceiling, vocabulary)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,35 @@ def load(path) -> Model:
|
|||||||
)
|
)
|
||||||
return Model(meta=data.get("meta", {}), opcodes=ops)
|
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]]:
|
def dependents(model: Model) -> dict[int, list[int]]:
|
||||||
"""Reverse of depends_on: op -> [ops whose semantics depend on it]."""
|
"""Reverse of depends_on: op -> [ops whose semantics depend on it]."""
|
||||||
rev: dict[int, list[int]] = {op: [] for op in model.opcodes}
|
rev: dict[int, list[int]] = {op: [] for op in model.opcodes}
|
||||||
|
|||||||
@@ -58,8 +58,67 @@ def test_load():
|
|||||||
rev = M.dependents(m)
|
rev = M.dependents(m)
|
||||||
check(rev.get(0x1f4) == [0x90], "dependents: 0x1f4 depended on by 0x90")
|
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():
|
def main():
|
||||||
test_load()
|
test_load()
|
||||||
|
test_lint()
|
||||||
print("FAILURES:", len(FAILS))
|
print("FAILURES:", len(FAILS))
|
||||||
return 1 if FAILS else 0
|
return 1 if FAILS else 0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user