at_coder_friends 0.6.2 → 0.6.7
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/.rubocop.yml +3 -2
- data/.travis.yml +2 -2
- data/CHANGELOG.md +53 -0
- data/Gemfile.lock +32 -36
- data/README.md +1 -1
- data/at_coder_friends.gemspec +5 -7
- data/config/default.yml +146 -72
- data/docs/CONFIGURATION.md +224 -140
- data/lib/at_coder_friends.rb +3 -0
- data/lib/at_coder_friends/cli.rb +8 -0
- data/lib/at_coder_friends/config_loader.rb +11 -3
- data/lib/at_coder_friends/context.rb +10 -6
- data/lib/at_coder_friends/emitter.rb +2 -2
- data/lib/at_coder_friends/generator/base.rb +42 -0
- data/lib/at_coder_friends/generator/cxx_builtin.rb +196 -143
- data/lib/at_coder_friends/generator/main.rb +8 -2
- data/lib/at_coder_friends/generator/ruby_builtin.rb +97 -51
- data/lib/at_coder_friends/parser/constraints.rb +1 -0
- data/lib/at_coder_friends/parser/input_format.rb +389 -188
- data/lib/at_coder_friends/parser/input_type.rb +92 -0
- data/lib/at_coder_friends/parser/main.rb +1 -0
- data/lib/at_coder_friends/parser/modulo.rb +1 -1
- data/lib/at_coder_friends/parser/sections.rb +3 -3
- data/lib/at_coder_friends/path_info.rb +51 -0
- data/lib/at_coder_friends/path_util.rb +0 -31
- data/lib/at_coder_friends/problem.rb +81 -6
- data/lib/at_coder_friends/scraping/agent.rb +11 -2
- data/lib/at_coder_friends/scraping/custom_test.rb +5 -6
- data/lib/at_coder_friends/scraping/submission.rb +6 -7
- data/lib/at_coder_friends/scraping/tasks.rb +8 -3
- data/lib/at_coder_friends/test_runner/base.rb +17 -4
- data/lib/at_coder_friends/test_runner/judge.rb +7 -9
- data/lib/at_coder_friends/test_runner/sample.rb +2 -4
- data/lib/at_coder_friends/verifier.rb +2 -3
- data/lib/at_coder_friends/version.rb +1 -1
- data/templates/{cxx_builtin_interactive.cxx → cxx_builtin.cxx.erb} +26 -4
- data/templates/{ruby_builtin_interactive.rb → ruby_builtin.rb.erb} +17 -3
- metadata +11 -17
- data/tasks/regression/check_const.rake +0 -136
- data/tasks/regression/check_diff.rake +0 -30
- data/tasks/regression/check_fmt.rake +0 -42
- data/tasks/regression/check_parse.rake +0 -70
- data/tasks/regression/regression.rb +0 -72
- data/tasks/regression/section_list.rake +0 -53
- data/tasks/regression/setup.rake +0 -48
- data/templates/cxx_builtin_default.cxx +0 -26
- data/templates/ruby_builtin_default.rb +0 -7
@@ -14,8 +14,14 @@ module AtCoderFriends
|
|
14
14
|
def process(pbm)
|
15
15
|
generators = ctx.config.dig('generators') || []
|
16
16
|
generators.each do |gen_name|
|
17
|
-
|
18
|
-
|
17
|
+
begin
|
18
|
+
gen_obj = load_obj(gen_name)
|
19
|
+
gen_obj.process(pbm)
|
20
|
+
rescue StandardError => e
|
21
|
+
puts "an error occurred in generator:#{gen_name}."
|
22
|
+
puts e.to_s
|
23
|
+
puts e.backtrace
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
|
@@ -3,43 +3,20 @@
|
|
3
3
|
module AtCoderFriends
|
4
4
|
module Generator
|
5
5
|
# generates Ruby source from problem description
|
6
|
-
class RubyBuiltin
|
6
|
+
class RubyBuiltin < Base
|
7
7
|
ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
|
8
8
|
TMPL_DIR = File.join(ACF_HOME, 'templates')
|
9
|
-
DEFAULT_TMPL = File.join(TMPL_DIR, '
|
10
|
-
|
9
|
+
DEFAULT_TMPL = File.join(TMPL_DIR, 'ruby_builtin.rb.erb')
|
10
|
+
ATTRS = Attributes.new(:rb, DEFAULT_TMPL)
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
def initialize(cfg = nil)
|
15
|
-
@cfg = cfg || {}
|
16
|
-
end
|
17
|
-
|
18
|
-
def process(pbm)
|
19
|
-
src = generate(pbm)
|
20
|
-
pbm.add_src(:rb, src)
|
21
|
-
end
|
22
|
-
|
23
|
-
def generate(pbm)
|
24
|
-
@pbm = pbm
|
25
|
-
File
|
26
|
-
.read(select_template)
|
27
|
-
.gsub('### URL ###', pbm.url)
|
28
|
-
.gsub('### CONSTS ###', gen_consts.join("\n"))
|
29
|
-
.gsub('### DCLS ###', gen_decls.join("\n"))
|
30
|
-
.gsub('### OUTPUT ###', gen_output)
|
12
|
+
def attrs
|
13
|
+
ATTRS
|
31
14
|
end
|
32
15
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def default_template
|
38
|
-
cfg['default_template'] || DEFAULT_TMPL
|
39
|
-
end
|
40
|
-
|
41
|
-
def interactive_template
|
42
|
-
cfg['interactive_template'] || INTERACTIVE_TMPL
|
16
|
+
def render(src)
|
17
|
+
src = embed_lines(src, '### CONSTS ###', gen_consts)
|
18
|
+
src = embed_lines(src, '### DCLS ###', gen_decls)
|
19
|
+
src
|
43
20
|
end
|
44
21
|
|
45
22
|
def gen_consts(constants = pbm.constants)
|
@@ -64,35 +41,45 @@ module AtCoderFriends
|
|
64
41
|
when :harray
|
65
42
|
gen_harray_decl(inpdef)
|
66
43
|
when :varray
|
67
|
-
|
68
|
-
gen_varray_1_decl(inpdef)
|
69
|
-
else
|
70
|
-
gen_varray_n_decl(inpdef)
|
71
|
-
end
|
44
|
+
gen_varray_decl(inpdef)
|
72
45
|
when :matrix
|
73
46
|
gen_matrix_decl(inpdef)
|
47
|
+
when :varray_matrix, :matrix_varray
|
48
|
+
gen_cmb_decl(inpdef)
|
49
|
+
when :vmatrix
|
50
|
+
gen_vmatrix_decl(inpdef)
|
51
|
+
when :hmatrix
|
52
|
+
gen_hmatrix_decl(inpdef)
|
74
53
|
end
|
75
54
|
end
|
76
55
|
|
77
56
|
def gen_single_decl(inpdef)
|
78
57
|
names = inpdef.names
|
79
58
|
dcl = names.join(', ')
|
80
|
-
expr = gen_expr(inpdef
|
59
|
+
expr = gen_expr(inpdef, names.size > 1)
|
81
60
|
"#{dcl} = #{expr}"
|
82
61
|
end
|
83
62
|
|
84
63
|
def gen_harray_decl(inpdef)
|
85
64
|
v = inpdef.names[0]
|
86
65
|
dcl = "#{v}s"
|
87
|
-
expr = gen_expr(inpdef
|
66
|
+
expr = gen_expr(inpdef, true)
|
88
67
|
"#{dcl} = #{expr}"
|
89
68
|
end
|
90
69
|
|
70
|
+
def gen_varray_decl(inpdef)
|
71
|
+
if inpdef.names.size == 1
|
72
|
+
gen_varray_1_decl(inpdef)
|
73
|
+
else
|
74
|
+
gen_varray_n_decl(inpdef)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
91
78
|
def gen_varray_1_decl(inpdef)
|
92
79
|
v = inpdef.names[0]
|
93
80
|
sz = inpdef.size[0]
|
94
81
|
dcl = "#{v}s"
|
95
|
-
expr = gen_expr(inpdef
|
82
|
+
expr = gen_expr(inpdef, false)
|
96
83
|
"#{dcl} = Array.new(#{sz}) { #{expr} }"
|
97
84
|
end
|
98
85
|
|
@@ -100,7 +87,7 @@ module AtCoderFriends
|
|
100
87
|
names = inpdef.names
|
101
88
|
sz = inpdef.size[0]
|
102
89
|
dcl = names.map { |v| "#{v}s[i]" }.join(', ')
|
103
|
-
expr = gen_expr(inpdef
|
90
|
+
expr = gen_expr(inpdef, true)
|
104
91
|
ret = []
|
105
92
|
ret += names.map { |v| "#{v}s = Array.new(#{sz})" }
|
106
93
|
ret << "#{sz}.times do |i|"
|
@@ -113,28 +100,87 @@ module AtCoderFriends
|
|
113
100
|
v = inpdef.names[0]
|
114
101
|
sz = inpdef.size[0]
|
115
102
|
decl = "#{v}ss"
|
116
|
-
expr = gen_expr(inpdef
|
103
|
+
expr = gen_expr(inpdef, true)
|
117
104
|
"#{decl} = Array.new(#{sz}) { #{expr} }"
|
118
105
|
end
|
119
106
|
|
120
|
-
def
|
121
|
-
|
107
|
+
def gen_cmb_decl(inpdef)
|
108
|
+
mx = inpdef.container == :varray_matrix ? -1 : 0
|
109
|
+
vs = inpdef.names.map { |v| "#{v}s" }
|
110
|
+
vs[mx] += 's'
|
111
|
+
sz = inpdef.size[0]
|
112
|
+
dcls = vs.map { |v| "#{v}[i]" }
|
113
|
+
dcls[mx] = '*' + dcls[mx] unless inpdef.item == :char
|
114
|
+
dcl = dcls.join(', ')
|
115
|
+
expr = gen_cmb_expr(inpdef)
|
116
|
+
ret = []
|
117
|
+
ret += vs.map { |v| "#{v} = Array.new(#{sz})" }
|
118
|
+
ret << "#{sz}.times do |i|"
|
119
|
+
ret << " #{dcl} = #{expr}"
|
120
|
+
ret << 'end'
|
121
|
+
ret
|
122
|
+
end
|
123
|
+
|
124
|
+
def gen_vmatrix_decl(inpdef)
|
125
|
+
names = inpdef.names
|
126
|
+
sz1, sz2 = inpdef.size
|
127
|
+
dcl = names.map { |v| "#{v}ss[i][j]" }.join(', ')
|
128
|
+
expr = gen_expr(inpdef, true)
|
129
|
+
ret = []
|
130
|
+
ret += names.map do |v|
|
131
|
+
"#{v}ss = Array.new(#{sz1}) { Array.new(#{sz2}) }"
|
132
|
+
end
|
133
|
+
ret << "#{sz1}.times do |i|"
|
134
|
+
ret << " #{sz2}.times do |j|"
|
135
|
+
ret << " #{dcl} = #{expr}"
|
136
|
+
ret << ' end'
|
137
|
+
ret << 'end'
|
138
|
+
ret
|
139
|
+
end
|
140
|
+
|
141
|
+
def gen_hmatrix_decl(inpdef)
|
142
|
+
names = inpdef.names
|
143
|
+
sz = inpdef.size[0]
|
144
|
+
dcl = names.map { |v| "#{v}ss[i]" }.join(', ')
|
145
|
+
expr = gen_expr(inpdef, true)
|
146
|
+
ret = []
|
147
|
+
ret += names.map { |v| "#{v}ss = Array.new(#{sz})" }
|
148
|
+
ret << "#{sz}.times do |i|"
|
149
|
+
ret << " #{dcl} = #{expr}.each_slice(#{names.size}).to_a.transpose"
|
150
|
+
ret << 'end'
|
151
|
+
ret
|
152
|
+
end
|
153
|
+
|
154
|
+
def gen_expr(inpdef, split)
|
155
|
+
read = gen_read(inpdef.delim)
|
156
|
+
case inpdef.item
|
122
157
|
when :number
|
123
|
-
split ?
|
158
|
+
split ? "#{read}.split.map(&:to_i)" : "#{read}.to_i"
|
159
|
+
when :decimal
|
160
|
+
split ? "#{read}.split.map(&:to_f)" : "#{read}.to_f"
|
124
161
|
when :string
|
125
|
-
split ?
|
162
|
+
split ? "#{read}.chomp.split" : "#{read}.chomp"
|
126
163
|
when :char
|
127
164
|
'gets.chomp'
|
128
165
|
end
|
129
166
|
end
|
130
167
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
168
|
+
def gen_cmb_expr(inpdef)
|
169
|
+
read = gen_read(inpdef.delim)
|
170
|
+
case inpdef.item
|
171
|
+
when :number
|
172
|
+
"#{read}.split.map(&:to_i)"
|
173
|
+
when :decimal
|
174
|
+
"#{read}.split.map(&:to_f)"
|
175
|
+
when :string, :char
|
176
|
+
"#{read}.chomp.split"
|
136
177
|
end
|
137
178
|
end
|
179
|
+
|
180
|
+
def gen_read(delim)
|
181
|
+
sub = delim.chars.map { |d| ".gsub('#{d}', ' ')" }.join
|
182
|
+
"gets#{sub}"
|
183
|
+
end
|
138
184
|
end
|
139
185
|
end
|
140
186
|
end
|
@@ -2,195 +2,463 @@
|
|
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(/\\hspace\{\d+pt\}/, ' ')
|
60
|
+
.gsub('\\(', '')
|
61
|
+
.gsub('\\)', '')
|
62
|
+
.gsub('\\lvert', '|')
|
63
|
+
.gsub('\\rvert', '|')
|
64
|
+
.gsub('\\mathit', '')
|
65
|
+
.gsub('\\mathrm', '')
|
66
|
+
.gsub('\\times', '*')
|
67
|
+
.gsub(/\\begin(\{[^{}]*\})*/, '')
|
68
|
+
.gsub(/\\end(\{[^{}]*\})*/, '')
|
69
|
+
.gsub(/\\[cdlv]?dots/, '..')
|
70
|
+
.gsub(/\{\}/, ' ')
|
71
|
+
.gsub('−', '-') # full width hyphen
|
72
|
+
.gsub(/[・.:‥⋮︙…]+/, '..')
|
73
|
+
.gsub(/[\\$']/, '') # s' -> s
|
74
|
+
.gsub(/[&~|]/, ' ') # |S| -> S
|
75
|
+
.gsub(/^\s*[.:][\s.:]*$/, '..')
|
76
|
+
.tr('()', '{}')
|
77
|
+
.gsub(/#{RE_BLOCK}/) { |w| w.delete(' ') } # 2)
|
78
|
+
.split("\n")
|
79
|
+
.map(&:strip)
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_delim(str)
|
83
|
+
# a-b, a/b, a:b -> a b
|
84
|
+
str = str.dup
|
85
|
+
dlms =
|
86
|
+
DELIMS.select { |c| str.gsub!(/#{c}(#{RE_SINGLE})/, ' \1') }.join
|
87
|
+
[str, dlms]
|
88
|
+
end
|
89
|
+
|
90
|
+
def normalize_name(s)
|
91
|
+
s.delete('{},').gsub(/(\A_+|_+\z)/, '')
|
92
|
+
end
|
93
|
+
|
94
|
+
def normalize_names(names)
|
95
|
+
names.map { |nm| normalize_name(nm) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize_size(container, size, ix0)
|
99
|
+
sz = size_array(container, size)
|
100
|
+
sz0 = size_array(container, ix0)
|
101
|
+
|
102
|
+
sz.map.with_index do |s, i|
|
103
|
+
if sz0[i] == '0'
|
104
|
+
# 0 -> 1, N-1 -> N, N-2 -> N-1 if 0 origin
|
105
|
+
s.gsub(/\A0\z/, '1').gsub(/-1\z/, '').gsub(/-2\z/, '-1')
|
106
|
+
else
|
107
|
+
s
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# split size by container dimension
|
113
|
+
def size_array(container, size)
|
114
|
+
(
|
115
|
+
case DIMENSION_TBL[container]
|
116
|
+
when 2
|
117
|
+
split_size(size)
|
118
|
+
when 1
|
119
|
+
[size]
|
120
|
+
when 0
|
121
|
+
[]
|
122
|
+
end
|
123
|
+
).map { |s| normalize_name(s) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def split_size(str)
|
127
|
+
str = str.gsub(/(\A\{|\}\z)/, '') while str =~ /\A#{RE_BLOCK}\z/
|
128
|
+
|
129
|
+
sz = str.split(',')
|
130
|
+
return sz if sz.size == 2
|
131
|
+
|
132
|
+
sz = str.scan(/(?<nbl>[^{}]+)|#{RE_BLOCK}/).flatten.compact
|
133
|
+
return sz if sz.size == 2
|
134
|
+
|
135
|
+
str = str.delete('{},')
|
136
|
+
|
137
|
+
sz = str.scan(/[^_](?:_[^_])?/)
|
138
|
+
return sz if sz.size == 2
|
139
|
+
|
140
|
+
sz = str.split('_')
|
141
|
+
return sz if sz.size == 2
|
142
|
+
|
143
|
+
[str[0] || '_', str[1..-1] || '_']
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# holds regular expressions and matches it with input format string
|
148
|
+
class InputFormatMatcher
|
149
|
+
include InputFormatUtils
|
150
|
+
|
151
|
+
attr_reader :container, :item, :pat, :gen_names, :gen_pat2
|
152
|
+
attr_reader :names, :pat2, :size, :delim, :ix0
|
153
|
+
|
154
|
+
def initialize(
|
155
|
+
container: nil, item: nil,
|
156
|
+
pat: nil, gen_names: nil, gen_pat2: nil
|
157
|
+
)
|
158
|
+
@container = container
|
159
|
+
@item = item
|
160
|
+
@pat = pat
|
161
|
+
@gen_names = gen_names
|
162
|
+
@gen_pat2 = gen_pat2
|
163
|
+
end
|
7
164
|
|
8
165
|
def match(str)
|
166
|
+
str, dlm = extract_delim(str)
|
9
167
|
return false unless (m1 = pat.match(str))
|
10
168
|
|
11
169
|
@names = gen_names.call(m1)
|
12
170
|
@pat2 = gen_pat2&.call(names)
|
13
171
|
@size = m1.names.include?('sz') && m1['sz'] || ''
|
172
|
+
@ix0 = m1.names.include?('ix0') && m1['ix0'] || size
|
173
|
+
@delim = dlm
|
14
174
|
true
|
15
175
|
end
|
16
176
|
|
17
177
|
def match2(str)
|
18
|
-
return false unless
|
178
|
+
return false unless pat2
|
179
|
+
|
180
|
+
str, _dlm = extract_delim(str)
|
19
181
|
return true if /\A\.+\z/ =~ str
|
20
|
-
return false unless (m2 =
|
182
|
+
return false unless (m2 = pat2.match(str))
|
21
183
|
|
22
184
|
m2.names.include?('sz') && @size = m2['sz']
|
23
185
|
true
|
24
186
|
end
|
187
|
+
|
188
|
+
def to_inpdef
|
189
|
+
Problem::InputFormat.new(
|
190
|
+
container: container, item: item,
|
191
|
+
names: normalize_names(names),
|
192
|
+
size: normalize_size(container, size, ix0),
|
193
|
+
delim: delim
|
194
|
+
)
|
195
|
+
end
|
25
196
|
end
|
26
197
|
|
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,
|
198
|
+
# matcher constants
|
199
|
+
module InputFormatMatcherConstants
|
200
|
+
include InputFormatConstants
|
201
|
+
|
202
|
+
MATRIX_MATCHER = InputFormatMatcher.new(
|
203
|
+
container: :matrix,
|
204
|
+
pat:
|
38
205
|
/
|
39
206
|
\A
|
40
|
-
(?<v>#{
|
41
|
-
(\s
|
42
|
-
|
43
|
-
(\s+\k<v>#{SZ})+
|
207
|
+
(?<v>#{RE_ITEM})#{RE_IX_00}
|
208
|
+
(\s+(\.+|\k<v>#{RE_IX}))*
|
209
|
+
\s+\k<v>#{RE_SZ}
|
44
210
|
\z
|
45
211
|
/x,
|
46
|
-
|
47
|
-
|
212
|
+
gen_names: ->(m) { [m[:v]] },
|
213
|
+
gen_pat2:
|
214
|
+
lambda { |(v)|
|
48
215
|
/
|
49
216
|
\A
|
50
|
-
#{v}
|
51
|
-
(\s
|
52
|
-
|
53
|
-
(\s+#{v}#{SZ})*
|
217
|
+
#{v}#{RE_IX}
|
218
|
+
(\s+(\.+|#{v}#{RE_IX}))*
|
219
|
+
\s+(\.+|#{v}#{RE_SZ})
|
54
220
|
\z
|
55
221
|
/x
|
56
222
|
}
|
57
|
-
|
58
|
-
|
59
|
-
|
223
|
+
)
|
224
|
+
MATRIX_CHAR_MATCHER = InputFormatMatcher.new(
|
225
|
+
container: :matrix,
|
226
|
+
item: :char,
|
227
|
+
pat:
|
60
228
|
/
|
61
229
|
\A
|
62
|
-
(?<v>#{
|
63
|
-
(\k<v
|
64
|
-
|
65
|
-
(\k<v>#{SZ})+
|
230
|
+
(?<v>#{RE_ITEM})#{RE_IX_00}
|
231
|
+
(\s*\.+\s*|\k<v>#{RE_IX})*
|
232
|
+
\k<v>#{RE_SZ}
|
66
233
|
\z
|
67
234
|
/x,
|
68
|
-
|
69
|
-
|
235
|
+
gen_names: ->(m) { [m[:v]] },
|
236
|
+
gen_pat2:
|
237
|
+
lambda { |(v)|
|
70
238
|
/
|
71
239
|
\A
|
72
|
-
(#{v}
|
73
|
-
(\s*\.+\s*
|
74
|
-
(
|
240
|
+
(#{v}#{RE_IX})+
|
241
|
+
(\s*\.+\s*|#{v}#{RE_IX})*
|
242
|
+
(\s*\.+\s*|#{v}#{RE_SZ})
|
75
243
|
\z
|
76
244
|
/x
|
77
245
|
}
|
78
|
-
|
79
|
-
|
80
|
-
|
246
|
+
)
|
247
|
+
HARRAY_MATCHER = InputFormatMatcher.new(
|
248
|
+
container: :harray,
|
249
|
+
pat:
|
81
250
|
/
|
82
251
|
\A
|
83
|
-
(?<v>#{
|
84
|
-
(\s
|
85
|
-
|
86
|
-
(\s+\k<v>#{SZ})+
|
252
|
+
(?<v>#{RE_ITEM})#{RE_IX_0}
|
253
|
+
(\s+(\.+|\k<v>#{RE_IX}))*
|
254
|
+
\s+\k<v>#{RE_SZ}
|
87
255
|
\z
|
88
256
|
/x,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
257
|
+
gen_names: ->(m) { [m[:v]] }
|
258
|
+
)
|
259
|
+
HARRAY_CHAR_MATCHER = InputFormatMatcher.new(
|
260
|
+
container: :harray,
|
261
|
+
item: :char,
|
262
|
+
pat:
|
94
263
|
/
|
95
264
|
\A
|
96
|
-
(?<v>#{
|
97
|
-
(\k<v
|
98
|
-
|
99
|
-
(\k<v>#{SZ})+
|
265
|
+
(?<v>#{RE_ITEM})#{RE_IX_0}
|
266
|
+
(\s*\.+\s*|\k<v>#{RE_IX})*
|
267
|
+
\k<v>#{RE_SZ}
|
100
268
|
\z
|
101
269
|
/x,
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
270
|
+
gen_names: ->(m) { [m[:v]] }
|
271
|
+
)
|
272
|
+
VARRAY_MATRIX_MATCHER = InputFormatMatcher.new(
|
273
|
+
container: :varray_matrix,
|
274
|
+
pat:
|
107
275
|
/
|
108
276
|
\A
|
109
|
-
#{
|
110
|
-
|
277
|
+
(?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
|
278
|
+
\s+(?<m>#{RE_ITEM})#{RE_IX_00}
|
279
|
+
(\s+(\.+|\k<m>#{RE_IX}))*
|
280
|
+
\s+\k<m>#{RE_SZ}
|
111
281
|
\z
|
112
282
|
/x,
|
113
|
-
|
283
|
+
gen_names:
|
284
|
+
->(m) { [*m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }, m[:m]] },
|
285
|
+
gen_pat2:
|
114
286
|
lambda { |vs|
|
115
|
-
|
116
|
-
|
287
|
+
ws = vs[0..-2].map { |v| v + RE_IX.source }.join('\s+')
|
288
|
+
m = vs[-1]
|
289
|
+
/
|
290
|
+
\A
|
291
|
+
#{ws}
|
292
|
+
\s+#{m}#{RE_IX}
|
293
|
+
(\s+(\.+|#{m}#{RE_IX}))*
|
294
|
+
\s+(\.+|#{m}#{RE_SZ})
|
295
|
+
\z
|
296
|
+
/x
|
117
297
|
}
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
298
|
+
)
|
299
|
+
VARRAY_MATRIX_CHAR_MATCHER = InputFormatMatcher.new(
|
300
|
+
container: :varray_matrix,
|
301
|
+
item: :char,
|
302
|
+
pat:
|
303
|
+
/
|
304
|
+
\A
|
305
|
+
(?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
|
306
|
+
\s+(?<m>#{RE_ITEM})#{RE_IX_00}
|
307
|
+
(\s*\.+\s*|\k<m>#{RE_IX})*
|
308
|
+
\k<m>#{RE_SZ} \z
|
309
|
+
/x,
|
310
|
+
gen_names:
|
311
|
+
->(m) { [*m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }, m[:m]] },
|
312
|
+
gen_pat2:
|
313
|
+
lambda { |vs|
|
314
|
+
ws = vs[0..-2].map { |v| v + RE_IX.source }.join('\s+')
|
315
|
+
m = vs[-1]
|
316
|
+
/
|
317
|
+
\A
|
318
|
+
#{ws}
|
319
|
+
\s+#{m}#{RE_IX}
|
320
|
+
(\s*\.+\s*|#{m}#{RE_IX})*
|
321
|
+
(\s*\.+\s*|#{m}#{RE_SZ})
|
322
|
+
\z
|
323
|
+
/x
|
324
|
+
}
|
325
|
+
)
|
326
|
+
MATRIX_VARRAY_MATCHER = InputFormatMatcher.new(
|
327
|
+
container: :matrix_varray,
|
328
|
+
pat:
|
329
|
+
/
|
330
|
+
\A
|
331
|
+
(?<m>#{RE_ITEM})#{RE_IX_00}
|
332
|
+
(\s+(\.+|\k<m>#{RE_IX}))*
|
333
|
+
\s+\k<m>#{RE_SZ}
|
334
|
+
\s+(?<vs>#{RE_ITEM}#{RE_SZ2_0} (\s+#{RE_ITEM}#{RE_SZ2_REF})*)
|
335
|
+
\z
|
336
|
+
/x,
|
337
|
+
gen_names:
|
338
|
+
->(m) { [m[:m], *m[:vs].split.map { |w| w.scan(RE_ITEM)[0] }] },
|
339
|
+
gen_pat2:
|
340
|
+
lambda { |vs|
|
341
|
+
m = vs[0]
|
342
|
+
ws = vs[1..-1].map { |v| v + RE_IX.source }.join('\s+')
|
343
|
+
/
|
344
|
+
\A
|
345
|
+
#{m}#{RE_IX}
|
346
|
+
(\s+(\.+|#{m}#{RE_IX}))*
|
347
|
+
\s+(\.+|#{m}#{RE_SZ})
|
348
|
+
\s+#{ws}
|
349
|
+
\z
|
350
|
+
/x
|
351
|
+
}
|
352
|
+
)
|
353
|
+
VMATRIX_MATCHER = InputFormatMatcher.new(
|
354
|
+
container: :vmatrix,
|
355
|
+
pat:
|
356
|
+
/
|
357
|
+
\A
|
358
|
+
#{RE_ITEM}#{RE_SZ_00} (\s+#{RE_ITEM}#{RE_SZ_REF})*
|
359
|
+
\z
|
360
|
+
/x,
|
361
|
+
gen_names: ->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] } },
|
362
|
+
gen_pat2:
|
363
|
+
lambda { |vs|
|
364
|
+
ws = [
|
365
|
+
vs[0] + RE_SZ.source,
|
366
|
+
*vs[1..-1]&.map { |v| v + RE_IX.source }
|
367
|
+
].join('\s+')
|
368
|
+
/\A#{ws}\z/
|
369
|
+
}
|
370
|
+
)
|
371
|
+
HMATRIX_MATCHER = InputFormatMatcher.new(
|
372
|
+
container: :hmatrix,
|
373
|
+
pat:
|
374
|
+
/
|
375
|
+
\A
|
376
|
+
#{RE_ITEM}#{RE_IX_00}
|
377
|
+
(\s+(\.+|#{RE_ITEM}#{RE_IX_99}))*
|
378
|
+
\s+#{RE_ITEM}#{RE_SZ_99}
|
379
|
+
\z
|
380
|
+
/x,
|
381
|
+
gen_names:
|
382
|
+
->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] }.uniq },
|
383
|
+
gen_pat2:
|
384
|
+
lambda { |vs|
|
385
|
+
ws1 = vs.map { |v| v + RE_IX.source }.join('\s+')
|
386
|
+
ws2 = [
|
387
|
+
vs[0] + RE_SZ.source,
|
388
|
+
*vs[1..-1]&.map { |v| v + RE_IX.source }
|
389
|
+
].join('\s+')
|
390
|
+
/
|
391
|
+
\A
|
392
|
+
#{ws1} (\s+(\.+|#{ws1}))* \s+(\.+|#{ws2})
|
393
|
+
\z
|
394
|
+
/x
|
395
|
+
}
|
396
|
+
)
|
397
|
+
VARRAY_MATCHER = InputFormatMatcher.new(
|
398
|
+
container: :varray,
|
399
|
+
pat:
|
400
|
+
/
|
401
|
+
\A
|
402
|
+
#{RE_ITEM}#{RE_SZ_0} (\s+#{RE_ITEM}#{RE_SZ_REF})*
|
403
|
+
\z
|
404
|
+
/x,
|
405
|
+
gen_names:
|
406
|
+
->(m) { m[0].split.map { |w| w.scan(RE_ITEM)[0] } },
|
407
|
+
gen_pat2:
|
408
|
+
lambda { |vs|
|
409
|
+
ws = [
|
410
|
+
vs[0] + RE_SZ.source,
|
411
|
+
*vs[1..-1]&.map { |v| v + RE_IX.source }
|
412
|
+
].join('\s+')
|
413
|
+
/\A#{ws}\z/
|
414
|
+
}
|
415
|
+
)
|
416
|
+
SINGLE_MATCHER = InputFormatMatcher.new(
|
417
|
+
container: :single,
|
418
|
+
pat: /\A(.*\s)?#{RE_SINGLE}(\s.*)?\z/,
|
419
|
+
gen_names: ->(m) { m[0].split.select { |w| w =~ /\A#{RE_SINGLE}\z/ } }
|
420
|
+
)
|
421
|
+
MATCHERS = [
|
422
|
+
MATRIX_MATCHER,
|
423
|
+
MATRIX_CHAR_MATCHER,
|
424
|
+
HARRAY_MATCHER,
|
425
|
+
HARRAY_CHAR_MATCHER,
|
426
|
+
VARRAY_MATRIX_MATCHER,
|
427
|
+
VARRAY_MATRIX_CHAR_MATCHER,
|
428
|
+
MATRIX_VARRAY_MATCHER,
|
429
|
+
VMATRIX_MATCHER,
|
430
|
+
HMATRIX_MATCHER,
|
431
|
+
VARRAY_MATCHER,
|
432
|
+
SINGLE_MATCHER
|
125
433
|
].freeze
|
126
434
|
end
|
127
435
|
|
128
436
|
# parses input data format and generates input definitons
|
129
437
|
module InputFormat
|
438
|
+
extend InputFormatUtils
|
130
439
|
include InputFormatConstants
|
440
|
+
include InputFormatMatcherConstants
|
131
441
|
|
132
442
|
module_function
|
133
443
|
|
134
444
|
def process(pbm)
|
135
445
|
return unless (str = find_fmt(pbm))
|
136
446
|
|
137
|
-
inpdefs = parse(str
|
138
|
-
pbm.
|
447
|
+
inpdefs = parse(str)
|
448
|
+
pbm.formats_src = inpdefs
|
139
449
|
end
|
140
450
|
|
141
451
|
def find_fmt(pbm)
|
142
452
|
str = nil
|
143
453
|
SECTIONS.any? do |key|
|
144
|
-
str = pbm.sections[key]&.code_block_html
|
145
|
-
str && !str.empty?
|
454
|
+
(str = pbm.sections[key]&.code_block_html) && !str.empty?
|
146
455
|
end
|
147
456
|
str
|
148
457
|
end
|
149
458
|
|
150
|
-
def parse(str
|
459
|
+
def parse(str)
|
151
460
|
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)
|
461
|
+
parse_fmt(lines)
|
194
462
|
end
|
195
463
|
|
196
464
|
def parse_fmt(lines)
|
@@ -199,85 +467,18 @@ module AtCoderFriends
|
|
199
467
|
if matcher
|
200
468
|
next if matcher.match2(line)
|
201
469
|
|
202
|
-
ret
|
470
|
+
ret << matcher.to_inpdef
|
203
471
|
end
|
204
472
|
if (matcher = MATCHERS.find { |m| m.match(line) })
|
205
|
-
ret << Problem::InputFormat.new(
|
206
|
-
matcher.container, matcher.item, matcher.names, ''
|
207
|
-
)
|
208
473
|
elsif !line.empty?
|
209
474
|
puts "unknown format: #{line}"
|
210
|
-
|
475
|
+
ret << unknown_fmt(line)
|
211
476
|
end
|
212
477
|
end
|
213
478
|
end
|
214
479
|
|
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
|
480
|
+
def unknown_fmt(line)
|
481
|
+
Problem::InputFormat.new(container: :unknown, item: line)
|
281
482
|
end
|
282
483
|
end
|
283
484
|
end
|