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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/Gemfile.lock +2 -2
- data/docs/CONFIGURATION.md +2 -4
- data/lib/at_coder_friends/generator/base.rb +41 -0
- data/lib/at_coder_friends/generator/cxx_builtin.rb +196 -143
- data/lib/at_coder_friends/generator/main.rb +7 -2
- data/lib/at_coder_friends/generator/ruby_builtin.rb +97 -51
- data/lib/at_coder_friends/parser/input_format.rb +387 -188
- data/lib/at_coder_friends/parser/input_type.rb +91 -0
- data/lib/at_coder_friends/parser/main.rb +1 -0
- data/lib/at_coder_friends/problem.rb +81 -6
- data/lib/at_coder_friends/version.rb +1 -1
- data/lib/at_coder_friends.rb +2 -0
- data/tasks/regression/check_const.rake +15 -14
- data/tasks/regression/check_diff.rake +2 -2
- data/tasks/regression/check_fmt.rake +18 -15
- data/tasks/regression/check_parse.rake +3 -4
- data/tasks/regression/list_handler.rb +46 -0
- data/tasks/regression/regression.rb +7 -41
- data/tasks/regression/report_handler.rb +20 -0
- data/tasks/regression/section_list.rake +1 -1
- data/templates/{cxx_builtin_interactive.cxx → cxx_builtin.cxx.erb} +33 -2
- data/templates/{ruby_builtin_interactive.rb → ruby_builtin.rb.erb} +17 -2
- metadata +8 -6
- data/templates/cxx_builtin_default.cxx +0 -26
- data/templates/ruby_builtin_default.rb +0 -7
@@ -2,195 +2,461 @@
|
|
2
2
|
|
3
3
|
module AtCoderFriends
|
4
4
|
module Parser
|
5
|
-
|
6
|
-
|
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('&', '&')
|
56
|
+
.gsub('>', '>')
|
57
|
+
.gsub('<', '<')
|
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
|
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 =
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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>#{
|
41
|
-
(\s
|
42
|
-
|
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
|
-
|
47
|
-
|
210
|
+
gen_names: ->(m) { [m[:v]] },
|
211
|
+
gen_pat2:
|
212
|
+
lambda { |(v)|
|
48
213
|
/
|
49
214
|
\A
|
50
|
-
#{v}
|
51
|
-
(\s
|
52
|
-
|
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
|
-
|
59
|
-
|
221
|
+
)
|
222
|
+
MATRIX_CHAR_MATCHER = InputFormatMatcher.new(
|
223
|
+
container: :matrix,
|
224
|
+
item: :char,
|
225
|
+
pat:
|
60
226
|
/
|
61
227
|
\A
|
62
|
-
(?<v>#{
|
63
|
-
(\k<v
|
64
|
-
|
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
|
-
|
69
|
-
|
233
|
+
gen_names: ->(m) { [m[:v]] },
|
234
|
+
gen_pat2:
|
235
|
+
lambda { |(v)|
|
70
236
|
/
|
71
237
|
\A
|
72
|
-
(#{v}
|
73
|
-
(\s*\.+\s*
|
74
|
-
(
|
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
|
-
|
80
|
-
|
244
|
+
)
|
245
|
+
HARRAY_MATCHER = InputFormatMatcher.new(
|
246
|
+
container: :harray,
|
247
|
+
pat:
|
81
248
|
/
|
82
249
|
\A
|
83
|
-
(?<v>#{
|
84
|
-
(\s
|
85
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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>#{
|
97
|
-
(\k<v
|
98
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
#{
|
110
|
-
|
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
|
-
|
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
|
-
|
116
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
138
|
-
pbm.
|
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
|
457
|
+
def parse(str)
|
151
458
|
lines = normalize_fmt(str)
|
152
|
-
|
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('&', '&')
|
171
|
-
.gsub('>', '>')
|
172
|
-
.gsub('<', '<')
|
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
|
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
|
-
|
473
|
+
ret << unknown_fmt(line)
|
211
474
|
end
|
212
475
|
end
|
213
476
|
end
|
214
477
|
|
215
|
-
def
|
216
|
-
|
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
|