57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Claro checker/linter (gentle)."""
|
|
import sys, pathlib
|
|
|
|
OPEN = {"IF":"ENDIF", "DO":"DONE", "REPEAT":"UNTIL", "TEACH":"LEARNED", "TRY":"ENDTRY"}
|
|
KNOWN = set(list(OPEN.keys()) + list(OPEN.values()) + ["ELSE","CATCH",
|
|
"SAY","ASK","SET","CALL","RETURN","WAIT","HELP",
|
|
"IMPORT","EXPORT",
|
|
"ADD","GET","PUT","COUNT","HAS","KEYS",
|
|
"READ","WRITE","APPEND","EXISTS","DELETE","FILE",
|
|
"PARSE","MAKE","JSON",
|
|
"WITH","TO","AS","INTO","AT","KEY","TIMES","STOP"
|
|
])
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python tools/check.py <file.claro>")
|
|
return 2
|
|
p = pathlib.Path(sys.argv[1])
|
|
lines = p.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
stack = []
|
|
bad = 0
|
|
for i, line in enumerate(lines, start=1):
|
|
if "\t" in line:
|
|
print(f"Line {i}: contains a TAB. Use spaces.")
|
|
bad += 1
|
|
stripped = line.strip()
|
|
if not stripped or stripped.startswith("#"):
|
|
continue
|
|
head = stripped.split()[0].upper()
|
|
if head not in KNOWN:
|
|
print(f"Line {i}: warning: unknown keyword '{head}'.")
|
|
if head in OPEN:
|
|
stack.append((head, i))
|
|
elif head in OPEN.values():
|
|
if not stack:
|
|
print(f"Line {i}: '{head}' without opener.")
|
|
bad += 1
|
|
else:
|
|
op, oi = stack.pop()
|
|
exp = OPEN[op]
|
|
if head != exp:
|
|
print(f"Line {i}: '{head}' closes '{op}' from line {oi}, expected '{exp}'.")
|
|
bad += 1
|
|
if stack:
|
|
for op, oi in stack:
|
|
print(f"Line {oi}: '{op}' not closed (expected {OPEN[op]}).")
|
|
bad += len(stack)
|
|
if bad:
|
|
print(f"\nFound {bad} issue(s).")
|
|
return 1
|
|
print("No problems found.")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|