Leplanner / code /report /check_tex.py
nottygian's picture
Push code package
872cf4d verified
Raw
History Blame Contribute Delete
2.18 kB
"""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())