at_coder_friends 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,195 +2,461 @@
2
2
 
3
3
  module AtCoderFriends
4
4
  module Parser
5
- InputFormatMatcher = Struct.new(:container, :item, :pat, :gen_names, :gen_pat2) do
6
- attr_reader :names, :size
5
+ module InputFormatConstants
6
+ SECTIONS = [Problem::SECTION_IN_FMT, Problem::SECTION_IO_FMT].freeze
7
+ DELIMS = %w[- / :].freeze
8
+ RE_SINGLE = /[A-Za-z{][A-Za-z_0-9{}]*/.freeze
9
+ RE_ITEM = /\{*[A-Za-z]+(?:_[A-Za-z]+)*\}*/.freeze
10
+ RE_0 = /[0-9]+/.freeze
11
+ RE_00 = /[01][,_]?[01]/.freeze
12
+ RE_99 = /[0-9]+[,_]?[0-9]+/.freeze
13
+ ADD_TAG = ->(tag, re) { /(?<#{tag}>#{re})/ }
14
+ TO_SUFFIX = ->(re) { /(_\{*#{re}\}*|\{+#{re}\}+)/ }
15
+ TO_SUFFIX_STR = ->(str) { "(_\\{*#{str}\\}*|\\{+#{str}\\}+)" }
16
+ RE_IX = TO_SUFFIX[/\S+?/]
17
+ RE_IX_0 = TO_SUFFIX[ADD_TAG['ix0', RE_0]]
18
+ RE_IX_00 = TO_SUFFIX[ADD_TAG['ix0', RE_00]]
19
+ RE_IX_99 = TO_SUFFIX[RE_99]
20
+ RE_SZ = TO_SUFFIX[ADD_TAG['sz', /\S+?/]]
21
+ RE_SZ_0 = TO_SUFFIX[ADD_TAG['sz', RE_0]]
22
+ RE_SZ_00 = TO_SUFFIX[ADD_TAG['sz', RE_00]]
23
+ RE_SZ_99 = TO_SUFFIX[ADD_TAG['sz', RE_99]]
24
+ RE_SZ_REF = TO_SUFFIX_STR['\k<sz>']
25
+ RE_SZ2_0 = TO_SUFFIX[ADD_TAG['sz2', RE_0]]
26
+ RE_SZ2_REF = TO_SUFFIX_STR['\k<sz2>']
27
+ RE_BLOCK = /(?<bl>\{(?:[^{}]|\g<bl>)*\})/.freeze
28
+ DIMENSION_TBL = {
29
+ single: 0,
30
+ varray: 1,
31
+ harray: 1,
32
+ matrix: 2,
33
+ varray_matrix: 2,
34
+ matrix_varray: 2,
35
+ vmatrix: 2,
36
+ hmatrix: 2
37
+ }.freeze
38
+ end
39
+
40
+ # utilities for input format parser
41
+ module InputFormatUtils
42
+ include InputFormatConstants
43
+
44
+ # 1) &npsp;, fill-width space -> half width space
45
+ # 2) {i, j}->{i,j} for nested {}
46
+ def normalize_fmt(str)
47
+ str
48
+ .tr('0-9A-Za-z', '0-9A-Za-z')
49
+ .gsub(/[[:space:]]/) { |c| c.gsub(/[^\n]/, ' ') } # 1)
50
+ .gsub(%r{<var>([^<>]+)</var>}i, '\1') # <sub><var>N</var></sub>
51
+ .gsub(%r{<sup>([^<>]+)</sup>}i, '^\1')
52
+ .gsub(%r{<sub>([^<>]+)</sub>}i, '_{\1}')
53
+ .gsub(%r{<sub>([^<>]+)</sub>}i, '_{\1}') # for nested<sub>
54
+ .gsub(/<("[^"]*"|'[^']*'|[^'"<>])*>/, '')
55
+ .gsub('&amp;', '&')
56
+ .gsub('&gt;', '>')
57
+ .gsub('&lt;', '<')
58
+ .gsub('\\ ', ' ')
59
+ .gsub('\\(', '')
60
+ .gsub('\\)', '')
61
+ .gsub('\\lvert', '|')
62
+ .gsub('\\rvert', '|')
63
+ .gsub('\\mathit', '')
64
+ .gsub('\\times', '*')
65
+ .gsub(/\\begin(\{[^{}]*\})*/, '')
66
+ .gsub(/\\end(\{[^{}]*\})*/, '')
67
+ .gsub(/\\[cdlv]?dots/, '..')
68
+ .gsub(/\{\}/, ' ')
69
+ .gsub('−', '-') # full width hyphen
70
+ .gsub(/[・.:‥⋮︙…]+/, '..')
71
+ .gsub(/[\\$']/, '') # s' -> s
72
+ .gsub(/[&~|]/, ' ') # |S| -> S
73
+ .gsub(/^\s*[.:][\s.:]*$/, '..')
74
+ .tr('()', '{}')
75
+ .gsub(/#{RE_BLOCK}/) { |w| w.delete(' ') } # 2)
76
+ .split("\n")
77
+ .map(&:strip)
78
+ end
79
+
80
+ def extract_delim(str)
81
+ # a-b, a/b, a:b -> a b
82
+ str = str.dup
83
+ dlms =
84
+ DELIMS.select { |c| str.gsub!(/#{c}(#{RE_SINGLE})/, ' \1') }.join
85
+ [str, dlms]
86
+ end
87
+
88
+ def normalize_name(s)
89
+ s.delete('{},').gsub(/(\A_+|_+\z)/, '')
90
+ end
91
+
92
+ def normalize_names(names)
93
+ names.map { |nm| normalize_name(nm) }
94
+ end
95
+
96
+ def normalize_size(container, size, ix0)
97
+ sz = size_array(container, size)
98
+ sz0 = size_array(container, ix0)
99
+
100
+ sz.map.with_index do |s, i|
101
+ if sz0[i] == '0'
102
+ # 0 -> 1, N-1 -> N, N-2 -> N-1 if 0 origin
103
+ s.gsub(/\A0\z/, '1').gsub(/-1\z/, '').gsub(/-2\z/, '-1')
104
+ else
105
+ s
106
+ end
107
+ end
108
+ end
109
+
110
+ # split size by container dimension
111
+ def size_array(container, size)
112
+ (
113
+ case DIMENSION_TBL[container]
114
+ when 2
115
+ split_size(size)
116
+ when 1
117
+ [size]
118
+ when 0
119
+ []
120
+ end
121
+ ).map { |s| normalize_name(s) }
122
+ end
123
+
124
+ def split_size(str)
125
+ str = str.gsub(/(\A\{|\}\z)/, '') while str =~ /\A#{RE_BLOCK}\z/
126
+
127
+ sz = str.split(',')
128
+ return sz if sz.size == 2
129
+
130
+ sz = str.scan(/(?<nbl>[^{}]+)|#{RE_BLOCK}/).flatten.compact
131
+ return sz if sz.size == 2
132
+
133
+ str = str.delete('{},')
134
+
135
+ sz = str.scan(/[^_](?:_[^_])?/)
136
+ return sz if sz.size == 2
137
+
138
+ sz = str.split('_')
139
+ return sz if sz.size == 2
140
+
141
+ [str[0] || '_', str[1..-1] || '_']
142
+ end
143
+ end
144
+
145
+ # holds regular expressions and matches it with input format string
146
+ class InputFormatMatcher
147
+ include InputFormatUtils
148
+
149
+ attr_reader :container, :item, :pat, :gen_names, :gen_pat2
150
+ attr_reader :names, :pat2, :size, :delim, :ix0
151
+
152
+ def initialize(
153
+ container: nil, item: nil,
154
+ pat: nil, gen_names: nil, gen_pat2: nil
155
+ )
156
+ @container = container
157
+ @item = item
158
+ @pat = pat
159
+ @gen_names = gen_names
160
+ @gen_pat2 = gen_pat2
161
+ end
7
162
 
8
163
  def match(str)
164
+ str, dlm = extract_delim(str)
9
165
  return false unless (m1 = pat.match(str))
10
166
 
11
167
  @names = gen_names.call(m1)
12
168
  @pat2 = gen_pat2&.call(names)
13
169
  @size = m1.names.include?('sz') && m1['sz'] || ''
170
+ @ix0 = m1.names.include?('ix0') && m1['ix0'] || size
171
+ @delim = dlm
14
172
  true
15
173
  end
16
174
 
17
175
  def match2(str)
18
- return false unless @pat2
176
+ return false unless pat2
177
+
178
+ str, _dlm = extract_delim(str)
19
179
  return true if /\A\.+\z/ =~ str
20
- return false unless (m2 = @pat2.match(str))
180
+ return false unless (m2 = pat2.match(str))
21
181
 
22
182
  m2.names.include?('sz') && @size = m2['sz']
23
183
  true
24
184
  end
185
+
186
+ def to_inpdef
187
+ Problem::InputFormat.new(
188
+ container: container, item: item,
189
+ names: normalize_names(names),
190
+ size: normalize_size(container, size, ix0),
191
+ delim: delim
192
+ )
193
+ end
25
194
  end
26
195
 
27
- module InputFormatConstants
28
- SECTIONS = [
29
- Problem::SECTION_IN_FMT,
30
- Problem::SECTION_IO_FMT
31
- ].freeze
32
- ITEM_PAT = /\{*[A-Za-z]+(?:_[A-Za-z]+)*\}*/.freeze
33
- SZ = '(_(?<sz>\S+)|{(?<sz>\S+)})'
34
- SINGLE_PAT = /([A-Za-z{][A-Za-z_0-9{}]*)/.freeze
35
- MATCHERS = [
36
- InputFormatMatcher.new(
37
- :matrix, :number,
196
+ # matcher constants
197
+ module InputFormatMatcherConstants
198
+ include InputFormatConstants
199
+
200
+ MATRIX_MATCHER = InputFormatMatcher.new(
201
+ container: :matrix,
202
+ pat:
38
203
  /
39
204
  \A
40
- (?<v>#{ITEM_PAT})[_{]\{*[01][,_]?[01]\}*
41
- (\s+\k<v>[_{]\S+)*
42
- (\s+\.+)?
43
- (\s+\k<v>#{SZ})+
205
+ (?<v>#{RE_ITEM})#{RE_IX_00}
206
+ (\s+(\.+|\k<v>#{RE_IX}))*
207
+ \s+\k<v>#{RE_SZ}
44
208
  \z
45
209
  /x,
46
- ->(m) { [m[:v]] },
47
- lambda { |((v))|
210
+ gen_names: ->(m) { [m[:v]] },
211
+ gen_pat2:
212
+ lambda { |(v)|
48
213
  /
49
214
  \A
50
- #{v}[_{]\S+
51
- (\s+#{v}#{SZ})*
52
- (\s+\.+)?
53
- (\s+#{v}#{SZ})*
215
+ #{v}#{RE_IX}
216
+ (\s+(\.+|#{v}#{RE_IX}))*
217
+ \s+(\.+|#{v}#{RE_SZ})
54
218
  \z
55
219
  /x
56
220
  }
57
- ),
58
- InputFormatMatcher.new(
59
- :matrix, :char,
221
+ )
222
+ MATRIX_CHAR_MATCHER = InputFormatMatcher.new(
223
+ container: :matrix,
224
+ item: :char,
225
+ pat:
60
226
  /
61
227
  \A
62
- (?<v>#{ITEM_PAT})[_{]\{*[01][,_]?[01]\}*
63
- (\k<v>[_{]\S+)*
64
- (\s*\.+\s*)?
65
- (\k<v>#{SZ})+
228
+ (?<v>#{RE_ITEM})#{RE_IX_00}
229
+ (\s*\.+\s*|\k<v>#{RE_IX})*
230
+ \k<v>#{RE_SZ}
66
231
  \z
67
232
  /x,
68
- ->(m) { [m[:v]] },
69
- lambda { |((v))|
233
+ gen_names: ->(m) { [m[:v]] },
234
+ gen_pat2:
235
+ lambda { |(v)|
70
236
  /
71
237
  \A
72
- (#{v}[_{]\S+)+
73
- (\s*\.+\s*)?
74
- (#{v}#{SZ})+
238
+ (#{v}#{RE_IX})+
239
+ (\s*\.+\s*|#{v}#{RE_IX})*
240
+ (\s*\.+\s*|#{v}#{RE_SZ})
75
241
  \z
76
242
  /x
77
243
  }
78
- ),
79
- InputFormatMatcher.new(
80
- :harray, :number,
244
+ )
245
+ HARRAY_MATCHER = InputFormatMatcher.new(
246
+ container: :harray,
247
+ pat:
81
248
  /
82
249
  \A
83
- (?<v>#{ITEM_PAT})[_{]\{*[0-9]\}*
84
- (\s+\k<v>[_{]\S+)*
85
- (\s+\.+)?
86
- (\s+\k<v>#{SZ})+
250
+ (?<v>#{RE_ITEM})#{RE_IX_0}
251
+ (\s+(\.+|\k<v>#{RE_IX}))*
252
+ \s+\k<v>#{RE_SZ}
87
253
  \z
88
254
  /x,
89
- ->(m) { [m[:v]] },
90
- nil
91
- ),
92
- InputFormatMatcher.new(
93
- :harray, :char,
255
+ gen_names: ->(m) { [m[:v]] }
256
+ )
257
+ HARRAY_CHAR_MATCHER = InputFormatMatcher.new(
258
+ container: :harray,
259
+ item: :char,
260
+ pat:
94
261
  /
95
262
  \A
96
- (?<v>#{ITEM_PAT})[_{]\{*[0-9]\}*
97
- (\k<v>[_{]\S+)*
98
- (\s*\.+\s*)?
99
- (\k<v>#{SZ})+
263
+ (?<v>#{RE_ITEM})#{RE_IX_0}
264
+ (\s*\.+\s*|\k<v>#{RE_IX})*
265
+ \k<v>#{RE_SZ}
100
266
  \z
101
267
  /x,
102
- ->(m) { [m[:v]] },
103
- nil
104
- ),
105
- InputFormatMatcher.new(
106
- :varray, :number,
268
+ gen_names: ->(m) { [m[:v]] }
269
+ )
270
+ VARRAY_MATRIX_MATCHER = InputFormatMatcher.new(
271
+ container: :varray_matrix,
272
+ pat:
107
273
  /
108
274
  \A
109
- #{ITEM_PAT}[_{]\{*(?<sz>[0-9]+)\}*
110
- (\s+#{ITEM_PAT}[_{]\{*\k<sz>\}*)*
275
+ (?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
276
+ \s+(?<m>#{RE_ITEM})#{RE_IX_00}
277
+ (\s+(\.+|\k<m>#{RE_IX}))*
278
+ \s+\k<m>#{RE_SZ}
111
279
  \z
112
280
  /x,
113
- ->(m) { m[0].split.map { |w| w.scan(ITEM_PAT)[0] } },
281
+ gen_names:
282
+ ->(m) { [*m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }, m[:m]] },
283
+ gen_pat2:
114
284
  lambda { |vs|
115
- pat2 = vs.map { |v| v + SZ }.join('\s+')
116
- /\A#{pat2}\z/
285
+ ws = vs[0..-2].map { |v| v + RE_IX.source }.join('\s+')
286
+ m = vs[-1]
287
+ /
288
+ \A
289
+ #{ws}
290
+ \s+#{m}#{RE_IX}
291
+ (\s+(\.+|#{m}#{RE_IX}))*
292
+ \s+(\.+|#{m}#{RE_SZ})
293
+ \z
294
+ /x
117
295
  }
118
- ),
119
- InputFormatMatcher.new(
120
- :single, :number,
121
- /\A(.*\s)?#{SINGLE_PAT}(\s.*)?\z/,
122
- ->(m) { m[0].split.select { |w| w =~ /\A#{SINGLE_PAT}\z/ } },
123
- nil
124
- )
296
+ )
297
+ VARRAY_MATRIX_CHAR_MATCHER = InputFormatMatcher.new(
298
+ container: :varray_matrix,
299
+ item: :char,
300
+ pat:
301
+ /
302
+ \A
303
+ (?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
304
+ \s+(?<m>#{RE_ITEM})#{RE_IX_00}
305
+ (\s*\.+\s*|\k<m>#{RE_IX})*
306
+ \k<m>#{RE_SZ} \z
307
+ /x,
308
+ gen_names:
309
+ ->(m) { [*m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }, m[:m]] },
310
+ gen_pat2:
311
+ lambda { |vs|
312
+ ws = vs[0..-2].map { |v| v + RE_IX.source }.join('\s+')
313
+ m = vs[-1]
314
+ /
315
+ \A
316
+ #{ws}
317
+ \s+#{m}#{RE_IX}
318
+ (\s*\.+\s*|#{m}#{RE_IX})*
319
+ (\s*\.+\s*|#{m}#{RE_SZ})
320
+ \z
321
+ /x
322
+ }
323
+ )
324
+ MATRIX_VARRAY_MATCHER = InputFormatMatcher.new(
325
+ container: :matrix_varray,
326
+ pat:
327
+ /
328
+ \A
329
+ (?<m>#{RE_ITEM})#{RE_IX_00}
330
+ (\s+(\.+|\k<m>#{RE_IX}))*
331
+ \s+\k<m>#{RE_SZ}
332
+ \s+(?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
333
+ \z
334
+ /x,
335
+ gen_names:
336
+ ->(m) { [m[:m], *m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }] },
337
+ gen_pat2:
338
+ lambda { |vs|
339
+ m = vs[0]
340
+ ws = vs[1..-1].map { |v| v + RE_IX.source }.join('\s+')
341
+ /
342
+ \A
343
+ #{m}#{RE_IX}
344
+ (\s+(\.+|#{m}#{RE_IX}))*
345
+ \s+(\.+|#{m}#{RE_SZ})
346
+ \s+#{ws}
347
+ \z
348
+ /x
349
+ }
350
+ )
351
+ VMATRIX_MATCHER = InputFormatMatcher.new(
352
+ container: :vmatrix,
353
+ pat:
354
+ /
355
+ \A
356
+ #{RE_ITEM}#{RE_SZ_00} (\s+#{RE_ITEM}#{RE_SZ_REF})*
357
+ \z
358
+ /x,
359
+ gen_names: ->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] } },
360
+ gen_pat2:
361
+ lambda { |vs|
362
+ ws = [
363
+ vs[0] + RE_SZ.source,
364
+ *vs[1..-1]&.map { |v| v + RE_IX.source }
365
+ ].join('\s+')
366
+ /\A#{ws}\z/
367
+ }
368
+ )
369
+ HMATRIX_MATCHER = InputFormatMatcher.new(
370
+ container: :hmatrix,
371
+ pat:
372
+ /
373
+ \A
374
+ #{RE_ITEM}#{RE_IX_00}
375
+ (\s+(\.+|#{RE_ITEM}#{RE_IX_99}))*
376
+ \s+#{RE_ITEM}#{RE_SZ_99}
377
+ \z
378
+ /x,
379
+ gen_names:
380
+ ->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] }.uniq },
381
+ gen_pat2:
382
+ lambda { |vs|
383
+ ws1 = vs.map { |v| v + RE_IX.source }.join('\s+')
384
+ ws2 = [
385
+ vs[0] + RE_SZ.source,
386
+ *vs[1..-1]&.map { |v| v + RE_IX.source }
387
+ ].join('\s+')
388
+ /
389
+ \A
390
+ #{ws1} (\s+(\.+|#{ws1}))* \s+(\.+|#{ws2})
391
+ \z
392
+ /x
393
+ }
394
+ )
395
+ VARRAY_MATCHER = InputFormatMatcher.new(
396
+ container: :varray,
397
+ pat:
398
+ /
399
+ \A
400
+ #{RE_ITEM}#{RE_SZ_0} (\s+#{RE_ITEM}#{RE_SZ_REF})*
401
+ \z
402
+ /x,
403
+ gen_names:
404
+ ->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] } },
405
+ gen_pat2:
406
+ lambda { |vs|
407
+ ws = [
408
+ vs[0] + RE_SZ.source,
409
+ *vs[1..-1]&.map { |v| v + RE_IX.source }
410
+ ].join('\s+')
411
+ /\A#{ws}\z/
412
+ }
413
+ )
414
+ SINGLE_MATCHER = InputFormatMatcher.new(
415
+ container: :single,
416
+ pat: /\A(.*\s)?#{RE_SINGLE}(\s.*)?\z/,
417
+ gen_names: ->(m) { m[0].split.select { |w| w =~ /\A#{RE_SINGLE}\z/ } }
418
+ )
419
+ MATCHERS = [
420
+ MATRIX_MATCHER,
421
+ MATRIX_CHAR_MATCHER,
422
+ HARRAY_MATCHER,
423
+ HARRAY_CHAR_MATCHER,
424
+ VARRAY_MATRIX_MATCHER,
425
+ VARRAY_MATRIX_CHAR_MATCHER,
426
+ MATRIX_VARRAY_MATCHER,
427
+ VMATRIX_MATCHER,
428
+ HMATRIX_MATCHER,
429
+ VARRAY_MATCHER,
430
+ SINGLE_MATCHER
125
431
  ].freeze
126
432
  end
127
433
 
128
434
  # parses input data format and generates input definitons
129
435
  module InputFormat
436
+ extend InputFormatUtils
130
437
  include InputFormatConstants
438
+ include InputFormatMatcherConstants
131
439
 
132
440
  module_function
133
441
 
134
442
  def process(pbm)
135
443
  return unless (str = find_fmt(pbm))
136
444
 
137
- inpdefs = parse(str, pbm.samples)
138
- pbm.formats = inpdefs
445
+ inpdefs = parse(str)
446
+ pbm.formats_src = inpdefs
139
447
  end
140
448
 
141
449
  def find_fmt(pbm)
142
450
  str = nil
143
451
  SECTIONS.any? do |key|
144
- str = pbm.sections[key]&.code_block_html
145
- str && !str.empty?
452
+ (str = pbm.sections[key]&.code_block_html) && !str.empty?
146
453
  end
147
454
  str
148
455
  end
149
456
 
150
- def parse(str, smps)
457
+ def parse(str)
151
458
  lines = normalize_fmt(str)
152
- inpdefs = parse_fmt(lines)
153
- normalize_defs!(inpdefs)
154
- smpx = max_smp(smps)
155
- smpx && match_smp!(inpdefs, smpx)
156
- inpdefs
157
- end
158
-
159
- def normalize_fmt(fmt)
160
- # 1) &npsp; , fill-width space -> half width space
161
- # 2) {i, j}->{i,j} for nested {}
162
- fmt
163
- .tr('0-9A-Za-z', '0-9A-Za-z')
164
- .gsub(/[[:space:]]/) { |c| c.gsub(/[^\n]/, ' ') } # 1)
165
- .gsub(%r{<var>([^<>]+)</var>}i, '\1') # <sub><var>N</var></sub>
166
- .gsub(%r{<sup>([^<>]+)</sup>}i, '^\1')
167
- .gsub(%r{<sub>([^<>]+)</sub>}i, '_{\1}')
168
- .gsub(%r{<sub>([^<>]+)</sub>}i, '_{\1}') # for nested<sub>
169
- .gsub(/<("[^"]*"|'[^']*'|[^'"<>])*>/, '')
170
- .gsub('&amp;', '&')
171
- .gsub('&gt;', '>')
172
- .gsub('&lt;', '<')
173
- .gsub('\\ ', ' ')
174
- .gsub('\\(', '')
175
- .gsub('\\)', '')
176
- .gsub('\\lvert', '|')
177
- .gsub('\\rvert', '|')
178
- .gsub('\\mathit', '')
179
- .gsub('\\times', '*')
180
- .gsub(/\\begin(\{[^{}]*\})*/, '')
181
- .gsub(/\\end(\{[^{}]*\})*/, '')
182
- .gsub(/\\[cdlv]?dots/, '..')
183
- .gsub(/\{\}/, ' ')
184
- .gsub('−', '-') # fill width hyphen
185
- .gsub(/[・:‥⋮︙…]+/, '..')
186
- .gsub(/[\\$']/, '') # s' -> s
187
- .gsub(/[&~|]/, ' ') # |S| -> S
188
- .gsub(%r{[-/:](#{SINGLE_PAT})}, ' \1') # a-b, a/b, a:b -> a b
189
- .gsub(/^\s*[.:][\s.:]*$/, '..')
190
- .tr('()', '{}')
191
- .gsub(/(?<bl>\{(?:[^{}]|\g<bl>)*\})/) { |w| w.delete(' ') } # 2)
192
- .split("\n")
193
- .map(&:strip)
459
+ parse_fmt(lines)
194
460
  end
195
461
 
196
462
  def parse_fmt(lines)
@@ -199,85 +465,18 @@ module AtCoderFriends
199
465
  if matcher
200
466
  next if matcher.match2(line)
201
467
 
202
- ret.last.size = matcher.size
468
+ ret << matcher.to_inpdef
203
469
  end
204
470
  if (matcher = MATCHERS.find { |m| m.match(line) })
205
- ret << Problem::InputFormat.new(
206
- matcher.container, matcher.item, matcher.names, ''
207
- )
208
471
  elsif !line.empty?
209
472
  puts "unknown format: #{line}"
210
- # ret << Problem::InputFormat.new(:unknown, nil, line)
473
+ ret << unknown_fmt(line)
211
474
  end
212
475
  end
213
476
  end
214
477
 
215
- def normalize_defs!(inpdefs)
216
- inpdefs.each do |inpdef|
217
- inpdef.names = normalize_names(inpdef.names)
218
- inpdef.size = normalize_size(inpdef.container, inpdef.size)
219
- end
220
- end
221
-
222
- def normalize_names(names)
223
- return names unless names.is_a?(Array)
224
-
225
- names.map { |nm| nm.delete('{}').gsub(/(\A_+|_+\z)/, '') }
226
- end
227
-
228
- def normalize_size(container, size)
229
- sz =
230
- case container
231
- when :matrix
232
- matrix_size(size)
233
- when :harray, :varray
234
- [size]
235
- else
236
- []
237
- end
238
- sz.map do |w|
239
- w
240
- .delete('{},')
241
- .gsub(/(\A_+|(_|-1)+\z)/, '') # extra underscores, N-1 -> N
242
- end
243
- end
244
-
245
- def matrix_size(str)
246
- sz = str.scan(/([^{}]+|\{[^{}]+\}})/).flatten
247
- return sz if sz.size == 2
248
-
249
- sz = str.split(',')
250
- return sz if sz.size == 2
251
-
252
- sz = str.split('_')
253
- return sz if sz.size == 2
254
-
255
- str = str.delete('{},')
256
- len = str.size
257
- if len.positive? && len.even?
258
- return str.chars.each_slice(len / 2).map(&:join)
259
- end
260
-
261
- [str[0] || '_', str[1..-1] || '_']
262
- end
263
-
264
- def max_smp(smps)
265
- smps
266
- .select { |smp| smp.ext == :in }
267
- .max_by { |smp| smp.txt.size }
268
- &.txt
269
- end
270
-
271
- def match_smp!(inpdefs, smp)
272
- lines = smp.split("\n")
273
- inpdefs.each_with_index do |inpdef, i|
274
- break if i >= lines.size
275
- next if inpdef.item != :number
276
-
277
- inpdef.item = :string if lines[i].split[0] =~ /[^\-0-9]/
278
- break if %i[varray matrix].include?(inpdef.container)
279
- end
280
- inpdefs
478
+ def unknown_fmt(line)
479
+ Problem::InputFormat.new(container: :unknown, item: line)
281
480
  end
282
481
  end
283
482
  end