at_coder_friends 0.6.9 → 0.7.0

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 (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
  }