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