at_coder_friends 0.6.2 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -2
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +53 -0
  5. data/Gemfile.lock +32 -36
  6. data/README.md +1 -1
  7. data/at_coder_friends.gemspec +5 -7
  8. data/config/default.yml +146 -72
  9. data/docs/CONFIGURATION.md +224 -140
  10. data/lib/at_coder_friends.rb +3 -0
  11. data/lib/at_coder_friends/cli.rb +8 -0
  12. data/lib/at_coder_friends/config_loader.rb +11 -3
  13. data/lib/at_coder_friends/context.rb +10 -6
  14. data/lib/at_coder_friends/emitter.rb +2 -2
  15. data/lib/at_coder_friends/generator/base.rb +42 -0
  16. data/lib/at_coder_friends/generator/cxx_builtin.rb +196 -143
  17. data/lib/at_coder_friends/generator/main.rb +8 -2
  18. data/lib/at_coder_friends/generator/ruby_builtin.rb +97 -51
  19. data/lib/at_coder_friends/parser/constraints.rb +1 -0
  20. data/lib/at_coder_friends/parser/input_format.rb +389 -188
  21. data/lib/at_coder_friends/parser/input_type.rb +92 -0
  22. data/lib/at_coder_friends/parser/main.rb +1 -0
  23. data/lib/at_coder_friends/parser/modulo.rb +1 -1
  24. data/lib/at_coder_friends/parser/sections.rb +3 -3
  25. data/lib/at_coder_friends/path_info.rb +51 -0
  26. data/lib/at_coder_friends/path_util.rb +0 -31
  27. data/lib/at_coder_friends/problem.rb +81 -6
  28. data/lib/at_coder_friends/scraping/agent.rb +11 -2
  29. data/lib/at_coder_friends/scraping/custom_test.rb +5 -6
  30. data/lib/at_coder_friends/scraping/submission.rb +6 -7
  31. data/lib/at_coder_friends/scraping/tasks.rb +8 -3
  32. data/lib/at_coder_friends/test_runner/base.rb +17 -4
  33. data/lib/at_coder_friends/test_runner/judge.rb +7 -9
  34. data/lib/at_coder_friends/test_runner/sample.rb +2 -4
  35. data/lib/at_coder_friends/verifier.rb +2 -3
  36. data/lib/at_coder_friends/version.rb +1 -1
  37. data/templates/{cxx_builtin_interactive.cxx → cxx_builtin.cxx.erb} +26 -4
  38. data/templates/{ruby_builtin_interactive.rb → ruby_builtin.rb.erb} +17 -3
  39. metadata +11 -17
  40. data/tasks/regression/check_const.rake +0 -136
  41. data/tasks/regression/check_diff.rake +0 -30
  42. data/tasks/regression/check_fmt.rake +0 -42
  43. data/tasks/regression/check_parse.rake +0 -70
  44. data/tasks/regression/regression.rb +0 -72
  45. data/tasks/regression/section_list.rake +0 -53
  46. data/tasks/regression/setup.rake +0 -48
  47. data/templates/cxx_builtin_default.cxx +0 -26
  48. data/templates/ruby_builtin_default.rb +0 -7
@@ -11,20 +11,28 @@ module AtCoderFriends
11
11
  DEFAULT_FILE = File.join(ACF_HOME, 'config', 'default.yml')
12
12
 
13
13
  class << self
14
+ def dotfile
15
+ DOTFILE
16
+ end
17
+
18
+ def default_file
19
+ DEFAULT_FILE
20
+ end
21
+
14
22
  def load_config(ctx)
15
23
  path = config_file_for(ctx.path)
16
24
  config = load_yaml(path)
17
- return config if path == DEFAULT_FILE
25
+ return config if path == default_file
18
26
 
19
27
  merge_with_default(config)
20
28
  end
21
29
 
22
30
  def config_file_for(target_dir)
23
- find_project_dotfile(target_dir) || DEFAULT_FILE
31
+ find_project_dotfile(target_dir) || default_file
24
32
  end
25
33
 
26
34
  def find_project_dotfile(target_dir)
27
- find_file_upwards(DOTFILE, target_dir)
35
+ find_file_upwards(dotfile, target_dir)
28
36
  end
29
37
 
30
38
  def find_file_upwards(filename, start_dir)
@@ -7,11 +7,15 @@ module AtCoderFriends
7
7
  # - configuration
8
8
  # - application modules
9
9
  class Context
10
- attr_reader :options, :path
10
+ attr_reader :options, :path_info
11
11
 
12
12
  def initialize(options, path)
13
13
  @options = options
14
- @path = File.expand_path(path)
14
+ @path_info = PathInfo.new(File.expand_path(path))
15
+ end
16
+
17
+ def path
18
+ path_info.path
15
19
  end
16
20
 
17
21
  def config
@@ -26,6 +30,10 @@ module AtCoderFriends
26
30
  @generator ||= Generator::Main.new(self)
27
31
  end
28
32
 
33
+ def emitter
34
+ @emitter ||= Emitter.new(self)
35
+ end
36
+
29
37
  def sample_test_runner
30
38
  @sample_test_runner ||= TestRunner::Sample.new(self)
31
39
  end
@@ -38,10 +46,6 @@ module AtCoderFriends
38
46
  @verifier ||= Verifier.new(self)
39
47
  end
40
48
 
41
- def emitter
42
- @emitter ||= Emitter.new(self)
43
- end
44
-
45
49
  def post_process
46
50
  @scraping_agent&.save_session
47
51
  end
@@ -9,8 +9,8 @@ module AtCoderFriends
9
9
  include PathUtil
10
10
 
11
11
  def initialize(ctx)
12
- @src_dir = ctx.path
13
- @smp_dir = smp_dir(@src_dir)
12
+ @src_dir = ctx.path_info.src_dir
13
+ @smp_dir = ctx.path_info.smp_dir
14
14
  end
15
15
 
16
16
  def emit(pbm)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
5
+ module AtCoderFriends
6
+ module Generator
7
+ Attributes = Struct.new(:file_ext, :default_template)
8
+
9
+ # common behavior of generators
10
+ class Base
11
+ attr_reader :cfg, :pbm
12
+
13
+ def initialize(cfg = nil)
14
+ @cfg = cfg || {}
15
+ end
16
+
17
+ def process(pbm)
18
+ pbm.add_src(attrs.file_ext, generate(pbm))
19
+ end
20
+
21
+ def generate(pbm)
22
+ @pbm = pbm
23
+ src = File.read(select_template)
24
+ src = ERB.new(src, safe_level = nil, trim_mode = '-').result(binding)
25
+ src = render(src) if respond_to?(:render)
26
+ src
27
+ end
28
+
29
+ def select_template
30
+ cfg['default_template'] || attrs.default_template
31
+ end
32
+
33
+ def embed_lines(src, pat, lines)
34
+ re = Regexp.escape(pat)
35
+ src.gsub(
36
+ /^(.*)#{re}(.*)$\n/,
37
+ lines.compact.map { |s| "\\1#{s}\\2\n" }.join
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -2,96 +2,8 @@
2
2
 
3
3
  module AtCoderFriends
4
4
  module Generator
5
- module CxxBuiltinConstants
6
- ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
7
- TMPL_DIR = File.join(ACF_HOME, 'templates')
8
- DEFAULT_TMPL = File.join(TMPL_DIR, 'cxx_builtin_default.cxx')
9
- INTERACTIVE_TMPL = File.join(TMPL_DIR, 'cxx_builtin_interactive.cxx')
10
- SCANF_FMTS = [
11
- 'scanf("%<fmt>s", %<addr>s);',
12
- 'REP(i, %<sz1>s) scanf("%<fmt>s", %<addr>s);',
13
- 'REP(i, %<sz1>s) REP(j, %<sz2>s) scanf("%<fmt>s", %<addr>s);'
14
- ].freeze
15
- FMT_FMTS = { number: '%d', string: '%s', char: '%s' }.freeze
16
- ADDR_FMTS = {
17
- single: {
18
- number: '&%<v>s',
19
- string: '%<v>s'
20
- },
21
- harray: {
22
- number: '%<v>s + i',
23
- string: '%<v>s[i]',
24
- char: '%<v>s'
25
- },
26
- varray: {
27
- number: '%<v>s + i',
28
- string: '%<v>s[i]'
29
- },
30
- matrix: {
31
- number: '&%<v>s[i][j]',
32
- string: '%<v>s[i][j]',
33
- char: '%<v>s[i]'
34
- }
35
- }.freeze
36
- DEFAULT_OUTPUT = <<~TEXT
37
- int ans = 0;
38
- printf("%d\\n", ans);
39
- TEXT
40
- BINARY_OUTPUT_FMT = <<~TEXT
41
- bool cond = false;
42
- puts(cond ? "%s" : "%s");
43
- TEXT
44
- end
45
-
46
- # generates C++ source from problem description
47
- class CxxBuiltin
48
- include CxxBuiltinConstants
49
-
50
- attr_reader :cfg, :pbm
51
-
52
- def initialize(cfg = nil)
53
- @cfg = cfg || {}
54
- end
55
-
56
- def process(pbm)
57
- src = generate(pbm)
58
- pbm.add_src(:cxx, src)
59
- end
60
-
61
- def generate(pbm)
62
- @pbm = pbm
63
- src = File.read(select_template)
64
- src = embed_lines(src, '/*** URL ***/', [pbm.url])
65
- src = embed_lines(src, '/*** CONSTS ***/', gen_consts)
66
- src = embed_lines(src, '/*** DCLS ***/', gen_decls)
67
- src = embed_lines(src, '/*** INPUTS ***/', gen_inputs)
68
- embed_lines(src, '/*** OUTPUT ***/', gen_output.split("\n"))
69
- end
70
-
71
- def embed_lines(src, pat, lines)
72
- re = Regexp.escape(pat)
73
- src.gsub(
74
- /^(.*)#{re}(.*)$/,
75
- lines.map { |s| '\1' + s + '\2' }.join("\n")
76
- )
77
- end
78
-
79
- def select_template(interactive = pbm.options.interactive)
80
- interactive ? interactive_template : default_template
81
- end
82
-
83
- def default_template
84
- cfg['default_template'] || DEFAULT_TMPL
85
- end
86
-
87
- def interactive_template
88
- cfg['interactive_template'] || INTERACTIVE_TMPL
89
- end
90
-
91
- def gen_consts(constants = pbm.constants)
92
- constants.map { |c| gen_const(c) }
93
- end
94
-
5
+ # generates C++ constants
6
+ module CxxBuiltinConstGen
95
7
  def gen_const(c)
96
8
  v = cnv_const_value(c.value)
97
9
  if c.type == :max
@@ -107,98 +19,239 @@ module AtCoderFriends
107
19
  .sub(/\b2\^/, '1<<')
108
20
  .gsub(',', "'")
109
21
  end
22
+ end
110
23
 
111
- def gen_decls(inpdefs = pbm.formats)
112
- inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
113
- end
114
-
24
+ # generates C++ variable declarations
25
+ module CxxBuiltinDeclGen
26
+ TYPE_TBL = {
27
+ number: 'int',
28
+ decimal: 'double',
29
+ string: 'char',
30
+ char: 'char'
31
+ }.tap { |h| h.default = 'int' }
115
32
  def gen_decl(inpdef)
116
- case inpdef.container
117
- when :single
118
- gen_single_decl(inpdef)
119
- when :harray
120
- gen_harray_decl(inpdef)
121
- when :varray
122
- gen_varray_decl(inpdef)
123
- when :matrix
124
- gen_matrix_decl(inpdef)
33
+ if inpdef.components
34
+ inpdef.components.map { |cmp| gen_decl(cmp) }
35
+ else
36
+ case inpdef.container
37
+ when :single
38
+ gen_single_decl(inpdef)
39
+ when :harray
40
+ gen_harray_decl(inpdef)
41
+ when :varray
42
+ gen_varray_decl(inpdef)
43
+ when :matrix, :vmatrix, :hmatrix
44
+ gen_matrix_decl(inpdef)
45
+ end
125
46
  end
126
47
  end
127
48
 
128
49
  def gen_single_decl(inpdef)
129
- names = inpdef.names
130
- case inpdef.item
131
- when :number
50
+ names, cols = inpdef.vars.transpose
51
+ if cols.uniq.size == 1 && cols[0] != :string
52
+ type = TYPE_TBL[cols[0]]
132
53
  dcl = names.join(', ')
133
- "int #{dcl};"
134
- when :string
135
- names.map { |v| "char #{v}[#{v.upcase}_MAX + 1];" }
54
+ "#{type} #{dcl};"
55
+ else
56
+ inpdef.vars.map do |v, item|
57
+ type = TYPE_TBL[item]
58
+ dcl = v
59
+ dcl += "[#{v.upcase}_MAX + 1]" if item == :string
60
+ "#{type} #{dcl};"
61
+ end
136
62
  end
137
63
  end
138
64
 
139
65
  def gen_harray_decl(inpdef)
66
+ type = TYPE_TBL[inpdef.item]
140
67
  v = inpdef.names[0]
141
68
  sz = gen_arr_size(inpdef.size)[0]
142
69
  case inpdef.item
143
- when :number
144
- "int #{v}[#{sz}];"
70
+ when :number, :decimal
71
+ "#{type} #{v}[#{sz}];"
145
72
  when :string
146
- "char #{v}[#{sz}][#{v.upcase}_MAX + 1];"
73
+ "#{type} #{v}[#{sz}][#{v.upcase}_MAX + 1];"
147
74
  when :char
148
- "char #{v}[#{sz} + 1];"
75
+ "#{type} #{v}[#{sz} + 1];"
149
76
  end
150
77
  end
151
78
 
152
79
  def gen_varray_decl(inpdef)
153
- names = inpdef.names
154
80
  sz = gen_arr_size(inpdef.size)[0]
155
- case inpdef.item
156
- when :number
157
- names.map { |v| "int #{v}[#{sz}];" }
158
- when :string
159
- names.map { |v| "char #{v}[#{sz}][#{v.upcase}_MAX + 1];" }
81
+ inpdef.vars.map do |v, item|
82
+ type = TYPE_TBL[item]
83
+ dcl = "#{v}[#{sz}]"
84
+ dcl += "[#{v.upcase}_MAX + 1]" if item == :string
85
+ "#{type} #{dcl};"
160
86
  end
161
87
  end
162
88
 
163
89
  def gen_matrix_decl(inpdef)
164
- v = inpdef.names[0]
165
90
  sz1, sz2 = gen_arr_size(inpdef.size)
166
- case inpdef.item
167
- when :number
168
- "int #{v}[#{sz1}][#{sz2}];"
169
- when :string
170
- "char #{v}[#{sz1}][#{sz2}][#{v.upcase}_MAX + 1];"
171
- when :char
172
- "char #{v}[#{sz1}][#{sz2} + 1];"
91
+ inpdef.vars.map do |v, item|
92
+ type = TYPE_TBL[item]
93
+ dcl = "#{v}[#{sz1}]"
94
+ dcl += item == :char ? "[#{sz2} + 1]" : "[#{sz2}]"
95
+ dcl += "[#{v.upcase}_MAX + 1]" if item == :string
96
+ "#{type} #{dcl};"
173
97
  end
174
98
  end
175
99
 
176
100
  def gen_arr_size(szs)
177
101
  szs.map { |sz| sz.gsub(/([a-z][a-z0-9_]*)/i, '\1_MAX').upcase }
178
102
  end
103
+ end
179
104
 
180
- def gen_inputs(inpdefs = pbm.formats)
181
- inpdefs.map { |inpdef| gen_input(inpdef) }.flatten
182
- end
105
+ # generates C++ input source
106
+ module CxxBuiltinInputGen
107
+ SCANF_FMTS = [
108
+ 'scanf("%<fmt>s", %<addr>s);',
109
+ 'REP(i, %<sz1>s) scanf("%<fmt>s", %<addr>s);',
110
+ 'REP(i, %<sz1>s) REP(j, %<sz2>s) scanf("%<fmt>s", %<addr>s);'
111
+ ].freeze
112
+ SCANF_FMTS_CMB = {
113
+ varray_matrix:
114
+ [
115
+ <<~TEXT,
116
+ REP(i, %<sz1>s) {
117
+ scanf("%<fmt1>s", %<addr1>s);
118
+ scanf("%<fmt2>s", %<addr2>s);
119
+ }
120
+ TEXT
121
+ <<~TEXT
122
+ REP(i, %<sz1>s) {
123
+ scanf("%<fmt1>s", %<addr1>s);
124
+ REP(j, %<sz2>s[i]) scanf("%<fmt2>s", %<addr2>s);
125
+ }
126
+ TEXT
127
+ ],
128
+ matrix_varray:
129
+ [
130
+ <<~TEXT,
131
+ REP(i, %<sz1>s) {
132
+ scanf("%<fmt1>s", %<addr1>s);
133
+ scanf("%<fmt2>s", %<addr2>s);
134
+ }
135
+ TEXT
136
+ <<~TEXT
137
+ REP(i, %<sz1>s) {
138
+ REP(j, %<sz2>s) scanf("%<fmt1>s", %<addr1>s);
139
+ scanf("%<fmt2>s", %<addr2>s);
140
+ }
141
+ TEXT
142
+ ]
143
+ }.tap { |h| h.default = h[:varray_matrix] }
144
+ FMT_FMTS = {
145
+ number: '%d',
146
+ decimal: '%lf',
147
+ string: '%s',
148
+ char: '%s'
149
+ }.tap { |h| h.default = h[:number] }
150
+ SINGLE_ADDR_FMTS = {
151
+ number: '&%<v>s',
152
+ decimal: '&%<v>s',
153
+ string: '%<v>s'
154
+ }.tap { |h| h.default = h[:number] }
155
+ ARRAY_ADDR_FMTS = {
156
+ number: '%<v>s + i',
157
+ decimal: '%<v>s + i',
158
+ string: '%<v>s[i]',
159
+ char: '%<v>s'
160
+ }.tap { |h| h.default = h[:number] }
161
+ MATRIX_ADDR_FMTS = {
162
+ number: '&%<v>s[i][j]',
163
+ decimal: '&%<v>s[i][j]',
164
+ string: '%<v>s[i][j]',
165
+ char: '%<v>s[i]'
166
+ }.tap { |h| h.default = h[:number] }
167
+ ADDR_FMTS = {
168
+ single: SINGLE_ADDR_FMTS,
169
+ harray: ARRAY_ADDR_FMTS,
170
+ varray: ARRAY_ADDR_FMTS,
171
+ matrix: MATRIX_ADDR_FMTS,
172
+ vmatrix: MATRIX_ADDR_FMTS,
173
+ hmatrix: MATRIX_ADDR_FMTS
174
+ }.tap { |h| h.default = h[:single] }
183
175
 
184
- # rubocop:disable Metrics/AbcSize
185
176
  def gen_input(inpdef)
186
- dim = inpdef.size.size - (inpdef.item == :char ? 1 : 0)
187
- scanf = SCANF_FMTS[dim]
177
+ if inpdef.components
178
+ gen_cmb_input(inpdef)
179
+ else
180
+ gen_plain_input(inpdef)
181
+ end
182
+ end
183
+
184
+ def gen_plain_input(inpdef)
185
+ scanf = SCANF_FMTS[inpdef.size.size - (inpdef.item == :char ? 1 : 0)]
188
186
  sz1, sz2 = inpdef.size
189
- fmt = FMT_FMTS[inpdef.item] * inpdef.names.size
190
- addr_fmt = ADDR_FMTS[inpdef.container][inpdef.item]
191
- addr = inpdef.names.map { |v| format(addr_fmt, v: v) }.join(', ')
187
+ fmt, addr = scanf_params(inpdef)
192
188
  format(scanf, sz1: sz1, sz2: sz2, fmt: fmt, addr: addr)
193
189
  end
194
- # rubocop:enable Metrics/AbcSize
195
190
 
196
- def gen_output(vs = pbm.options.binary_values)
197
- if vs
198
- format(BINARY_OUTPUT_FMT, *vs)
199
- else
200
- DEFAULT_OUTPUT
201
- end
191
+ def gen_cmb_input(inpdef)
192
+ scanf = SCANF_FMTS_CMB.dig(
193
+ inpdef.container, inpdef.item == :char ? 0 : 1
194
+ )
195
+ sz1 = inpdef.size[0]
196
+ sz2 = inpdef.size[1].split('_')[0]
197
+ fmt1, addr1, fmt2, addr2 =
198
+ inpdef.components.map { |cmp| scanf_params(cmp) }.flatten
199
+ format(
200
+ scanf,
201
+ sz1: sz1, sz2: sz2,
202
+ fmt1: fmt1, addr1: addr1,
203
+ fmt2: fmt2, addr2: addr2
204
+ ).split("\n")
205
+ end
206
+
207
+ def scanf_params(inpdef)
208
+ [scanf_fmt(inpdef), scanf_addr(inpdef)]
209
+ end
210
+
211
+ def scanf_fmt(inpdef)
212
+ inpdef.vars.map { |(_v, item)| FMT_FMTS[item] }.join
213
+ end
214
+
215
+ def scanf_addr(inpdef)
216
+ inpdef.vars.map do |(v, item)|
217
+ addr_fmt = ADDR_FMTS.dig(inpdef.container, item)
218
+ format(addr_fmt, v: v)
219
+ end.join(', ')
220
+ end
221
+ end
222
+
223
+ # generates C++ source from problem description
224
+ class CxxBuiltin < Base
225
+ include CxxBuiltinConstGen
226
+ include CxxBuiltinDeclGen
227
+ include CxxBuiltinInputGen
228
+
229
+ ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
230
+ TMPL_DIR = File.join(ACF_HOME, 'templates')
231
+ DEFAULT_TMPL = File.join(TMPL_DIR, 'cxx_builtin.cxx.erb')
232
+ ATTRS = Attributes.new(:cxx, DEFAULT_TMPL)
233
+
234
+ def attrs
235
+ ATTRS
236
+ end
237
+
238
+ def render(src)
239
+ src = embed_lines(src, '/*** CONSTS ***/', gen_consts)
240
+ src = embed_lines(src, '/*** DCLS ***/', gen_decls)
241
+ src = embed_lines(src, '/*** INPUTS ***/', gen_inputs)
242
+ src
243
+ end
244
+
245
+ def gen_consts(constants = pbm.constants)
246
+ constants.map { |c| gen_const(c) }
247
+ end
248
+
249
+ def gen_decls(inpdefs = pbm.formats)
250
+ inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
251
+ end
252
+
253
+ def gen_inputs(inpdefs = pbm.formats)
254
+ inpdefs.map { |inpdef| gen_input(inpdef) }.flatten
202
255
  end
203
256
  end
204
257
  end