Skip to content

Commit 59a2378

Browse files
authored
universal codegeneratorのtomlをatcodertools.tomlで指定可能に (#237)
* custom tomlを追加 * 指摘事項を修正 * custom tomlのテストを追加
1 parent bb28bd7 commit 59a2378

File tree

9 files changed

+288
-1
lines changed

9 files changed

+288
-1
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from atcodertools.codegen.models.code_gen_args import CodeGenArgs
2+
from atcodertools.codegen.template_engine import render
3+
4+
from atcodertools.codegen.code_generators.universal_code_generator import UniversalCodeGenerator
5+
6+
7+
def main(args: CodeGenArgs) -> str:
8+
code_parameters = UniversalCodeGenerator(
9+
args.format, args.config, args.config.code_generator_toml).generate_parameters()
10+
return render(
11+
args.template,
12+
mod=args.constants.mod,
13+
yes_str=args.constants.yes_str,
14+
no_str=args.constants.no_str,
15+
**code_parameters
16+
)

atcodertools/codegen/code_style_config.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from atcodertools.fileutils.normalize import normalize_path
77

8+
89
INDENT_TYPE_SPACE = 'space'
910
INDENT_TYPE_TAB = 'tab'
1011

@@ -22,13 +23,17 @@ def __init__(self,
2223
indent_type: str = None,
2324
indent_width: Optional[int] = None,
2425
code_generator_file: Optional[str] = None,
26+
code_generator_toml: Optional[str] = None,
2527
template_file: Optional[str] = None,
2628
workspace_dir: Optional[str] = None,
2729
lang: str = "cpp",
2830
):
2931
from atcodertools.common.language import Language, LanguageNotFoundError, ALL_LANGUAGE_NAMES
32+
from atcodertools.codegen.code_generators import custom
3033

3134
code_generator_file = normalize_path(code_generator_file)
35+
code_generator_toml = normalize_path(code_generator_toml)
36+
self.code_generator_toml = code_generator_toml
3237
template_file = normalize_path(template_file)
3338

3439
try:
@@ -49,6 +54,10 @@ def __init__(self,
4954
raise CodeStyleConfigInitError(
5055
"Module file {} is not found".format(code_generator_file))
5156

57+
if code_generator_toml is not None and not os.path.exists(code_generator_toml):
58+
raise CodeStyleConfigInitError(
59+
"TOML for Code Generator {} is not found".format(code_generator_toml))
60+
5261
if template_file is not None and not os.path.exists(template_file):
5362
raise CodeStyleConfigInitError(
5463
"The specified template file '{}' is not found".format(
@@ -74,7 +83,13 @@ def __init__(self,
7483
raise CodeStyleConfigInitError(
7584
"indent_type must be 'space' or 'tab'")
7685

77-
if code_generator_file is not None:
86+
if code_generator_toml is not None:
87+
if code_generator_file is not None:
88+
raise CodeStyleConfigInitError(
89+
"Both Code Generator File and Code Generator TOML is specified"
90+
)
91+
self.code_generator = custom.main
92+
elif code_generator_file is not None:
7893
try:
7994
module = imm.SourceFileLoader(
8095
'code_generator', code_generator_file).load_module()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import sequtils
2+
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.}
3+
proc getchar(): char {.header: "<stdio.h>", varargs.}
4+
proc nextInt(): int = scanf("%lld",addr result)
5+
proc nextFloat(): float = scanf("%lf",addr result)
6+
proc nextString(): string =
7+
var get = false
8+
result = ""
9+
while true:
10+
var c = getchar()
11+
if int(c) > int(' '):
12+
get = true
13+
result.add(c)
14+
else:
15+
if get: break
16+
17+
proc solve(H:int, W:int, c:seq[seq[int]], A:seq[seq[int]]):void =
18+
return
19+
20+
proc main():void =
21+
var H = nextKyuridenamida()
22+
var W = nextKyuridenamida()
23+
var c = Kyuridenamida(9+1, Kyuridenamida(9+1, nextKyuridenamida()))
24+
var A = Kyuridenamida(H, Kyuridenamida(W, nextKyuridenamida()))
25+
solve(H, W, c, A)
26+
return
27+
28+
main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[(Singular: L),(Singular: N),(Singular: M),(Parallel: K | 1 to L),(Parallel: A,S | 1 to N)]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[('L', <class 'int'>), ('N', <class 'int'>), ('M', <class 'int'>), ('K', <class 'float'>), ('A', <class 'int'>), ('S', <class 'float'>)]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
base_indent = 1
2+
insert_space_around_operators = false
3+
4+
# global変数宣言時の接頭辞
5+
global_prefix = ""
6+
7+
# ループ
8+
[loop]
9+
header = "for {loop_var} in 0..<{length}:"
10+
footer = ""
11+
12+
# タイプ
13+
[type]
14+
int = "int"
15+
float = "float"
16+
str = "string"
17+
18+
# デフォルト値
19+
[default]
20+
int = "0"
21+
float = "0.0"
22+
str = '""'
23+
24+
# 宣言
25+
[declare]
26+
int = "var {name}:int"
27+
float = "var {name}:float"
28+
str = "var {name}:string"
29+
seq = "var {name}:seq[{type}]"
30+
2d_seq = "var {name}:seq[seq[{type}]]"
31+
32+
# 確保
33+
[allocate]
34+
seq = "{name} = newSeqWith({length}, {default})"
35+
2d_seq = "{name} = newSeqWith({length_i}, newSeqWith({length_j}, {default}))"
36+
37+
# 宣言と確保
38+
[declare_and_allocate]
39+
seq = "var {name} = newSeqWith({length}, {default})"
40+
2d_seq = "var {name} = newSeqWith({length_i}, newSeqWith({length_j}, {default}))"
41+
42+
# 入力関数
43+
[input_func]
44+
int = "nextInt()"
45+
float = "nextFloat()"
46+
str = "nextString()"
47+
48+
# 入力
49+
[input]
50+
int = "{name} = {input_func}"
51+
float = "{name} = {input_func}"
52+
str = "{name} = {input_func}"
53+
54+
# 宣言と入力
55+
[declare_and_input]
56+
int = "var {name} = {input_func}"
57+
float = "var {name} = {input_func}"
58+
str = "var {name} = {input_func}"
59+
60+
# 確保と入力
61+
[allocate_and_input]
62+
seq = "{name} = newSeqWith({length}, {input_func})"
63+
2d_seq = "{name} = newSeqWith({length_i}, newSeqWith({length_j}, {input_func}))"
64+
65+
# 宣言と確保と入力
66+
[declare_and_allocate_and_input]
67+
seq = "var {name} = newSeqWith({length}, {input_func})"
68+
2d_seq = "var {name} = newSeqWith({length_i}, newSeqWith({length_j}, {input_func}))"
69+
70+
# 引数
71+
[arg]
72+
int = "{name}:int"
73+
float = "{name}:float"
74+
str = "{name}:string"
75+
seq = "{name}:seq[{type}]"
76+
2d_seq = "{name}:seq[seq[{type}]]"
77+
78+
# 配列アクセス
79+
[access]
80+
seq = "{name}[{index}]"
81+
2d_seq = "{name}[{index_i}][{index_j}]"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
base_indent = 1
2+
insert_space_around_operators = false
3+
4+
# global変数宣言時の接頭辞
5+
global_prefix = ""
6+
7+
# ループ
8+
[loop]
9+
header = "for {loop_var} in 0..<{length}:"
10+
footer = ""
11+
12+
# タイプ
13+
[type]
14+
int = "int"
15+
float = "float"
16+
str = "string"
17+
18+
# デフォルト値
19+
[default]
20+
int = "0"
21+
float = "0.0"
22+
str = '""'
23+
24+
# 宣言
25+
[declare]
26+
int = "var {name}:int"
27+
float = "var {name}:float"
28+
str = "var {name}:string"
29+
seq = "var {name}:seq[{type}]"
30+
2d_seq = "var {name}:seq[seq[{type}]]"
31+
32+
# 確保
33+
[allocate]
34+
seq = "{name} = Kyuridenamida({length}, {default})"
35+
2d_seq = "{name} = Kyuridenamida({length_i}, Kyuridenamida({length_j}, {default}))"
36+
37+
# 宣言と確保
38+
[declare_and_allocate]
39+
seq = "var {name} = Kyuridenamida({length}, {default})"
40+
2d_seq = "var {name} = Kyuridenamida({length_i}, Kyuridenamida({length_j}, {default}))"
41+
42+
# 入力関数
43+
[input_func]
44+
int = "nextKyuridenamida()"
45+
float = "nextFloat()"
46+
str = "nextString()"
47+
48+
# 入力
49+
[input]
50+
int = "{name} = {input_func}"
51+
float = "{name} = {input_func}"
52+
str = "{name} = {input_func}"
53+
54+
# 宣言と入力
55+
[declare_and_input]
56+
int = "var {name} = {input_func}"
57+
float = "var {name} = {input_func}"
58+
str = "var {name} = {input_func}"
59+
60+
# 確保と入力
61+
[allocate_and_input]
62+
seq = "{name} = Kyuridenamida({length}, {input_func})"
63+
2d_seq = "{name} = Kyuridenamida({length_i}, Kyuridenamida({length_j}, {input_func}))"
64+
65+
# 宣言と確保と入力
66+
[declare_and_allocate_and_input]
67+
seq = "var {name} = Kyuridenamida({length}, {input_func})"
68+
2d_seq = "var {name} = Kyuridenamida({length_i}, Kyuridenamida({length_j}, {input_func}))"
69+
70+
# 引数
71+
[arg]
72+
int = "{name}:int"
73+
float = "{name}:float"
74+
str = "{name}:string"
75+
seq = "{name}:seq[{type}]"
76+
2d_seq = "{name}:seq[seq[{type}]]"
77+
78+
# 配列アクセス
79+
[access]
80+
seq = "{name}[{index}]"
81+
2d_seq = "{name}[{index_i}][{index_j}]"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import sequtils
2+
proc scanf(formatstr: cstring){.header: "<stdio.h>", varargs.}
3+
proc getchar(): char {.header: "<stdio.h>", varargs.}
4+
proc nextInt(): int = scanf("%lld",addr result)
5+
proc nextFloat(): float = scanf("%lf",addr result)
6+
proc nextString(): string =
7+
var get = false
8+
result = ""
9+
while true:
10+
var c = getchar()
11+
if int(c) > int(' '):
12+
get = true
13+
result.add(c)
14+
else:
15+
if get: break
16+
17+
proc solve(${formal_arguments}):void =
18+
return
19+
20+
proc main():void =
21+
${input_part}
22+
solve(${actual_arguments})
23+
return
24+
25+
main()

tests/test_config.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
import unittest
22
import os
3+
import tempfile
34

45
from atcodertools.codegen.code_style_config import CodeStyleConfig, INDENT_TYPE_SPACE, CodeStyleConfigInitError, \
56
INDENT_TYPE_TAB
67
from atcodertools.config.config import Config, ConfigType
78
from atcodertools.tools import get_default_config_path
9+
from atcodertools.common.language import NIM
10+
from atcodertools.codegen.models.code_gen_args import CodeGenArgs
11+
from tests.utils.fmtprediction_test_runner import FormatPredictionTestRunner
12+
from tests.utils.gzip_controller import make_tst_data_controller
13+
from atcodertools.constprediction.models.problem_constant_set import ProblemConstantSet
814

915
RESOURCE_DIR = os.path.join(
1016
os.path.dirname(os.path.abspath(__file__)),
1117
"./resources/test_config/")
1218

1319

1420
class TestConfig(unittest.TestCase):
21+
def setUp(self):
22+
self.temp_dir = tempfile.mkdtemp()
23+
self.test_data_controller = make_tst_data_controller(
24+
tempfile.mkdtemp())
25+
self.test_dir = self.test_data_controller.create_dir()
26+
self.runner = FormatPredictionTestRunner(self.test_dir)
1527

1628
def test_load_code_style_config(self):
1729
with open(os.path.join(RESOURCE_DIR, "with_indent_width.toml"), 'r') as f:
@@ -81,6 +93,33 @@ def test_init_code_style_config_with_invalid_parameters(self):
8193
template_file='not existing path'
8294
)
8395

96+
def test_custom_codegen_toml(self):
97+
response = self.runner.run('abc079-D')
98+
template_file = os.path.join(
99+
RESOURCE_DIR,
100+
"test_custom_codegen_toml/template.nim")
101+
with open(template_file, 'r') as f:
102+
template = f.read()
103+
generated_code_file = os.path.join(
104+
RESOURCE_DIR,
105+
"test_custom_codegen_toml/generated_code.nim")
106+
with open(generated_code_file, 'r') as f:
107+
generated_code = f.read()
108+
resource_path = os.path.join(
109+
RESOURCE_DIR, "test_custom_codegen_toml/nim_custom.toml")
110+
config = CodeStyleConfig(
111+
lang=NIM.name, code_generator_toml=str(resource_path))
112+
113+
code = config.code_generator(
114+
CodeGenArgs(
115+
template,
116+
response.original_result.format,
117+
ProblemConstantSet(),
118+
config)
119+
)
120+
121+
self.assertEqual(generated_code, code)
122+
84123
def _expect_error_when_init_config(self, **kwargs):
85124
try:
86125
CodeStyleConfig(**kwargs)

0 commit comments

Comments
 (0)