File size: 2,180 Bytes
872cf4d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | """Mechanical sanity check on report.tex.
There is no LaTeX engine on this machine, so this catches the errors a
compile would have caught: dangling \\ref, missing \\input or figure files,
unbalanced braces or environments, and unclosed inline math.
python report/check_tex.py
"""
import re
import sys
from collections import Counter
from pathlib import Path
HERE = Path(__file__).resolve().parent
def main():
src = (HERE / 'report.tex').read_text(encoding='utf-8')
# strip comments, so commented-out examples don't trip the checks
body = '\n'.join(re.sub(r'(?<!\\)%.*$', '', ln) for ln in src.splitlines())
problems = 0
labels = set(re.findall(r'\\label\{([^}]+)\}', body))
refs = set(re.findall(r'\\(?:eq)?ref\{([^}]+)\}', body))
dangling = sorted(refs - labels)
print('undefined refs :', dangling or 'none')
problems += len(dangling)
print('unused labels :', sorted(labels - refs) or 'none')
missing = [f for f in re.findall(r'\\input\{([^}]+)\}', body)
if not (HERE / (f + '.tex')).exists()]
print('missing inputs :', missing or 'none')
problems += len(missing)
figs = re.findall(r'\\includegraphics(?:\[[^\]]*\])?\{([^}]+)\}', body)
missing = [f for f in figs if not (HERE / 'figures' / f).exists()]
print('missing figures:', missing or 'none')
problems += len(missing)
balance = body.count('{') - body.count('}')
print('brace balance :', balance)
problems += abs(balance)
counts = Counter()
for kind, name in re.findall(r'\\(begin|end)\{([^}]+)\}', body):
counts[name] += 1 if kind == 'begin' else -1
bad_envs = {k: v for k, v in counts.items() if v}
print('unbalanced envs:', bad_envs or 'none')
problems += len(bad_envs)
odd = [i + 1 for i, ln in enumerate(body.splitlines())
if len(re.findall(r'(?<!\\)\$', ln)) % 2]
print('odd $ lines :', odd or 'none')
problems += len(odd)
print()
print('OK' if problems == 0 else f'{problems} problem(s)')
return 0 if problems == 0 else 1
if __name__ == '__main__':
sys.exit(main())
|