| 13 | |
| 14 | |
| 15 | class Expander: |
| 16 | atcoder_include = re.compile( |
| 17 | r'#include\s*["<](atcoder/[a-z_]*(|.hpp))[">]\s*') |
| 18 | |
| 19 | include_guard = re.compile(r'#.*ATCODER_[A-Z_]*_HPP') |
| 20 | |
| 21 | def is_ignored_line(self, line) -> bool: |
| 22 | if self.include_guard.match(line): |
| 23 | return True |
| 24 | if line.strip() == "#pragma once": |
| 25 | return True |
| 26 | if line.strip().startswith('//'): |
| 27 | return True |
| 28 | return False |
| 29 | |
| 30 | def __init__(self, lib_paths: List[Path]): |
| 31 | self.lib_paths = lib_paths |
| 32 | |
| 33 | included = set() # type: Set[Path] |
| 34 | |
| 35 | def find_acl(self, acl_name: str) -> Path: |
| 36 | for lib_path in self.lib_paths: |
| 37 | path = lib_path / acl_name |
| 38 | if path.exists(): |
| 39 | return path |
| 40 | logger.error('cannot find: {}'.format(acl_name)) |
| 41 | raise FileNotFoundError() |
| 42 | |
| 43 | def expand_acl(self, acl_file_path: Path) -> List[str]: |
| 44 | if acl_file_path in self.included: |
| 45 | logger.info('already included: {}'.format(acl_file_path.name)) |
| 46 | return [] |
| 47 | self.included.add(acl_file_path) |
| 48 | logger.info('include: {}'.format(acl_file_path.name)) |
| 49 | |
| 50 | acl_source = open(str(acl_file_path)).read() |
| 51 | |
| 52 | result = [] # type: List[str] |
| 53 | for line in acl_source.splitlines(): |
| 54 | if self.is_ignored_line(line): |
| 55 | continue |
| 56 | |
| 57 | m = self.atcoder_include.match(line) |
| 58 | if m: |
| 59 | name = m.group(1) |
| 60 | result.extend(self.expand_acl(self.find_acl(name))) |
| 61 | continue |
| 62 | |
| 63 | result.append(line) |
| 64 | return result |
| 65 | |
| 66 | def expand(self, source: str, origname) -> str: |
| 67 | self.included = set() |
| 68 | result = [] # type: List[str] |
| 69 | linenum = 0 |
| 70 | for line in source.splitlines(): |
| 71 | linenum += 1 |
| 72 | m = self.atcoder_include.match(line) |