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