125
run.py
125
run.py
@@ -26,6 +26,15 @@ BUILD_DIR = "./build"
|
||||
|
||||
@unique
|
||||
class Language(Enum):
|
||||
"""
|
||||
실행 가능한 언어(Language)를 정의하는 클래스입니다. 각 언어는 고유 문자열 값을 가짐.
|
||||
- Python: py
|
||||
- C: c
|
||||
- C++: cpp
|
||||
- Rust: rs
|
||||
- Kotlin: kt
|
||||
- Lua: lua
|
||||
"""
|
||||
PYTHON = "py"
|
||||
C = "c"
|
||||
CPP = "cpp"
|
||||
@@ -37,6 +46,9 @@ class Language(Enum):
|
||||
|
||||
@property
|
||||
def build_command(self):
|
||||
"""
|
||||
각 언어에 대한 빌드 명령어.
|
||||
"""
|
||||
return {
|
||||
Language.PYTHON: [
|
||||
"python",
|
||||
@@ -67,10 +79,16 @@ class Language(Enum):
|
||||
|
||||
@property
|
||||
def working_dir(self):
|
||||
"""
|
||||
각 언어의 작업 디렉토리
|
||||
"""
|
||||
return f"{SRC_SPACE_DIR}/src-{self.value}"
|
||||
|
||||
@property
|
||||
def build_executable(self):
|
||||
"""
|
||||
각 언어의 빌드 후 생성되는 실행 파일의 이름.
|
||||
"""
|
||||
return {
|
||||
Language.PYTHON: "py.pyc",
|
||||
Language.C: "c.out",
|
||||
@@ -79,19 +97,27 @@ class Language(Enum):
|
||||
Language.KOTLIN: "kt.jar",
|
||||
Language.LUA: "lua.luac",
|
||||
}[self]
|
||||
|
||||
def run_executable_command(self, executable_loc):
|
||||
|
||||
@property
|
||||
def executable_command(self):
|
||||
"""
|
||||
각 언어에 대한 실행 명령어.
|
||||
"""
|
||||
return {
|
||||
Language.PYTHON: ["python", f"{executable_loc}"],
|
||||
Language.C: [f"{executable_loc}"],
|
||||
Language.CPP: [f"{executable_loc}"],
|
||||
Language.RUST: [f"{executable_loc}"],
|
||||
Language.KOTLIN: ["java", "-jar", f"{executable_loc}"],
|
||||
Language.LUA: ["lua", f"{executable_loc}"],
|
||||
Language.PYTHON: ["python", f"{BUILD_DIR}/{self.build_executable}"],
|
||||
Language.C: [f"{BUILD_DIR}/{self.build_executable}"],
|
||||
Language.CPP: [f"{BUILD_DIR}/{self.build_executable}"],
|
||||
Language.RUST: [f"{BUILD_DIR}/{self.build_executable}"],
|
||||
Language.KOTLIN: ["java", "-jar", f"{BUILD_DIR}/{self.build_executable}"],
|
||||
Language.LUA: ["lua", f"{BUILD_DIR}/{self.build_executable}"],
|
||||
}[self]
|
||||
|
||||
@staticmethod
|
||||
def convert_ext(ext: str):
|
||||
"""
|
||||
확장자를 언어 Enum으로 변환하는 메소드.
|
||||
만약 확장자가 정의된 언어에 해당하지 않으면 UNDEFINED를 반환.
|
||||
"""
|
||||
match ext:
|
||||
case "py":
|
||||
return Language.PYTHON
|
||||
@@ -109,8 +135,14 @@ class Language(Enum):
|
||||
return Language.UNDEFINED
|
||||
|
||||
@staticmethod
|
||||
def convert_name(name: str):
|
||||
match name.lower():
|
||||
def convert_name(format_name: str):
|
||||
"""
|
||||
다양한 언어 형식 이름을 언어 Enum으로 변환하는 메소드.
|
||||
convert_ext의 Super Set임.
|
||||
"""
|
||||
if Language.convert_ext(format_name) is not Language.UNDEFINED:
|
||||
return Language.convert_ext(format_name)
|
||||
match format_name.lower():
|
||||
case "py" | "py3" | "python" | "python3":
|
||||
return Language.PYTHON
|
||||
case "c":
|
||||
@@ -131,36 +163,41 @@ def natural_sort_key(s: str):
|
||||
"""Natural sort key: splits string into text/number chunks for proper ordering."""
|
||||
return [int(c) if c.isdigit() else c.lower() for c in re.split(r"(\d+)", s)]
|
||||
|
||||
def parse_range_string(range_str: str) -> list[int]:
|
||||
"""
|
||||
범위 문자열을 정수 리스트로 변환.
|
||||
* ..N: 1부터 N까지
|
||||
* M..N: M부터 N까지
|
||||
* N: 단일 숫자
|
||||
"""
|
||||
if range_str.startswith(".."):
|
||||
end_str = range_str[2:]
|
||||
if not end_str.isdigit():
|
||||
raise ValueError("Invalid range format")
|
||||
return list(range(1, int(end_str) + 1))
|
||||
|
||||
def parse_range_string(range_str):
|
||||
try:
|
||||
if range_str.startswith(".."):
|
||||
# 형식: "..N" → 1부터 N까지
|
||||
end = int(range_str[2:])
|
||||
return list(range(1, end + 1))
|
||||
else:
|
||||
# 형식: "M..N" → M부터 N까지
|
||||
parts = range_str.split("..")
|
||||
if len(parts) == 1:
|
||||
# 단일 숫자 (예: "1")
|
||||
return [int(parts[0])]
|
||||
elif len(parts) == 2:
|
||||
# 범위 (예: "3..5")
|
||||
start = int(parts[0])
|
||||
end = int(parts[1])
|
||||
return list(range(start, end + 1))
|
||||
else:
|
||||
raise ValueError("Invalid range format")
|
||||
except ValueError as e:
|
||||
print(f"Error: {e}")
|
||||
return []
|
||||
parts = range_str.split("..")
|
||||
if len(parts) == 1:
|
||||
if not parts[0].isdigit():
|
||||
raise ValueError("Invalid range format")
|
||||
return [int(parts[0])]
|
||||
elif len(parts) == 2:
|
||||
if not parts[0].isdigit() or not parts[1].isdigit():
|
||||
raise ValueError("Invalid range format")
|
||||
return list(range(int(parts[0]), int(parts[1]) + 1))
|
||||
else:
|
||||
raise ValueError("Invalid range format")
|
||||
|
||||
|
||||
def parse_range_string_list(str_list):
|
||||
result = []
|
||||
def parse_range_string_list(str_list) -> list[int]:
|
||||
"""
|
||||
여러 범위 문자열을 정수 리스트로 변환하는 함수; 이때 중복은 제거함.
|
||||
* e.g. ["1..3", "5", "7..9"] -> [1, 2, 3, 5, 7, 8, 9]
|
||||
"""
|
||||
result = set()
|
||||
for s in str_list:
|
||||
result.extend(parse_range_string(s))
|
||||
return result
|
||||
result.update(parse_range_string(s))
|
||||
return list(result)
|
||||
|
||||
|
||||
@click.group()
|
||||
@@ -173,6 +210,9 @@ def cli():
|
||||
@click.option("--target", "-t", default=["1"], multiple=True)
|
||||
@click.option("--verbose/--no-verbose", "-v/-nv", default=True)
|
||||
def run(from_: str, target: str, verbose: bool):
|
||||
"""
|
||||
지정된 언어로 빌드 및 실행하는 명령어
|
||||
"""
|
||||
# Language 확인
|
||||
from_language: Language = Language.convert_name(from_)
|
||||
|
||||
@@ -223,8 +263,6 @@ def run(from_: str, target: str, verbose: bool):
|
||||
click.echo(">>>>>> [Error while BUILD process] >>>>>>")
|
||||
raise click.ClickException(e.stderr)
|
||||
|
||||
build_executable = from_language.build_executable
|
||||
|
||||
for tc in tcs:
|
||||
if not (
|
||||
os.path.isfile(f"{TC_DIR}/{tc}.in") and os.path.isfile(f"{TC_DIR}/{tc}.out")
|
||||
@@ -236,9 +274,7 @@ def run(from_: str, target: str, verbose: bool):
|
||||
with open(f_in_path, "r", encoding="utf-8") as f_in:
|
||||
try:
|
||||
stdout = subprocess.run(
|
||||
from_language.run_executable_command(
|
||||
f"{BUILD_DIR}/{build_executable}"
|
||||
),
|
||||
from_language.executable_command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
@@ -268,7 +304,9 @@ def run(from_: str, target: str, verbose: bool):
|
||||
@click.option("--force", "-f", is_flag=True)
|
||||
def load(file: str, to: str, from_: str, force: bool):
|
||||
"""
|
||||
load specific file into corresponding src-space
|
||||
파일을 각 언어의 src-space로 이동시키는 명령어.
|
||||
이 명령어는 다음 과정을 수행함.
|
||||
|
||||
"""
|
||||
|
||||
# 파일의 src-space가 어딘지 확인
|
||||
@@ -338,7 +376,7 @@ def load(file: str, to: str, from_: str, force: bool):
|
||||
file_is_completed = True
|
||||
file_loc = f"{STORAGE_DIR}/{from_}/{to_language.value}/completed/{file_name}.{to_language.value}"
|
||||
# 존재하면 move
|
||||
# 존재하지 않으면 빈 파일을 생성하고
|
||||
# 존재하지 않으면 빈 파일(또는 템플릿 파일)을 생성하고
|
||||
# state에 기록함
|
||||
if file_is_exist:
|
||||
with (
|
||||
@@ -753,6 +791,7 @@ def find(keyword: str, completed: bool | None):
|
||||
click.echo(f" {status} {file_name}.{lang_name}")
|
||||
|
||||
|
||||
|
||||
cli.add_command(run)
|
||||
cli.add_command(load)
|
||||
cli.add_command(init)
|
||||
|
||||
Reference in New Issue
Block a user