#!/usr/bin/env python3 """ Render README.md from README.md.in by expanding @snippet directives. Directive format (in README.md.in): Snippet tags (in C++ source files): // --8<-- [start:snippet_name] ...content... // --8<-- [end:snippet_name] In Python files use # instead of //. """ import re import sys import textwrap from pathlib import Path ROOT = Path(__file__).parent.parent TEMPLATE = ROOT / "README.md.in" OUTPUT = ROOT / "README.md" DIRECTIVE_RE = re.compile(r'') LANG_MAP = {'.cpp': 'cpp', '.hpp': 'cpp', '.h': 'cpp', '.py': 'python', '.c': 'c'} def comment_prefix(path: Path) -> str: return '#' if path.suffix == '.py' else '//' def extract_snippet(file_path: Path, name: str) -> str: prefix = comment_prefix(file_path) open_tag = f'{prefix} --8<-- [start:{name}]' close_tag = f'{prefix} --8<-- [end:{name}]' text = file_path.read_text() lines = text.splitlines() in_snippet = False found = False result = [] for line in lines: stripped = line.strip() if stripped == open_tag: if in_snippet: raise ValueError(f"Nested open tag for '{name}' in {file_path}") in_snippet = True found = True continue if stripped == close_tag: if not in_snippet: raise ValueError(f"Unexpected close tag for '{name}' in {file_path}") in_snippet = False continue if in_snippet: result.append(line) if not found: raise ValueError(f"Snippet '{name}' not found in {file_path}") if in_snippet: raise ValueError(f"Unclosed snippet '{name}' in {file_path}") # Strip leading/trailing blank lines while result and not result[0].strip(): result.pop(0) while result and not result[-1].strip(): result.pop() # Dedent common leading whitespace content = textwrap.dedent('\n'.join(result)) return content def lang_for(file_path: Path) -> str: return LANG_MAP.get(file_path.suffix, '') def render(template: str) -> str: errors = [] def replace(m: re.Match) -> str: rel_path = m.group(1) name = m.group(2) file_path = ROOT / rel_path if not file_path.exists(): errors.append(f"File not found: {file_path}") return m.group(0) try: content = extract_snippet(file_path, name) except ValueError as e: errors.append(str(e)) return m.group(0) lang = lang_for(file_path) return f'```{lang}\n{content}\n```' result = DIRECTIVE_RE.sub(replace, template) if errors: for err in errors: print(f"ERROR: {err}", file=sys.stderr) sys.exit(1) return result def main() -> None: if not TEMPLATE.exists(): print(f"Error: {TEMPLATE} not found", file=sys.stderr) sys.exit(1) rendered = render(TEMPLATE.read_text()) OUTPUT.write_text(rendered) print(f"Rendered {TEMPLATE.name} → {OUTPUT.name}") if __name__ == '__main__': main()