#!/usr/bin/env python3 """그림 안 가 전부 이름인지 본다. `code-tables-diagrams.md` 의 자가 점검을 기계로 옮긴 것이다. 그림은 관계를 보이고 문장은 `` 와 옆 문단에 둔다. 길이가 아니라 **서술하느냐** 가 기준이다. python3 scripts/check-figure-text.py [프로젝트 ...] python3 scripts/check-figure-text.py --file 그림.svg `` 과 `<desc>` 는 화면을 못 보는 사람이 듣는 자리라 검사하지 않는다. """ from __future__ import annotations import argparse import glob import os import re import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) TEXT = re.compile(r"<text[^>]*>([^<]*)</text>") # 서술어로 끝난다 — `~있다`, `~막는다`, `~바꾼다` PREDICATE = re.compile( r"(?:이다|아니다|있다|없다|한다|된다|본다|같다|다르다|늘어난다|줄어든다" r"|막는다|바꾼다|만든다|넣는다|뺀다|남는다|생긴다|끝난다)$") # 조사로 두 대상을 잇는다 — `A를 B로`, `A에서 B까지` # 관형형 어미(`깊은 페이지`)와 겹치는 는·은·이·가 는 넣지 않는다. 그쪽은 서술어 규칙이 잡는다 JOINING = re.compile( r"[가-힣A-Za-z0-9_)\]]+(?:을|를|에서|에게|에는|으로|로서|로써|으로써|마다|부터|까지|보다|처럼)\s+\S") # 문장 부호로 끝난다. `…` 과 축약형 `(?,…)` 은 뺀다 SENTENCE_END = re.compile(r"[.?!]$") def offences(label: str) -> list[str]: t = label.strip() if not t: return [] found = [] if SENTENCE_END.search(t) and not t.endswith(("…", "..")): found.append("문장 부호로 끝난다") if PREDICATE.search(t): found.append("서술어가 있다") if JOINING.search(t): found.append("조사로 두 대상을 잇는다") return found def check(path: str) -> list[tuple[str, list[str]]]: body = open(path, encoding="utf-8").read() out = [] for label in TEXT.findall(body): why = offences(label) if why: out.append((label.strip(), why)) return out def svgs_of(project: str) -> list[str]: assets = os.path.join(ROOT, "docs", project, "final", "assets") return sorted(glob.glob(os.path.join(assets, "**", "*.svg"), recursive=True)) def main() -> int: ap = argparse.ArgumentParser(description="그림 안 <text> 가 전부 이름인지 본다.") ap.add_argument("projects", nargs="*") ap.add_argument("--file", action="append", default=[], help="프로젝트 대신 SVG 파일을 직접 준다") args = ap.parse_args() targets = list(args.file) if not targets: projects = args.projects or sorted( d for d in os.listdir(os.path.join(ROOT, "docs")) if not d.startswith("_") and os.path.isdir(os.path.join(ROOT, "docs", d, "final"))) for project in projects: targets.extend(svgs_of(project)) total = 0 files = 0 for path in targets: hits = check(path) if not hits: continue files += 1 total += len(hits) print(f" {os.path.relpath(path, ROOT)}") for label, why in hits: print(f" {label} ← {' · '.join(why)}") checked = len(targets) if total: print(f"\nFIGURE TEXT: FAIL — 그림 {checked}장 · 문장 {total}건 / 그림 {files}장") print("그림 안 <text> 는 이름만 담는다. 문장은 <desc> 와 옆 문단으로 내린다.") return 1 print(f"FIGURE TEXT: PASS — 그림 {checked}장 · 문장 0건") return 0 if __name__ == "__main__": raise SystemExit(main())