Plantilla para generar esqueleto de soluciones
This commit is contained in:
305
src/template.py
Normal file
305
src/template.py
Normal file
@@ -0,0 +1,305 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Creation of templates for Exercitium problems
|
||||
"""
|
||||
|
||||
import ast
|
||||
import re
|
||||
import shutil
|
||||
from argparse import ArgumentParser
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Example:
|
||||
input_value: object
|
||||
expected_value: object
|
||||
|
||||
|
||||
def load_instructions(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def parse_instructions(text: str) -> tuple[str, list[Example]]:
|
||||
examples: list[Example] = []
|
||||
function_name: str | None = None
|
||||
|
||||
for raw_line in text.splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
left, right = line.split("==", maxsplit=1)
|
||||
match = re.match(r"^--\s*([A-Za-z_][A-Za-z0-9_]*)\s+(.*)$", left.strip())
|
||||
if match is None:
|
||||
raise ValueError(f"Cannot parse instruction line: {raw_line}")
|
||||
|
||||
current_name = match.group(1)
|
||||
if function_name is None:
|
||||
function_name = current_name
|
||||
elif function_name != current_name:
|
||||
raise ValueError("All instruction lines must use the same function name")
|
||||
|
||||
examples.append(
|
||||
Example(
|
||||
input_value=ast.literal_eval(match.group(2).strip()),
|
||||
expected_value=ast.literal_eval(right.strip()),
|
||||
)
|
||||
)
|
||||
|
||||
if function_name is None:
|
||||
raise ValueError("No examples found in instructions")
|
||||
|
||||
return function_name, examples
|
||||
|
||||
|
||||
def infer_python_type(value: object) -> str:
|
||||
if isinstance(value, bool):
|
||||
return "bool"
|
||||
if isinstance(value, int):
|
||||
return "int"
|
||||
if isinstance(value, str):
|
||||
return "str"
|
||||
if isinstance(value, list):
|
||||
inner_type = infer_python_type(value[0]) if value else "object"
|
||||
return f"list[{inner_type}]"
|
||||
return "object"
|
||||
|
||||
|
||||
def infer_go_type(value: object) -> str:
|
||||
if isinstance(value, bool):
|
||||
return "bool"
|
||||
if isinstance(value, int):
|
||||
return "int"
|
||||
if isinstance(value, str):
|
||||
return "string"
|
||||
if isinstance(value, list):
|
||||
inner_type = infer_go_type(value[0]) if value else "int"
|
||||
return f"[]{inner_type}"
|
||||
raise TypeError(f"Unsupported Go type: {value!r}")
|
||||
|
||||
|
||||
def infer_rust_type(value: object) -> str:
|
||||
if isinstance(value, bool):
|
||||
return "bool"
|
||||
if isinstance(value, int):
|
||||
return "i32"
|
||||
if isinstance(value, str):
|
||||
return "&str"
|
||||
if isinstance(value, list):
|
||||
inner_type = infer_rust_type(value[0]) if value else "i32"
|
||||
return f"Vec<{inner_type}>"
|
||||
raise TypeError(f"Unsupported Rust type: {value!r}")
|
||||
|
||||
|
||||
def infer_rust_param_type(value: object) -> str:
|
||||
if isinstance(value, list):
|
||||
inner_type = infer_rust_type(value[0]) if value else "i32"
|
||||
return f"&[{inner_type}]"
|
||||
return infer_rust_type(value)
|
||||
|
||||
|
||||
def render_python(value: object) -> str:
|
||||
return repr(value)
|
||||
|
||||
|
||||
def render_julia(value: object) -> str:
|
||||
if isinstance(value, str):
|
||||
return f'"{value}"'
|
||||
if isinstance(value, list):
|
||||
return "[" + ", ".join(render_julia(item) for item in value) + "]"
|
||||
return str(value)
|
||||
|
||||
|
||||
def render_go(value: object) -> str:
|
||||
if isinstance(value, str):
|
||||
return f'"{value}"'
|
||||
if isinstance(value, list):
|
||||
return (
|
||||
f"{infer_go_type(value)}{{"
|
||||
+ ", ".join(render_go(item) for item in value)
|
||||
+ "}"
|
||||
)
|
||||
return str(value)
|
||||
|
||||
|
||||
def render_rust(value: object) -> str:
|
||||
if isinstance(value, str):
|
||||
return f'"{value}"'
|
||||
if isinstance(value, list):
|
||||
return "vec![" + ", ".join(render_rust(item) for item in value) + "]"
|
||||
return str(value)
|
||||
|
||||
|
||||
def build_python_checks(function_name: str, examples: list[Example]) -> str:
|
||||
return "\n\n".join(
|
||||
[
|
||||
f"check = {render_python(example.input_value)}\n"
|
||||
f"print({function_name}(check)) # {render_python(example.expected_value)}"
|
||||
for example in examples
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_julia_checks(function_name: str, examples: list[Example]) -> str:
|
||||
return "\n\n".join(
|
||||
[
|
||||
f"check = {render_julia(example.input_value)}\n"
|
||||
f"println({function_name}(check)) # {render_julia(example.expected_value)}"
|
||||
for example in examples
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_rust_checks(function_name: str, examples: list[Example]) -> str:
|
||||
return "\n\n".join(
|
||||
[
|
||||
f" let check = {render_rust(example.input_value)};\n"
|
||||
f' println!("{{:?}}", {function_name}(&check)); // {render_python(example.expected_value)}'
|
||||
for example in examples
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_go_checks(function_name: str, examples: list[Example]) -> str:
|
||||
checks: list[str] = []
|
||||
|
||||
for index, example in enumerate(examples):
|
||||
assign_op = ":=" if index == 0 else "="
|
||||
checks.append(
|
||||
f" check {assign_op} {render_go(example.input_value)}\n"
|
||||
f" fmt.Println({function_name}(check)) // {render_python(example.expected_value)}"
|
||||
)
|
||||
|
||||
return "\n\n".join(checks)
|
||||
|
||||
|
||||
def comment_block(prefix: str, text: str) -> str:
|
||||
return "\n".join(
|
||||
f"{prefix} {line}".rstrip()
|
||||
for line in text.splitlines()
|
||||
)
|
||||
|
||||
|
||||
def create_python(
|
||||
problem_py: Path,
|
||||
function_name: str,
|
||||
examples: list[Example],
|
||||
) -> None:
|
||||
input_type = infer_python_type(examples[0].input_value)
|
||||
return_type = infer_python_type(examples[0].expected_value)
|
||||
|
||||
template = "\n".join(
|
||||
[
|
||||
f"def {function_name}(values: {input_type}) -> {return_type}:",
|
||||
" return []",
|
||||
"",
|
||||
build_python_checks(function_name, examples),
|
||||
"",
|
||||
]
|
||||
)
|
||||
problem_py.write_text(template, encoding="utf-8")
|
||||
|
||||
|
||||
def create_julia(
|
||||
problem_jl: Path,
|
||||
function_name: str,
|
||||
examples: list[Example],
|
||||
) -> None:
|
||||
template = "\n".join(
|
||||
[
|
||||
f"function {function_name}(values)",
|
||||
" return []",
|
||||
"end",
|
||||
"",
|
||||
build_julia_checks(function_name, examples),
|
||||
"",
|
||||
]
|
||||
)
|
||||
problem_jl.write_text(template, encoding="utf-8")
|
||||
|
||||
|
||||
def create_rust(
|
||||
problem_rs: Path,
|
||||
function_name: str,
|
||||
examples: list[Example],
|
||||
) -> None:
|
||||
input_type = infer_rust_param_type(examples[0].input_value)
|
||||
return_type = infer_rust_type(examples[0].expected_value)
|
||||
|
||||
template = "\n".join(
|
||||
[
|
||||
f"fn {function_name}(values: {input_type}) -> {return_type} {{",
|
||||
" let _ = values;",
|
||||
" vec![]",
|
||||
"}",
|
||||
"",
|
||||
"fn main() {",
|
||||
build_rust_checks(function_name, examples),
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
)
|
||||
problem_rs.write_text(template, encoding="utf-8")
|
||||
|
||||
|
||||
def create_go(
|
||||
problem_go: Path,
|
||||
function_name: str,
|
||||
examples: list[Example],
|
||||
) -> None:
|
||||
input_type = infer_go_type(examples[0].input_value)
|
||||
return_type = infer_go_type(examples[0].expected_value)
|
||||
|
||||
template = "\n".join(
|
||||
[
|
||||
"package main",
|
||||
"",
|
||||
'import "fmt"',
|
||||
"",
|
||||
f"func {function_name}(values {input_type}) {return_type} " + "{",
|
||||
" _ = values",
|
||||
f" return {return_type}" + "{}",
|
||||
"}",
|
||||
"",
|
||||
"func main() {",
|
||||
build_go_checks(function_name, examples),
|
||||
"}",
|
||||
"",
|
||||
]
|
||||
)
|
||||
problem_go.write_text(template, encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--problem-number",
|
||||
dest="problem_number",
|
||||
type=int,
|
||||
required=True,
|
||||
help="number of the problem to solve",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
base_dir = Path(__file__).resolve().parent
|
||||
instructions = load_instructions(base_dir / "instructions.txt")
|
||||
|
||||
problem_stem = f"{args.problem_number:03d}"
|
||||
problem_py = base_dir / f"{problem_stem}.py"
|
||||
problem_jl = base_dir / f"{problem_stem}.jl"
|
||||
problem_rs = base_dir / f"{problem_stem}.rs"
|
||||
problem_go = base_dir / f"{problem_stem}.go"
|
||||
|
||||
function_name, examples = parse_instructions(instructions)
|
||||
create_python(problem_py, function_name, examples)
|
||||
create_julia(problem_jl, function_name, examples)
|
||||
create_rust(problem_rs, function_name, examples)
|
||||
create_go(problem_go, function_name, examples)
|
||||
|
||||
shutil.move(problem_py, base_dir / "Python")
|
||||
shutil.move(problem_jl, base_dir / "Julia")
|
||||
shutil.move(problem_rs, base_dir / "Rust")
|
||||
shutil.move(problem_go, base_dir / "Go")
|
||||
Reference in New Issue
Block a user