at_coder_friends 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +1 -4
  4. data/Gemfile.lock +1 -1
  5. data/README.md +1 -1
  6. data/config/default.yml +3 -0
  7. data/docs/CONFIGURATION.md +74 -9
  8. data/lib/at_coder_friends.rb +10 -5
  9. data/lib/at_coder_friends/cli.rb +2 -5
  10. data/lib/at_coder_friends/context.rb +4 -0
  11. data/lib/at_coder_friends/emitter.rb +2 -2
  12. data/lib/at_coder_friends/generator/cxx_builtin.rb +191 -0
  13. data/lib/at_coder_friends/generator/main.rb +53 -0
  14. data/lib/at_coder_friends/generator/ruby_builtin.rb +128 -0
  15. data/lib/at_coder_friends/parser/binary.rb +39 -0
  16. data/lib/at_coder_friends/parser/constraints.rb +36 -0
  17. data/lib/at_coder_friends/parser/{format_parser.rb → input_format.rb} +42 -30
  18. data/lib/at_coder_friends/parser/interactive.rb +29 -0
  19. data/lib/at_coder_friends/parser/main.rb +6 -3
  20. data/lib/at_coder_friends/parser/sample_data.rb +24 -0
  21. data/lib/at_coder_friends/parser/section_wrapper.rb +49 -0
  22. data/lib/at_coder_friends/parser/{page_parser.rb → sections.rb} +44 -50
  23. data/lib/at_coder_friends/problem.rb +40 -24
  24. data/lib/at_coder_friends/scraping/agent.rb +1 -5
  25. data/lib/at_coder_friends/scraping/authentication.rb +2 -2
  26. data/lib/at_coder_friends/scraping/session.rb +1 -1
  27. data/lib/at_coder_friends/scraping/tasks.rb +2 -6
  28. data/lib/at_coder_friends/test_runner/base.rb +36 -31
  29. data/lib/at_coder_friends/test_runner/judge.rb +2 -6
  30. data/lib/at_coder_friends/test_runner/sample.rb +8 -6
  31. data/lib/at_coder_friends/version.rb +1 -1
  32. data/tasks/regression/check_diff.rake +29 -0
  33. data/tasks/regression/check_parse.rake +56 -0
  34. data/tasks/regression/regression.rb +67 -0
  35. data/tasks/regression/section_list.rake +41 -0
  36. data/tasks/regression/setup.rake +48 -0
  37. data/templates/cxx_builtin_default.cxx +26 -0
  38. data/templates/cxx_builtin_interactive.cxx +61 -0
  39. data/templates/ruby_builtin_default.rb +5 -0
  40. data/templates/ruby_builtin_interactive.rb +32 -0
  41. metadata +21 -8
  42. data/lib/at_coder_friends/cxx_generator.rb +0 -169
  43. data/lib/at_coder_friends/parser/constraints_parser.rb +0 -26
  44. data/lib/at_coder_friends/ruby_generator.rb +0 -97
  45. 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
@@ -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