at_coder_friends 0.6.2 → 0.6.3

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.
@@ -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