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)
|
||||
|
||||
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}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user