aboutsummaryrefslogtreecommitdiff
path: root/pack_submission.py
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.001@gmail.com>2026-09-15 08:16:43 +0800
committerRichard Qin <richard.qin.001@gmail.com>2026-09-15 08:16:43 +0800
commit4022e5c95210631e4aceb08eaf9d9071aa5aefc4 (patch)
treea5922559836e45fb3459a3d070367912e1035b1a /pack_submission.py
downloadrcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.gz
rcc-4022e5c95210631e4aceb08eaf9d9071aa5aefc4.tar.xz
Set up a git repoHEADmaster
Diffstat (limited to 'pack_submission.py')
-rw-r--r--pack_submission.py157
1 files changed, 157 insertions, 0 deletions
diff --git a/pack_submission.py b/pack_submission.py
new file mode 100644
index 0000000..7314e5a
--- /dev/null
+++ b/pack_submission.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+"""Pack rcc source files into a zip for contest submission, stripping comments.
+
+Source code for RCC-Legacy.
+Copyright (C) 2026 Richard Qin
+
+This file is part of RCC.
+
+RCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+RCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with RCC; see the file LICENSE. If not see
+<http://www.gnu.org/licenses/>.
+
+Includes:
+ - src/**/*.cc, src/**/*.h
+ - include/**/*.h
+
+Strips C/C++ comments (// and /* */) while preserving string/char literals.
+Does NOT modify any local files; output goes to ./rcc_submission.zip.
+
+Usage:
+ python3 pack_submission.py [output.zip]
+"""
+
+import re
+import sys
+import zipfile
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent
+PATTERNS = ("src/**/*.cc", "src/**/*.h", "include/**/*.h")
+
+
+def strip_comments(src: str) -> str:
+ out = []
+ i, n = 0, len(src)
+ while i < n:
+ c = src[i]
+
+ if c == '"':
+ out.append(c)
+ i += 1
+ while i < n:
+ ch = src[i]
+ out.append(ch)
+ i += 1
+ if ch == "\\" and i < n:
+ out.append(src[i])
+ i += 1
+ elif ch == '"':
+ break
+ continue
+
+ if c == "'":
+ out.append(c)
+ i += 1
+ while i < n:
+ ch = src[i]
+ out.append(ch)
+ i += 1
+ if ch == "\\" and i < n:
+ out.append(src[i])
+ i += 1
+ elif ch == "'":
+ break
+ continue
+
+ if c == "/" and i + 1 < n and src[i + 1] == "/":
+ while i < n and src[i] != "\n":
+ i += 1
+ continue
+
+ if c == "/" and i + 1 < n and src[i + 1] == "*":
+ i += 2
+ while i + 1 < n and not (src[i] == "*" and src[i + 1] == "/"):
+ i += 1
+ i += 2
+ continue
+
+ out.append(c)
+ i += 1
+ return "".join(out)
+
+
+def collapse_blank_lines(src: str) -> str:
+ src = re.sub(r"[ \t]+\n", "\n", src)
+ src = re.sub(r"\n{3,}", "\n\n", src)
+ return src
+
+
+_INCLUDE_RCC_RE = re.compile(r'(#\s*include\s*")rcc/([^"]+")')
+
+
+def rewrite_includes(src: str) -> str:
+ return _INCLUDE_RCC_RE.sub(r"\1\2", src)
+
+
+def main() -> int:
+ out_name = sys.argv[1] if len(sys.argv) > 1 else "rcc_submission.zip"
+ out_path = (ROOT / out_name).resolve()
+
+ if out_path.suffix != ".zip":
+ print(f"error: refuse to write non-.zip path: {out_path}", file=sys.stderr)
+ return 2
+ for guarded in ("src", "include"):
+ gpath = (ROOT / guarded).resolve()
+ try:
+ out_path.relative_to(gpath)
+ except ValueError:
+ continue
+ print(f"error: refuse to write inside source tree: {out_path}", file=sys.stderr)
+ return 2
+
+ files: list[Path] = []
+ for pat in PATTERNS:
+ files.extend(ROOT.glob(pat))
+ files = sorted(set(files))
+ if not files:
+ print("error: no source files found", file=sys.stderr)
+ return 1
+
+ if out_path.exists():
+ if not out_path.is_file():
+ print(f"error: output is not a file: {out_path}", file=sys.stderr)
+ return 2
+ out_path.unlink()
+
+ total_in = total_out = 0
+ with zipfile.ZipFile(out_path, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
+ for f in files:
+ rel = f.relative_to(ROOT).as_posix()
+ txt = f.read_text(encoding="utf-8", errors="replace")
+ stripped = rewrite_includes(collapse_blank_lines(strip_comments(txt)))
+ zf.writestr(rel, stripped)
+ total_in += len(txt)
+ total_out += len(stripped)
+ print(f" {rel:50s} {len(txt):>8d} -> {len(stripped):>8d}")
+
+ print(
+ f"\n{len(files)} files, {total_in} -> {total_out} bytes "
+ f"({100.0 * total_out / total_in:.1f}%), zip = {out_path.stat().st_size} bytes"
+ )
+ print(f"output: {out_path}")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())