at_coder_friends 0.5.2 → 0.6.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +1 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/config/default.yml +3 -0
- data/docs/CONFIGURATION.md +74 -9
- data/lib/at_coder_friends.rb +10 -5
- data/lib/at_coder_friends/cli.rb +2 -5
- data/lib/at_coder_friends/context.rb +4 -0
- data/lib/at_coder_friends/emitter.rb +2 -2
- data/lib/at_coder_friends/generator/cxx_builtin.rb +191 -0
- data/lib/at_coder_friends/generator/main.rb +53 -0
- data/lib/at_coder_friends/generator/ruby_builtin.rb +128 -0
- data/lib/at_coder_friends/parser/binary.rb +39 -0
- data/lib/at_coder_friends/parser/constraints.rb +36 -0
- data/lib/at_coder_friends/parser/{format_parser.rb → input_format.rb} +42 -30
- data/lib/at_coder_friends/parser/interactive.rb +29 -0
- data/lib/at_coder_friends/parser/main.rb +6 -3
- data/lib/at_coder_friends/parser/sample_data.rb +24 -0
- data/lib/at_coder_friends/parser/section_wrapper.rb +49 -0
- data/lib/at_coder_friends/parser/{page_parser.rb → sections.rb} +44 -50
- data/lib/at_coder_friends/problem.rb +40 -24
- data/lib/at_coder_friends/scraping/agent.rb +1 -5
- data/lib/at_coder_friends/scraping/authentication.rb +2 -2
- data/lib/at_coder_friends/scraping/session.rb +1 -1
- data/lib/at_coder_friends/scraping/tasks.rb +2 -6
- data/lib/at_coder_friends/test_runner/base.rb +36 -31
- data/lib/at_coder_friends/test_runner/judge.rb +2 -6
- data/lib/at_coder_friends/test_runner/sample.rb +8 -6
- data/lib/at_coder_friends/version.rb +1 -1
- data/tasks/regression/check_diff.rake +29 -0
- data/tasks/regression/check_parse.rake +56 -0
- data/tasks/regression/regression.rb +67 -0
- data/tasks/regression/section_list.rake +41 -0
- data/tasks/regression/setup.rake +48 -0
- data/templates/cxx_builtin_default.cxx +26 -0
- data/templates/cxx_builtin_interactive.cxx +61 -0
- data/templates/ruby_builtin_default.rb +5 -0
- data/templates/ruby_builtin_interactive.rb +32 -0
- metadata +21 -8
- data/lib/at_coder_friends/cxx_generator.rb +0 -169
- data/lib/at_coder_friends/parser/constraints_parser.rb +0 -26
- data/lib/at_coder_friends/ruby_generator.rb +0 -97
- data/tasks/regression.rake +0 -163
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AtCoderFriends
|
4
|
-
module Parser
|
5
|
-
# parses constraints
|
6
|
-
module ConstraintsParser
|
7
|
-
module_function
|
8
|
-
|
9
|
-
def process(pbm)
|
10
|
-
pbm.constraints = parse(pbm.desc)
|
11
|
-
end
|
12
|
-
|
13
|
-
def parse(desc)
|
14
|
-
desc
|
15
|
-
.gsub(/[,\\(){}|]/, '')
|
16
|
-
.gsub(/(≤|leq?)/i, '≦')
|
17
|
-
.scan(/([\da-z_]+)\s*≦\s*(\d+)(?:\^(\d+))?/i)
|
18
|
-
.map do |v, sz, k|
|
19
|
-
sz = sz.to_i
|
20
|
-
sz **= k.to_i if k
|
21
|
-
Constraint.new(v, :max, sz)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AtCoderFriends
|
4
|
-
# generates C++ source code from definition
|
5
|
-
class RubyGenerator
|
6
|
-
TEMPLATE = <<~TEXT
|
7
|
-
### DCLS ###
|
8
|
-
|
9
|
-
puts ans
|
10
|
-
TEXT
|
11
|
-
|
12
|
-
def process(pbm)
|
13
|
-
src = generate(pbm.defs)
|
14
|
-
pbm.add_src(:rb, src)
|
15
|
-
end
|
16
|
-
|
17
|
-
def generate(defs)
|
18
|
-
dcls = gen_decls(defs).join("\n")
|
19
|
-
TEMPLATE.sub('### DCLS ###', dcls)
|
20
|
-
end
|
21
|
-
|
22
|
-
def gen_decls(defs)
|
23
|
-
defs.map { |inpdef| gen_decl(inpdef) }.flatten
|
24
|
-
end
|
25
|
-
|
26
|
-
def gen_decl(inpdef)
|
27
|
-
case inpdef.container
|
28
|
-
when :single
|
29
|
-
gen_single_decl(inpdef)
|
30
|
-
when :harray
|
31
|
-
gen_harray_decl(inpdef)
|
32
|
-
when :varray
|
33
|
-
if inpdef.names.size == 1
|
34
|
-
gen_varray_1_decl(inpdef)
|
35
|
-
else
|
36
|
-
gen_varray_n_decl(inpdef)
|
37
|
-
end
|
38
|
-
when :matrix
|
39
|
-
gen_matrix_decl(inpdef)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def gen_single_decl(inpdef)
|
44
|
-
names = inpdef.names
|
45
|
-
dcl = names.join(', ')
|
46
|
-
expr = gen_expr(inpdef.item, names.size > 1)
|
47
|
-
"#{dcl} = #{expr}"
|
48
|
-
end
|
49
|
-
|
50
|
-
def gen_harray_decl(inpdef)
|
51
|
-
v = inpdef.names[0]
|
52
|
-
dcl = "#{v}s"
|
53
|
-
expr = gen_expr(inpdef.item, true)
|
54
|
-
"#{dcl} = #{expr}"
|
55
|
-
end
|
56
|
-
|
57
|
-
def gen_varray_1_decl(inpdef)
|
58
|
-
v = inpdef.names[0]
|
59
|
-
sz = inpdef.size[0]
|
60
|
-
dcl = "#{v}s"
|
61
|
-
expr = gen_expr(inpdef.item, false)
|
62
|
-
"#{dcl} = Array.new(#{sz}) { #{expr} }"
|
63
|
-
end
|
64
|
-
|
65
|
-
def gen_varray_n_decl(inpdef)
|
66
|
-
names = inpdef.names
|
67
|
-
sz = inpdef.size[0]
|
68
|
-
dcl = names.map { |v| "#{v}s[i]" }.join(', ')
|
69
|
-
expr = gen_expr(inpdef.item, true)
|
70
|
-
ret = []
|
71
|
-
ret += names.map { |v| "#{v}s = Array.new(#{sz})" }
|
72
|
-
ret << "#{sz}.times do |i|"
|
73
|
-
ret << " #{dcl} = #{expr}"
|
74
|
-
ret << 'end'
|
75
|
-
ret
|
76
|
-
end
|
77
|
-
|
78
|
-
def gen_matrix_decl(inpdef)
|
79
|
-
v = inpdef.names[0]
|
80
|
-
sz = inpdef.size[0]
|
81
|
-
decl = "#{v}ss"
|
82
|
-
expr = gen_expr(inpdef.item, true)
|
83
|
-
"#{decl} = Array.new(#{sz}) { #{expr} }"
|
84
|
-
end
|
85
|
-
|
86
|
-
def gen_expr(item, split)
|
87
|
-
case item
|
88
|
-
when :number
|
89
|
-
split ? 'gets.split.map(&:to_i)' : 'gets.to_i'
|
90
|
-
when :string
|
91
|
-
split ? 'gets.chomp.split' : 'gets.chomp'
|
92
|
-
when :char
|
93
|
-
'gets.chomp'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
data/tasks/regression.rake
DELETED
@@ -1,163 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require 'uri'
|
5
|
-
require 'json'
|
6
|
-
require 'mechanize'
|
7
|
-
require 'at_coder_friends'
|
8
|
-
|
9
|
-
module AtCoderFriends
|
10
|
-
# tasks for regression test
|
11
|
-
module Regression
|
12
|
-
module_function
|
13
|
-
|
14
|
-
CONTEST_LIST_URL = 'https://kenkoooo.com/atcoder/resources/contests.json'
|
15
|
-
ACF_HOME = File.realpath(File.join(__dir__, '..'))
|
16
|
-
REGRESSION_HOME = File.join(ACF_HOME, 'regression')
|
17
|
-
PAGES_DIR = File.join(REGRESSION_HOME, 'pages')
|
18
|
-
EMIT_ORG_DIR = File.join(REGRESSION_HOME, 'emit_org')
|
19
|
-
EMIT_DIR_FMT = File.join(REGRESSION_HOME, 'emit_%<now>s')
|
20
|
-
|
21
|
-
def setup
|
22
|
-
rmdir_force(PAGES_DIR)
|
23
|
-
rmdir_force(EMIT_ORG_DIR)
|
24
|
-
contest_id_list.each do |contest|
|
25
|
-
begin
|
26
|
-
ctx = context(EMIT_ORG_DIR, contest)
|
27
|
-
ctx.scraping_agent.fetch_all do |pbm|
|
28
|
-
begin
|
29
|
-
html_path = File.join(PAGES_DIR, contest, "#{pbm.q}.html")
|
30
|
-
save_file(html_path, pbm.page.body)
|
31
|
-
pipeline(ctx, pbm)
|
32
|
-
rescue StandardError => e
|
33
|
-
p e
|
34
|
-
end
|
35
|
-
end
|
36
|
-
rescue StandardError => e
|
37
|
-
p e
|
38
|
-
end
|
39
|
-
sleep 3
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_diff
|
44
|
-
emit_dir = format(EMIT_DIR_FMT, now: Time.now.strftime('%Y%m%d%H%M%S'))
|
45
|
-
rmdir_force(emit_dir)
|
46
|
-
|
47
|
-
local_pbm_list.each do |contest, q, url|
|
48
|
-
ctx = context(emit_dir, contest)
|
49
|
-
pbm = ctx.scraping_agent.fetch_problem(q, url)
|
50
|
-
pipeline(ctx, pbm)
|
51
|
-
end
|
52
|
-
|
53
|
-
system("diff -r #{EMIT_ORG_DIR} #{emit_dir}")
|
54
|
-
end
|
55
|
-
|
56
|
-
def section_list
|
57
|
-
agent = Mechanize.new
|
58
|
-
list = local_pbm_list.flat_map do |contest, q, url|
|
59
|
-
page = agent.get(url)
|
60
|
-
%w[h2 h3].flat_map do
|
61
|
-
page.search('h3').map do |h3|
|
62
|
-
{ contest: contest, q: q, text: normalize(h3.content) }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
list.group_by { |sec| sec[:text] }.each do |k, vs|
|
67
|
-
puts [k, vs.size, vs[0][:contest], vs[0][:q]].join("\t")
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def check_parse
|
72
|
-
parse_list
|
73
|
-
.select { |_, _, no_fmt, no_smp| no_fmt || no_smp }
|
74
|
-
.map { |c, q, no_fmt, no_smp| [c, q, f_to_s(no_fmt), f_to_s(no_smp)] }
|
75
|
-
.sort
|
76
|
-
.each { |args| puts args.join("\t") }
|
77
|
-
end
|
78
|
-
|
79
|
-
def parse_list
|
80
|
-
local_pbm_list.map do |contest, q, url|
|
81
|
-
ctx = context('', contest)
|
82
|
-
pbm = ctx.scraping_agent.fetch_problem(q, url)
|
83
|
-
Parser::Main.process(pbm)
|
84
|
-
no_fmt = pbm.fmt.empty?
|
85
|
-
no_smp = pbm.smps.all? { |smp| smp.txt.empty? }
|
86
|
-
[contest, q, no_fmt, no_smp]
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def f_to_s(f)
|
91
|
-
f ? '◯' : '-'
|
92
|
-
end
|
93
|
-
|
94
|
-
def contest_id_list
|
95
|
-
uri = URI.parse(CONTEST_LIST_URL)
|
96
|
-
json = Net::HTTP.get(uri)
|
97
|
-
contests = JSON.parse(json)
|
98
|
-
puts "Total #{contests.size} contests"
|
99
|
-
contests.map { |h| h['id'] }
|
100
|
-
end
|
101
|
-
|
102
|
-
def local_pbm_list
|
103
|
-
Dir.glob(PAGES_DIR + '/**/*.html').map do |pbm_path|
|
104
|
-
contest = File.basename(File.dirname(pbm_path))
|
105
|
-
q = File.basename(pbm_path, '.html')
|
106
|
-
url = "file://#{pbm_path}"
|
107
|
-
[contest, q, url]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def context(root, contest)
|
112
|
-
Context.new({}, File.join(root, contest))
|
113
|
-
end
|
114
|
-
|
115
|
-
def rmdir_force(dir)
|
116
|
-
FileUtils.rm_r(dir) if Dir.exist?(dir)
|
117
|
-
end
|
118
|
-
|
119
|
-
def save_file(path, content)
|
120
|
-
dir = File.dirname(path)
|
121
|
-
FileUtils.makedirs(dir) unless Dir.exist?(dir)
|
122
|
-
File.binwrite(path, content)
|
123
|
-
end
|
124
|
-
|
125
|
-
def pipeline(ctx, pbm)
|
126
|
-
@rb_gen ||= RubyGenerator.new
|
127
|
-
@cxx_gen ||= CxxGenerator.new
|
128
|
-
Parser::Main.process(pbm)
|
129
|
-
@rb_gen.process(pbm)
|
130
|
-
@cxx_gen.process(pbm)
|
131
|
-
ctx.emitter.emit(pbm)
|
132
|
-
end
|
133
|
-
|
134
|
-
def normalize(s)
|
135
|
-
s
|
136
|
-
.tr(' 0-9A-Za-z', ' 0-9A-Za-z')
|
137
|
-
.gsub(/[^一-龠_ぁ-ん_ァ-ヶーa-zA-Z0-9 ]/, '')
|
138
|
-
.strip
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
namespace :regression do
|
144
|
-
desc 'setup regression environment'
|
145
|
-
task :setup do
|
146
|
-
AtCoderFriends::Regression.setup
|
147
|
-
end
|
148
|
-
|
149
|
-
desc 'run regression check'
|
150
|
-
task :check_diff do
|
151
|
-
AtCoderFriends::Regression.check_diff
|
152
|
-
end
|
153
|
-
|
154
|
-
desc 'list all section titles'
|
155
|
-
task :section_list do
|
156
|
-
AtCoderFriends::Regression.section_list
|
157
|
-
end
|
158
|
-
|
159
|
-
desc 'checks page parse result'
|
160
|
-
task :check_parse do
|
161
|
-
AtCoderFriends::Regression.check_parse
|
162
|
-
end
|
163
|
-
end
|