at_coder_friends 0.6.9 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -1
  3. data/.github/workflows/ruby.yml +7 -1
  4. data/.gitignore +3 -0
  5. data/.rubocop.yml +1 -1
  6. data/CHANGELOG.md +17 -4
  7. data/Gemfile.lock +37 -40
  8. data/README.md +10 -91
  9. data/at_coder_friends.gemspec +2 -2
  10. data/lib/at_coder_friends/cli.rb +13 -28
  11. data/lib/at_coder_friends/config_loader.rb +1 -2
  12. data/lib/at_coder_friends/generator/any_builtin.rb +22 -0
  13. data/lib/at_coder_friends/generator/base.rb +55 -6
  14. data/lib/at_coder_friends/generator/c_builtin.rb +22 -0
  15. data/lib/at_coder_friends/generator/cxx_builtin.rb +8 -239
  16. data/lib/at_coder_friends/generator/fragment.rb +106 -0
  17. data/lib/at_coder_friends/generator/main.rb +14 -6
  18. data/lib/at_coder_friends/generator/ruby_builtin.rb +13 -165
  19. data/lib/at_coder_friends/parser/binary.rb +9 -3
  20. data/lib/at_coder_friends/parser/constraints.rb +8 -8
  21. data/lib/at_coder_friends/parser/input_format.rb +5 -5
  22. data/lib/at_coder_friends/path_util.rb +1 -1
  23. data/lib/at_coder_friends/problem.rb +4 -4
  24. data/lib/at_coder_friends/scraping/session.rb +1 -1
  25. data/lib/at_coder_friends/scraping/tasks.rb +1 -1
  26. data/lib/at_coder_friends/version.rb +1 -1
  27. data/lib/at_coder_friends.rb +3 -0
  28. data/templates/any_builtin.md.erb +30 -0
  29. data/templates/any_builtin_fragments.yml +5 -0
  30. data/templates/c_builtin.c.erb +36 -0
  31. data/templates/c_builtin_fragments.yml +127 -0
  32. data/templates/csharp_sample.cs.erb +29 -0
  33. data/templates/csharp_sample_fragments.yml +159 -0
  34. data/templates/java_sample.java.erb +25 -0
  35. data/templates/java_sample_fragments.yml +98 -0
  36. data/templates/ruby_builtin_fragments.yml +93 -0
  37. metadata +22 -6
  38. data/.travis.yml +0 -16
  39. data/docs/CONFIGURATION.md +0 -421
@@ -2,256 +2,25 @@
2
2
 
3
3
  module AtCoderFriends
4
4
  module Generator
5
- # generates C++ constants
6
- module CxxBuiltinConstGen
7
- def gen_const(c)
8
- v = cnv_const_value(c.value)
9
- if c.type == :max
10
- "const int #{c.name.upcase}_MAX = #{v};"
11
- else
12
- "const int MOD = #{v};"
13
- end
14
- end
15
-
16
- def cnv_const_value(v)
17
- v
18
- .sub(/\b10\^/, '1e')
19
- .sub(/\b2\^/, '1<<')
20
- .gsub(',', "'")
21
- end
22
- end
23
-
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' }
32
- def gen_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
46
- end
47
- end
48
-
49
- def gen_single_decl(inpdef)
50
- names, cols = inpdef.vars.transpose
51
- if cols.uniq.size == 1 && cols[0] != :string
52
- type = TYPE_TBL[cols[0]]
53
- dcl = names.join(', ')
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
62
- end
63
- end
64
-
65
- def gen_harray_decl(inpdef)
66
- type = TYPE_TBL[inpdef.item]
67
- v = inpdef.names[0]
68
- sz = gen_arr_size(inpdef.size)[0]
69
- case inpdef.item
70
- when :number, :decimal
71
- "#{type} #{v}[#{sz}];"
72
- when :string
73
- "#{type} #{v}[#{sz}][#{v.upcase}_MAX + 1];"
74
- when :char
75
- "#{type} #{v}[#{sz} + 1];"
76
- end
77
- end
78
-
79
- def gen_varray_decl(inpdef)
80
- sz = gen_arr_size(inpdef.size)[0]
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};"
86
- end
87
- end
88
-
89
- def gen_matrix_decl(inpdef)
90
- sz1, sz2 = gen_arr_size(inpdef.size)
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};"
97
- end
98
- end
99
-
100
- def gen_arr_size(szs)
101
- szs.map { |sz| sz.gsub(/([a-z][a-z0-9_]*)/i, '\1_MAX').upcase }
102
- end
103
- end
104
-
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] }
175
-
176
- def gen_input(inpdef)
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)]
186
- sz1, sz2 = inpdef.size
187
- fmt, addr = scanf_params(inpdef)
188
- format(scanf, sz1: sz1, sz2: sz2, fmt: fmt, addr: addr)
189
- end
190
-
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
5
  # 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)
6
+ class CxxBuiltin < CBuiltin
7
+ TEMPLATE = File.join(TMPL_DIR, 'cxx_builtin.cxx.erb')
8
+ ATTRS = Attributes.new(:cxx, TEMPLATE, FRAGMENTS)
233
9
 
234
10
  def attrs
235
11
  ATTRS
236
12
  end
237
13
 
14
+ def gen_const(c)
15
+ ConstFragment.new(c, fragments['cxx_constant']).generate
16
+ end
17
+
18
+ # deprecated, use ERB syntax
238
19
  def render(src)
239
20
  src = embed_lines(src, '/*** CONSTS ***/', gen_consts)
240
21
  src = embed_lines(src, '/*** DCLS ***/', gen_decls)
241
22
  embed_lines(src, '/*** INPUTS ***/', gen_inputs)
242
23
  end
243
-
244
- def gen_consts(constants = pbm.constants)
245
- constants.map { |c| gen_const(c) }
246
- end
247
-
248
- def gen_decls(inpdefs = pbm.formats)
249
- inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
250
- end
251
-
252
- def gen_inputs(inpdefs = pbm.formats)
253
- inpdefs.map { |inpdef| gen_input(inpdef) }.flatten
254
- end
255
24
  end
256
25
  end
257
26
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
5
+ module AtCoderFriends
6
+ module Generator
7
+ # base class for code fragment generator
8
+ class FragmentBase
9
+ attr_reader :templates
10
+
11
+ def initialize(obj, templates)
12
+ @obj = obj
13
+ @templates = templates
14
+ end
15
+
16
+ def render(*keys)
17
+ keys = keys.map(&:to_s)
18
+ template = templates.dig(*keys) || (raise AppError, "fragment key #{keys} not found")
19
+
20
+ return ERB.new(template, trim_mode: '-').result(binding) if template.is_a?(String)
21
+
22
+ if template.is_a?(Hash)
23
+ sub_key_props = template['__key'] || (raise AppError, "'__key' not found in fragment hash #{keys}")
24
+ sub_keys = sub_key_props.map { |k| send(k) }
25
+ return render(*keys, *sub_keys)
26
+ end
27
+
28
+ raise AppError, "can't render fragment #{keys}"
29
+ end
30
+
31
+ # delegate method calls to obj
32
+ def method_missing(name, *args, &block)
33
+ if @obj.respond_to?(name)
34
+ @obj.send(name, *args, &block)
35
+ elsif templates.key?(name.to_s)
36
+ render(name)
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def respond_to_missing?(name, include_private = false)
43
+ @obj.respond_to?(name, include_private) ||
44
+ templates.key?(name.to_s) ||
45
+ super
46
+ end
47
+
48
+ def to_s
49
+ @obj.to_s
50
+ end
51
+ end
52
+
53
+ # base class for constant declaration generator
54
+ class ConstFragment < FragmentBase
55
+ def generate
56
+ render(type)
57
+ end
58
+ end
59
+
60
+ # base class for variable declaration generator
61
+ class InputFormatFragment < FragmentBase
62
+ def generate
63
+ render(:main)
64
+ end
65
+
66
+ def vs
67
+ names
68
+ end
69
+
70
+ def v
71
+ vs[0]
72
+ end
73
+
74
+ def sz1
75
+ size[0]
76
+ end
77
+ alias sz sz1
78
+
79
+ def sz2
80
+ size[1]
81
+ end
82
+
83
+ def delims
84
+ delim.chars
85
+ end
86
+
87
+ def vars
88
+ @vars ||= super.map do |v, item|
89
+ var = Problem::InputFormat.new(
90
+ container: container,
91
+ names: [v],
92
+ item: item,
93
+ size: size
94
+ )
95
+ self.class.new(var, templates)
96
+ end
97
+ end
98
+
99
+ def components
100
+ @components ||= super&.map do |cmp|
101
+ self.class.new(cmp, templates)
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -18,26 +18,34 @@ module AtCoderFriends
18
18
  gen_obj.process(pbm)
19
19
  rescue StandardError => e
20
20
  puts "an error occurred in generator:#{gen_name}."
21
- puts e.to_s
21
+ puts e
22
22
  puts e.backtrace
23
23
  end
24
24
  end
25
25
 
26
26
  def load_obj(gen_name)
27
27
  @cache[gen_name] ||= begin
28
- gen_class = load_class(gen_name)
28
+ cls_name = gen_name.split('_')[0]
29
+ gen_class = load_class(cls_name)
29
30
  gen_cnf = config_for(gen_name)
30
31
  gen_class.new(gen_cnf)
31
32
  end
32
33
  end
33
34
 
34
35
  def load_class(gen_name)
35
- unless AtCoderFriends::Generator.const_defined?(gen_name)
36
- require "at_coder_friends/generator/#{to_snake(gen_name)}"
37
- end
36
+ snake_gen_name = to_snake(gen_name)
37
+ require "at_coder_friends/generator/#{snake_gen_name}" unless AtCoderFriends::Generator.const_defined?(gen_name)
38
38
  AtCoderFriends::Generator.const_get(gen_name)
39
39
  rescue LoadError
40
- raise AppError, "plugin load error : generator #{gen_name} not found."
40
+ raise AppError, <<~MSG
41
+ Error: Failed to load plugin.
42
+ The '#{gen_name}' plugin could not be found. To use this plugin, please install the required gem by following these steps:
43
+
44
+ 1. Open a terminal or command prompt.
45
+ 2. Run the following command:
46
+ gem install at_coder_friends-generator-#{snake_gen_name}
47
+ 3. Once the above command completes, please run the program again.
48
+ MSG
41
49
  end
42
50
 
43
51
  def config_for(gen_name)
@@ -4,181 +4,29 @@ module AtCoderFriends
4
4
  module Generator
5
5
  # generates Ruby source from problem description
6
6
  class RubyBuiltin < Base
7
+ include ConstFragmentMixin
8
+ include DeclFragmentMixin
9
+
7
10
  ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
8
11
  TMPL_DIR = File.join(ACF_HOME, 'templates')
9
- DEFAULT_TMPL = File.join(TMPL_DIR, 'ruby_builtin.rb.erb')
10
- ATTRS = Attributes.new(:rb, DEFAULT_TMPL)
12
+ TEMPLATE = File.join(TMPL_DIR, 'ruby_builtin.rb.erb')
13
+ FRAGMENTS = File.realpath(File.join(TMPL_DIR, 'ruby_builtin_fragments.yml'))
14
+ ATTRS = Attributes.new(:rb, TEMPLATE, FRAGMENTS)
11
15
 
12
16
  def attrs
13
17
  ATTRS
14
18
  end
15
19
 
16
- def render(src)
17
- src = embed_lines(src, '### CONSTS ###', gen_consts)
18
- embed_lines(src, '### DCLS ###', gen_decls)
19
- end
20
-
21
- def gen_consts(constants = pbm.constants)
22
- constants
20
+ def gen_consts
21
+ pbm.constants
23
22
  .select { |c| c.type == :mod }
24
- .map { |c| gen_mod(c) }
25
- end
26
-
27
- def gen_mod(c)
28
- v = c.value.gsub('^', '**').gsub(',', '_')
29
- "MOD = #{v}"
30
- end
31
-
32
- def gen_decls(inpdefs = pbm.formats)
33
- inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
23
+ .map { |c| gen_const(c) }
34
24
  end
35
25
 
36
- def gen_decl(inpdef)
37
- case inpdef.container
38
- when :single
39
- gen_single_decl(inpdef)
40
- when :harray
41
- gen_harray_decl(inpdef)
42
- when :varray
43
- gen_varray_decl(inpdef)
44
- when :matrix
45
- gen_matrix_decl(inpdef)
46
- when :varray_matrix, :matrix_varray
47
- gen_cmb_decl(inpdef)
48
- when :vmatrix
49
- gen_vmatrix_decl(inpdef)
50
- when :hmatrix
51
- gen_hmatrix_decl(inpdef)
52
- end
53
- end
54
-
55
- def gen_single_decl(inpdef)
56
- names = inpdef.names
57
- dcl = names.join(', ')
58
- expr = gen_expr(inpdef, names.size > 1)
59
- "#{dcl} = #{expr}"
60
- end
61
-
62
- def gen_harray_decl(inpdef)
63
- v = inpdef.names[0]
64
- dcl = "#{v}s"
65
- expr = gen_expr(inpdef, true)
66
- "#{dcl} = #{expr}"
67
- end
68
-
69
- def gen_varray_decl(inpdef)
70
- if inpdef.names.size == 1
71
- gen_varray_1_decl(inpdef)
72
- else
73
- gen_varray_n_decl(inpdef)
74
- end
75
- end
76
-
77
- def gen_varray_1_decl(inpdef)
78
- v = inpdef.names[0]
79
- sz = inpdef.size[0]
80
- dcl = "#{v}s"
81
- expr = gen_expr(inpdef, false)
82
- "#{dcl} = Array.new(#{sz}) { #{expr} }"
83
- end
84
-
85
- def gen_varray_n_decl(inpdef)
86
- names = inpdef.names
87
- sz = inpdef.size[0]
88
- dcl = names.map { |v| "#{v}s[i]" }.join(', ')
89
- expr = gen_expr(inpdef, true)
90
- ret = []
91
- ret += names.map { |v| "#{v}s = Array.new(#{sz})" }
92
- ret << "#{sz}.times do |i|"
93
- ret << " #{dcl} = #{expr}"
94
- ret << 'end'
95
- ret
96
- end
97
-
98
- def gen_matrix_decl(inpdef)
99
- v = inpdef.names[0]
100
- sz = inpdef.size[0]
101
- decl = "#{v}ss"
102
- expr = gen_expr(inpdef, true)
103
- "#{decl} = Array.new(#{sz}) { #{expr} }"
104
- end
105
-
106
- def gen_cmb_decl(inpdef)
107
- mx = inpdef.container == :varray_matrix ? -1 : 0
108
- vs = inpdef.names.map { |v| "#{v}s" }
109
- vs[mx] += 's'
110
- sz = inpdef.size[0]
111
- dcls = vs.map { |v| "#{v}[i]" }
112
- dcls[mx] = "*#{dcls[mx]}" unless inpdef.item == :char
113
- dcl = dcls.join(', ')
114
- expr = gen_cmb_expr(inpdef)
115
- ret = []
116
- ret += vs.map { |v| "#{v} = Array.new(#{sz})" }
117
- ret << "#{sz}.times do |i|"
118
- ret << " #{dcl} = #{expr}"
119
- ret << 'end'
120
- ret
121
- end
122
-
123
- def gen_vmatrix_decl(inpdef)
124
- names = inpdef.names
125
- sz1, sz2 = inpdef.size
126
- dcl = names.map { |v| "#{v}ss[i][j]" }.join(', ')
127
- expr = gen_expr(inpdef, true)
128
- ret = []
129
- ret += names.map do |v|
130
- "#{v}ss = Array.new(#{sz1}) { Array.new(#{sz2}) }"
131
- end
132
- ret << "#{sz1}.times do |i|"
133
- ret << " #{sz2}.times do |j|"
134
- ret << " #{dcl} = #{expr}"
135
- ret << ' end'
136
- ret << 'end'
137
- ret
138
- end
139
-
140
- def gen_hmatrix_decl(inpdef)
141
- names = inpdef.names
142
- sz = inpdef.size[0]
143
- dcl = names.map { |v| "#{v}ss[i]" }.join(', ')
144
- expr = gen_expr(inpdef, true)
145
- ret = []
146
- ret += names.map { |v| "#{v}ss = Array.new(#{sz})" }
147
- ret << "#{sz}.times do |i|"
148
- ret << " #{dcl} = #{expr}.each_slice(#{names.size}).to_a.transpose"
149
- ret << 'end'
150
- ret
151
- end
152
-
153
- def gen_expr(inpdef, split)
154
- read = gen_read(inpdef.delim)
155
- case inpdef.item
156
- when :number
157
- split ? "#{read}.split.map(&:to_i)" : "#{read}.to_i"
158
- when :decimal
159
- split ? "#{read}.split.map(&:to_f)" : "#{read}.to_f"
160
- when :string
161
- split ? "#{read}.chomp.split" : "#{read}.chomp"
162
- when :char
163
- 'gets.chomp'
164
- end
165
- end
166
-
167
- def gen_cmb_expr(inpdef)
168
- read = gen_read(inpdef.delim)
169
- case inpdef.item
170
- when :number
171
- "#{read}.split.map(&:to_i)"
172
- when :decimal
173
- "#{read}.split.map(&:to_f)"
174
- when :string, :char
175
- "#{read}.chomp.split"
176
- end
177
- end
178
-
179
- def gen_read(delim)
180
- sub = delim.chars.map { |d| ".gsub('#{d}', ' ')" }.join
181
- "gets#{sub}"
26
+ # deprecated, use ERB syntax
27
+ def render(src)
28
+ src = embed_lines(src, '### CONSTS ###', gen_consts)
29
+ embed_lines(src, '### DCLS ###', gen_decls)
182
30
  end
183
31
  end
184
32
  end
@@ -8,9 +8,7 @@ module AtCoderFriends
8
8
 
9
9
  def process(pbm)
10
10
  vs = exp_values(pbm)
11
- return unless vs.size == 2
12
- return if vs.any? { |v| v.include?("\n") }
13
- return if vs.any? { |v| v =~ /\A[0-9\s]*\z/ }
11
+ return unless binary_values?(vs)
14
12
 
15
13
  out_fmt = output_format(pbm)
16
14
  re1, re2 = vs.map { |v| Regexp.escape(v) }
@@ -32,6 +30,14 @@ module AtCoderFriends
32
30
  .uniq
33
31
  end
34
32
 
33
+ def binary_values?(vs)
34
+ return false unless vs.size == 2
35
+ return false if vs.any? { |v| v.include?("\n") }
36
+ return false if vs.any? { |v| v =~ /\A[0-9\s]*\z/ }
37
+
38
+ true
39
+ end
40
+
35
41
  def output_format(pbm)
36
42
  pbm.sections[Problem::SECTION_OUT_FMT]&.content || ''
37
43
  end
@@ -34,14 +34,14 @@ module AtCoderFriends
34
34
 
35
35
  def parse(str)
36
36
  str = normalize_content(str)
37
- str
38
- .scan(MAX_PATTERN)
39
- .map(&:compact)
40
- .map { |k, v| [normalize_names(k), normalize_value(v)] }
41
- .select { |_, v| v && !v.empty? }
42
- .flat_map { |ks, v| ks.map { |k| [k, v] } }
43
- .uniq
44
- .map { |k, v| Problem::Constant.new(k, :max, v) }
37
+ pats = str.scan(MAX_PATTERN).map(&:compact)
38
+ pairs = pats.each_with_object([]) do |(k, v), result|
39
+ value = normalize_value(v)
40
+ next unless value && !value.empty?
41
+
42
+ normalize_names(k).each { |name| result << [name, value] }
43
+ end
44
+ pairs.uniq.map { |k, v| Problem::Constant.new(k, :max, v) }
45
45
  end
46
46
 
47
47
  def normalize_content(s)
@@ -144,7 +144,7 @@ module AtCoderFriends
144
144
  sz = str.split('_')
145
145
  return sz if sz.size == 2
146
146
 
147
- [str[0] || '_', str[1..-1] || '_']
147
+ [str[0] || '_', str[1..] || '_']
148
148
  end
149
149
  end
150
150
 
@@ -343,7 +343,7 @@ module AtCoderFriends
343
343
  gen_pat2:
344
344
  lambda { |vs|
345
345
  m = vs[0]
346
- ws = vs[1..-1].map { |v| v + RE_IX.source }.join('\s+')
346
+ ws = vs[1..].map { |v| v + RE_IX.source }.join('\s+')
347
347
  /
348
348
  \A
349
349
  #{m}#{RE_IX}
@@ -367,7 +367,7 @@ module AtCoderFriends
367
367
  lambda { |vs|
368
368
  ws = [
369
369
  vs[0] + RE_SZ.source,
370
- *vs[1..-1]&.map { |v| v + RE_IX.source }
370
+ *vs[1..]&.map { |v| v + RE_IX.source }
371
371
  ].join('\s+')
372
372
  /\A#{ws}\z/
373
373
  }
@@ -389,7 +389,7 @@ module AtCoderFriends
389
389
  ws1 = vs.map { |v| v + RE_IX.source }.join('\s+')
390
390
  ws2 = [
391
391
  vs[0] + RE_SZ.source,
392
- *vs[1..-1]&.map { |v| v + RE_IX.source }
392
+ *vs[1..]&.map { |v| v + RE_IX.source }
393
393
  ].join('\s+')
394
394
  /
395
395
  \A
@@ -412,7 +412,7 @@ module AtCoderFriends
412
412
  lambda { |vs|
413
413
  ws = [
414
414
  vs[0] + RE_SZ.source,
415
- *vs[1..-1]&.map { |v| v + RE_IX.source }
415
+ *vs[1..]&.map { |v| v + RE_IX.source }
416
416
  ].join('\s+')
417
417
  /\A#{ws}\z/
418
418
  }