Felix's Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub fffelix-huang/CP-stuff

:warning: tools/expander.py

Code

import argparse
import os
import re
import sys

from logging import Logger, basicConfig, getLogger
from pathlib import Path
from typing import List, Set

logger = getLogger(__name__)

class Expander:
	header_regex = re.compile(r"^\s*#include\s*[<\"]([^>\"]+)[>\"]\s*$")

	def __init__(self, lib_paths: List[Path]):
		self.lib_paths = lib_paths
		self.included_headers = set()

	def remove_ignored_lines(self, source: List[str]) -> List[str]:
		result = []

		for line in source:
			if line.strip() == "#pragma once":
				continue

			if line.strip().startswith("//"):
				continue

			if line.find("debug") != -1:
				continue

			result.append(line)

		return result

	# Returns the full path of the header, or None if doesn't exists.
	def find_header(self, header: Path) -> Path:
		for lib_path in self.lib_paths:
			path = lib_path / header

			if path.exists() and path.is_file():
				return path

		return None

	def expand_header(self, header: Path, directory: Path) -> List[str]:
		if header.resolve() in self.included_headers:
			logger.info(f"Already included: {str(header.resolve())}")
			return []

		self.included_headers.add(header.resolve())
		logger.info(f"Include: {str(header.resolve())}")

		file_path_str = str(header)
		index_of_last_slash = file_path_str.rfind('/')
		directory_path = Path(file_path_str[:index_of_last_slash])

		source = open(str(header)).read().splitlines()
		source = self.remove_ignored_lines(source)

		result = []

		for line in source:
			match = self.header_regex.match(line)

			if match:
				# Case 1: Relative Path
				path = directory / match.group(1)

				if path.exists() and path.is_file():
					result.extend(self.expand_header(header=path, directory=path.parent))
					continue

				# Case 2: Included Path
				path = self.find_header(Path(match.group(1)))

				if path != None:
					result.extend(self.expand_header(header=path, directory=path.parent))
					continue

				# Case 3: Standard Header File
				if Path(match.group(1)) in self.included_headers:
					continue;

				self.included_headers.add(Path(match.group(1)))

			result.append(line)

		return result

	def expand(self, source: Path, directory: Path) -> List[str]:
		source = open(str(source)).read().splitlines()
		source = self.remove_ignored_lines(source)

		result = []

		for line in source:
			match = self.header_regex.match(line)

			if match:
				# Case 1: Relative Path
				path = directory / match.group(1)

				if path.exists() and path.is_file():
					result.extend(self.expand_header(header=path, directory=path.parent))
					continue

				# Case 2: Included Path
				path = self.find_header(Path(match.group(1)))

				if path != None:
					result.extend(self.expand_header(header=path, directory=path.parent))
					continue

				# Case 3: Standard Header File
				if Path(match.group(1)) in self.included_headers:
					continue;

				self.included_headers.add(Path(match.group(1)))

			result.append(line)

		return result

if __name__ == "__main__":
	basicConfig(
		format="%(asctime)s [%(levelname)s] %(message)s",
		datefmt="%H:%M:%S",
		level=os.getenv("LOG_LEVEL", "INFO")
	)

	parser = argparse.ArgumentParser(description=
		"""
			expander.py performs the following functions:

			1. Removes ignored lines, or any lines containing the word "debug".
			2. Merge all custom header files into a single file.
		"""
	)

	parser.add_argument("source", help="Source code file")
	parser.add_argument("destination", help="Destination file to store result")
	parser.add_argument("--lib", help="Path to libraries, seperated with \':\'. Example: /path/to/library1:/path/to/library2")
	parser.add_argument("--author", help="Author")

	opts = parser.parse_args()

	lib_paths = []

	if opts.lib:
		paths = list(map(Path, opts.lib.split(':')))

		for path in paths:
			logger.info(f"Add Library Path: {str(path)}")

		lib_paths.extend(paths)

	expander = Expander(lib_paths)
	output = expander.expand(source=Path(opts.source), directory=Path.cwd())

	if opts.author:
		output.insert(0, "// Author: " + opts.author)

	result = "\n".join(output)

	with open(opts.destination, "w") as f:
		f.write(result)

	logger.info(f"{len(output)} lines generated to {opts.destination}.")
Traceback (most recent call last):
  File "/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/site-packages/onlinejudge_verify/documentation/build.py", line 71, in _render_source_code_stat
    bundled_code = language.bundle(stat.path, basedir=basedir, options={'include_paths': [basedir]}).decode()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.12.5/x64/lib/python3.12/site-packages/onlinejudge_verify/languages/python.py", line 96, in bundle
    raise NotImplementedError
NotImplementedError
Back to top page