at_coder_friends 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -2
  3. data/.rubocop.yml +1 -0
  4. data/.rubocop_todo.yml +4 -1
  5. data/Gemfile.lock +16 -14
  6. data/Rakefile +2 -0
  7. data/at_coder_friends.gemspec +1 -0
  8. data/lib/at_coder_friends.rb +13 -5
  9. data/lib/at_coder_friends/cli.rb +1 -2
  10. data/lib/at_coder_friends/config_loader.rb +2 -2
  11. data/lib/at_coder_friends/context.rb +3 -3
  12. data/lib/at_coder_friends/cxx_generator.rb +7 -13
  13. data/lib/at_coder_friends/emitter.rb +0 -4
  14. data/lib/at_coder_friends/parser/constraints_parser.rb +26 -0
  15. data/lib/at_coder_friends/parser/format_parser.rb +154 -0
  16. data/lib/at_coder_friends/parser/main.rb +16 -0
  17. data/lib/at_coder_friends/parser/page_parser.rb +119 -0
  18. data/lib/at_coder_friends/path_util.rb +10 -0
  19. data/lib/at_coder_friends/problem.rb +11 -14
  20. data/lib/at_coder_friends/scraping/agent.rb +77 -0
  21. data/lib/at_coder_friends/scraping/authentication.rb +71 -0
  22. data/lib/at_coder_friends/scraping/custom_test.rb +53 -0
  23. data/lib/at_coder_friends/scraping/session.rb +26 -0
  24. data/lib/at_coder_friends/scraping/submission.rb +31 -0
  25. data/lib/at_coder_friends/scraping/tasks.rb +39 -0
  26. data/lib/at_coder_friends/test_runner/base.rb +123 -0
  27. data/lib/at_coder_friends/test_runner/judge.rb +44 -0
  28. data/lib/at_coder_friends/test_runner/sample.rb +35 -0
  29. data/lib/at_coder_friends/verifier.rb +4 -2
  30. data/lib/at_coder_friends/version.rb +1 -1
  31. data/tasks/regression.rake +163 -0
  32. metadata +30 -7
  33. data/lib/at_coder_friends/format_parser.rb +0 -151
  34. data/lib/at_coder_friends/judge_test_runner.rb +0 -34
  35. data/lib/at_coder_friends/sample_test_runner.rb +0 -31
  36. data/lib/at_coder_friends/scraping_agent.rb +0 -265
  37. data/lib/at_coder_friends/test_runner.rb +0 -104
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7ea93eb5e8bef9fd743ff3f5767cc906264b878b
4
- data.tar.gz: 6b41067f5105e20859e2a5e5c0d120ef52e29b9a
3
+ metadata.gz: 24142f8aee41542925cca00a32b8bc857c012625
4
+ data.tar.gz: 31511f4cde2547fe046b202981888691d070231a
5
5
  SHA512:
6
- metadata.gz: 963c99df359372cbc89cbe6efb4ccf8c3ffccfc50e68364dc62e7314574a1ad30cfa6e12086cb994a844f5ce71d93a1eb543fe76c50a8cf361b4f18cf7387669
7
- data.tar.gz: 81468b9c45065d8c8de6ef3c6355e400ce56d784c0743ad2d44e50784c5ebc3529ab3d5f4808561feea5ec388d53697a5b5d34030360b85f8cd676627c4a7a3b
6
+ metadata.gz: c6eab79caf8682d95758978e90ee04bbd687a66f302e4ecec3e1dbd73ce8bb2c4b5208411b76c319b992eabc01970e3b5e8c77d6d4fda9f88eb1ff617ff00d06
7
+ data.tar.gz: 87a88e30c96d63452044c56e5dad00981fecbac8a956e24f99b411709dae015b3046ee5be735ca5d707907da8cb6dd38f86eec0e2285ffeee83b03ba26c75fd4
data/.gitignore CHANGED
@@ -11,8 +11,11 @@
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
13
 
14
+ # rvm
15
+ .ruby-version
16
+
14
17
  # at_coder_friends
15
18
  .at_coder_friends.yml
16
19
 
17
- # rvm
18
- .ruby-version
20
+ # regression environment
21
+ /regression
data/.rubocop.yml CHANGED
@@ -3,6 +3,7 @@ inherit_from: .rubocop_todo.yml
3
3
  AllCops:
4
4
  Exclude:
5
5
  - 'spec/fixtures/**/*'
6
+ - 'regression/**/*'
6
7
  TargetRubyVersion: 2.3
7
8
 
8
9
  Lint/AmbiguousBlockAssociation:
data/.rubocop_todo.yml CHANGED
@@ -2,7 +2,10 @@ Metrics/AbcSize:
2
2
  Max: 20
3
3
 
4
4
  Metrics/ClassLength:
5
- Max: 142
5
+ Max: 135
6
+
7
+ Metrics/ModuleLength:
8
+ Max: 120
6
9
 
7
10
  Metrics/MethodLength:
8
11
  Max: 15
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- at_coder_friends (0.5.0)
4
+ at_coder_friends (0.5.1)
5
+ colorize (~> 0.8.1)
5
6
  launchy (~> 2.4.3)
6
7
  mechanize (~> 2.0)
7
8
 
@@ -10,6 +11,7 @@ GEM
10
11
  specs:
11
12
  addressable (2.7.0)
12
13
  public_suffix (>= 2.0.2, < 5.0)
14
+ colorize (0.8.1)
13
15
  connection_pool (2.2.2)
14
16
  crack (0.4.3)
15
17
  safe_yaml (~> 1.0.0)
@@ -32,9 +34,9 @@ GEM
32
34
  nokogiri (~> 1.6)
33
35
  ntlm-http (~> 0.1, >= 0.1.1)
34
36
  webrobots (>= 0.0.9, < 0.2)
35
- mime-types (3.2.2)
37
+ mime-types (3.3)
36
38
  mime-types-data (~> 3.2015)
37
- mime-types-data (3.2019.0331)
39
+ mime-types-data (3.2019.0904)
38
40
  mini_portile2 (2.4.0)
39
41
  net-http-digest_auth (1.4.1)
40
42
  net-http-persistent (3.1.0)
@@ -44,19 +46,19 @@ GEM
44
46
  ntlm-http (0.1.1)
45
47
  public_suffix (4.0.1)
46
48
  rake (13.0.0)
47
- rspec (3.8.0)
48
- rspec-core (~> 3.8.0)
49
- rspec-expectations (~> 3.8.0)
50
- rspec-mocks (~> 3.8.0)
51
- rspec-core (3.8.0)
52
- rspec-support (~> 3.8.0)
53
- rspec-expectations (3.8.2)
49
+ rspec (3.9.0)
50
+ rspec-core (~> 3.9.0)
51
+ rspec-expectations (~> 3.9.0)
52
+ rspec-mocks (~> 3.9.0)
53
+ rspec-core (3.9.0)
54
+ rspec-support (~> 3.9.0)
55
+ rspec-expectations (3.9.0)
54
56
  diff-lcs (>= 1.2.0, < 2.0)
55
- rspec-support (~> 3.8.0)
56
- rspec-mocks (3.8.0)
57
+ rspec-support (~> 3.9.0)
58
+ rspec-mocks (3.9.0)
57
59
  diff-lcs (>= 1.2.0, < 2.0)
58
- rspec-support (~> 3.8.0)
59
- rspec-support (3.8.0)
60
+ rspec-support (~> 3.9.0)
61
+ rspec-support (3.9.0)
60
62
  safe_yaml (1.0.5)
61
63
  simplecov (0.17.1)
62
64
  docile (~> 1.1)
data/Rakefile CHANGED
@@ -5,4 +5,6 @@ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
+ Dir['tasks/**/*.rake'].each { |t| load t }
9
+
8
10
  task default: :spec
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  spec.required_ruby_version = '>= 2.3.0'
32
32
 
33
+ spec.add_dependency 'colorize', '~> 0.8.1'
33
34
  spec.add_dependency 'launchy', '~> 2.4.3'
34
35
  spec.add_dependency 'mechanize', '~> 2.0'
35
36
 
@@ -5,12 +5,20 @@ require 'at_coder_friends/errors'
5
5
  require 'at_coder_friends/path_util'
6
6
  require 'at_coder_friends/config_loader'
7
7
  require 'at_coder_friends/verifier'
8
- require 'at_coder_friends/test_runner'
9
- require 'at_coder_friends/sample_test_runner'
10
- require 'at_coder_friends/judge_test_runner'
8
+ require 'at_coder_friends/test_runner/base'
9
+ require 'at_coder_friends/test_runner/sample'
10
+ require 'at_coder_friends/test_runner/judge'
11
11
  require 'at_coder_friends/problem'
12
- require 'at_coder_friends/scraping_agent'
13
- require 'at_coder_friends/format_parser'
12
+ require 'at_coder_friends/scraping/session'
13
+ require 'at_coder_friends/scraping/authentication'
14
+ require 'at_coder_friends/scraping/custom_test'
15
+ require 'at_coder_friends/scraping/submission'
16
+ require 'at_coder_friends/scraping/tasks'
17
+ require 'at_coder_friends/scraping/agent'
18
+ require 'at_coder_friends/parser/page_parser'
19
+ require 'at_coder_friends/parser/format_parser'
20
+ require 'at_coder_friends/parser/constraints_parser'
21
+ require 'at_coder_friends/parser/main'
14
22
  require 'at_coder_friends/ruby_generator'
15
23
  require 'at_coder_friends/cxx_generator'
16
24
  require 'at_coder_friends/emitter'
@@ -92,11 +92,10 @@ module AtCoderFriends
92
92
  raise AppError, "#{path} is not empty." \
93
93
  if Dir.exist?(path) && !Dir["#{path}/*"].empty?
94
94
 
95
- parser = FormatParser.new
96
95
  rb_gen = RubyGenerator.new
97
96
  cxx_gen = CxxGenerator.new
98
97
  ctx.scraping_agent.fetch_all do |pbm|
99
- parser.process(pbm)
98
+ Parser::Main.process(pbm)
100
99
  rb_gen.process(pbm)
101
100
  cxx_gen.process(pbm)
102
101
  ctx.emitter.emit(pbm)
@@ -43,7 +43,7 @@ module AtCoderFriends
43
43
  end
44
44
 
45
45
  def merge(base_hash, derived_hash)
46
- res = base_hash.merge(derived_hash || {}) do |_, base_val, derived_val|
46
+ res = base_hash.merge(derived_hash) do |_, base_val, derived_val|
47
47
  if base_val.is_a?(Hash) && derived_val.is_a?(Hash)
48
48
  merge(base_val, derived_val)
49
49
  else
@@ -55,7 +55,7 @@ module AtCoderFriends
55
55
 
56
56
  def load_yaml(path)
57
57
  yaml = IO.read(path, encoding: Encoding::UTF_8)
58
- YAML.safe_load(yaml, [], [], false, path)
58
+ YAML.safe_load(yaml, [], [], false, path) || {}
59
59
  rescue Errno::ENOENT
60
60
  raise ConfigNotFoundError,
61
61
  "Configuration file not found: #{path}"
@@ -19,15 +19,15 @@ module AtCoderFriends
19
19
  end
20
20
 
21
21
  def scraping_agent
22
- @scraping_agent ||= ScrapingAgent.new(self)
22
+ @scraping_agent ||= Scraping::Agent.new(self)
23
23
  end
24
24
 
25
25
  def sample_test_runner
26
- @sample_test_runner ||= SampleTestRunner.new(self)
26
+ @sample_test_runner ||= TestRunner::Sample.new(self)
27
27
  end
28
28
 
29
29
  def judge_test_runner
30
- @judge_test_runner ||= JudgeTestRunner.new(self)
30
+ @judge_test_runner ||= TestRunner::Judge.new(self)
31
31
  end
32
32
 
33
33
  def verifier
@@ -61,12 +61,12 @@ module AtCoderFriends
61
61
  }.freeze
62
62
 
63
63
  def process(pbm)
64
- src = generate(pbm.defs, pbm.desc)
64
+ src = generate(pbm.defs, pbm.constraints)
65
65
  pbm.add_src(:cxx, src)
66
66
  end
67
67
 
68
- def generate(defs, desc)
69
- consts = gen_consts(desc)
68
+ def generate(defs, constraints)
69
+ consts = gen_consts(constraints)
70
70
  dcls = gen_decls(defs)
71
71
  reads = gen_reads(defs)
72
72
  TEMPLATE
@@ -75,16 +75,10 @@ module AtCoderFriends
75
75
  .sub('/*** READS ***/', reads.map { |s| ' ' + s }.join("\n"))
76
76
  end
77
77
 
78
- def gen_consts(desc)
79
- desc
80
- .gsub(/[,\\\(\)\{\}\|]/, '')
81
- .gsub(/(≤|leq)/i, '≦')
82
- .scan(/([\da-z_]+)\s*≦\s*(\d+)(?:\^(\d+))?/i)
83
- .map do |v, sz, k|
84
- sz = sz.to_i
85
- sz **= k.to_i if k
86
- "const int #{v.upcase}_MAX = #{sz};"
87
- end
78
+ def gen_consts(constraints)
79
+ constraints
80
+ .select { |c| c.type == :max }
81
+ .map { |c| "const int #{c.name.upcase}_MAX = #{c.value};" }
88
82
  end
89
83
 
90
84
  def gen_decls(defs)
@@ -35,9 +35,5 @@ module AtCoderFriends
35
35
  File.write(src_path, src.txt)
36
36
  puts src_file
37
37
  end
38
-
39
- def makedirs_unless(dir)
40
- FileUtils.makedirs(dir) unless Dir.exist?(dir)
41
- end
42
38
  end
43
39
  end
@@ -0,0 +1,26 @@
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
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ module Parser
5
+ # parses input data format and generates input definitons
6
+ module FormatParser
7
+ module_function
8
+
9
+ # Iterates through elements of an array
10
+ class Iterator
11
+ def initialize(array)
12
+ @array = array
13
+ @i = 0
14
+ end
15
+
16
+ def next?
17
+ @i < @array.size
18
+ end
19
+
20
+ def next
21
+ ret = @array[@i]
22
+ @i += 1
23
+ ret
24
+ end
25
+ end
26
+
27
+ PARSERS = [
28
+ {
29
+ container: :harray,
30
+ item: :number,
31
+ pat: /^(?<v>[a-z]+)[01](\s+\k<v>.)*(\s+\.+)?(\s+\k<v>.)+$/i,
32
+ names: ->(m) { [m[:v]] },
33
+ pat2: ->(_) { nil },
34
+ size: ->(f) { [f[-1]] }
35
+ },
36
+ {
37
+ container: :harray,
38
+ item: :char,
39
+ pat: /^(?<v>[a-z]+)[01](\k<v>.)*(\s*\.+\s*)?(\k<v>.)+$/i,
40
+ names: ->(m) { [m[:v]] },
41
+ pat2: ->(_) { nil },
42
+ size: ->(f) { [f[-1]] }
43
+ },
44
+ {
45
+ container: :matrix,
46
+ item: :number,
47
+ pat: /^(?<v>[a-z]+)[01][01](\s+\k<v>..)*(\s+\.+)?(\s+\k<v>..)+$/i,
48
+ names: ->(m) { [m[:v]] },
49
+ pat2: ->(v) { /(^#{v}..(\s+#{v}..)*(\s+\.+)?(\s+#{v}..)+|\.+)$/ },
50
+ size: ->(f) { f[-2..-1].chars.to_a }
51
+ },
52
+ {
53
+ container: :matrix,
54
+ item: :char,
55
+ pat: /^(?<v>[a-z]+)[01][01](\k<v>..)*(\s*\.+\s*)?(\k<v>..)+$/i,
56
+ names: ->(m) { [m[:v]] },
57
+ pat2: ->(v) { /(^#{v}..(#{v}..)*(\s*\.+\s*)?(#{v}..)+|\.+)$/ },
58
+ size: ->(f) { f[-2..-1].chars.to_a }
59
+ },
60
+ {
61
+ container: :varray,
62
+ item: :number,
63
+ pat: /^[a-z]+(?<i>[0-9])(\s+[a-z]+\k<i>)*$/i,
64
+ names: ->(m) { m[0].split.map { |w| w[0..-2] } },
65
+ pat2: lambda { |vs|
66
+ pat = vs.map { |v| v + '.+' }.join('\s+')
67
+ /^(#{pat}|\.+)$/
68
+ },
69
+ size: ->(f) { /(?<sz>\d+)$/ =~ f ? [sz] : [f[-1]] }
70
+ },
71
+ {
72
+ container: :single,
73
+ item: :number,
74
+ pat: /^[a-z]+(\s+[a-z]+)*$/i,
75
+ names: ->(m) { m[0].split },
76
+ pat2: ->(_) { nil },
77
+ size: ->(_) { [] }
78
+ }
79
+ ].freeze
80
+
81
+ def process(pbm)
82
+ defs = parse(pbm.fmt, pbm.smps)
83
+ pbm.defs = defs
84
+ end
85
+
86
+ def parse(fmt, smps)
87
+ lines = normalize(fmt)
88
+ defs = parse_fmt(lines)
89
+ smpx = max_smp(smps)
90
+ smpx && match_smp!(defs, smpx)
91
+ defs
92
+ end
93
+
94
+ def normalize(fmt)
95
+ fmt
96
+ .gsub(/[+*-]\d+/, '') # N-1, N+1 -> N
97
+ .gsub(%r{[-/ ]}, ' ') # a-b, a/b -> a b
98
+ .gsub(/\{.*?\}/) { |w| w.delete(' ') } # {1, 1}->{1,1} shortest match
99
+ .gsub(/[_,'\\(){}|$]/, '')
100
+ .gsub(/[・::…‥]+/, '..')
101
+ .gsub(/[clv]?dots/, '..')
102
+ .gsub(/^[.\s]+$/, '..')
103
+ .split("\n")
104
+ .map(&:strip)
105
+ end
106
+
107
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
108
+ def parse_fmt(lines)
109
+ it = Iterator.new(lines + ['']) # sentinel
110
+ prv = nil
111
+ cur = it.next
112
+ Enumerator.new do |y|
113
+ loop do
114
+ unless (parser = PARSERS.find { |ps| ps[:pat] =~ cur })
115
+ puts "unknown format: #{cur}" unless cur.empty?
116
+ (cur = it.next) ? next : break
117
+ end
118
+ container, item = parser.values_at(:container, :item)
119
+ m = parser[:pat].match(cur)
120
+ names = parser[:names].call(m)
121
+ pat2 = parser[:pat2].call(names)
122
+ loop do
123
+ prv = cur
124
+ cur = it.next
125
+ break unless pat2 && pat2 =~ cur
126
+ end
127
+ size = parser[:size].call(prv)
128
+ y << InputDef.new(container, item, names, size)
129
+ end
130
+ end.to_a
131
+ end
132
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
133
+
134
+ def max_smp(smps)
135
+ smps
136
+ .select { |smp| smp.ext == :in }
137
+ .max_by { |smp| smp.txt.size }
138
+ &.txt
139
+ end
140
+
141
+ def match_smp!(inpdefs, smp)
142
+ lines = smp.split("\n")
143
+ inpdefs.each_with_index do |inpdef, i|
144
+ break if i >= lines.size
145
+ next if inpdef.item != :number
146
+
147
+ inpdef.item = :string if lines[i].split[0] =~ /[^\-0-9]/
148
+ break if %i[varray matrix].include?(inpdef.container)
149
+ end
150
+ inpdefs
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ module Parser
5
+ # entry point for parsing problem description
6
+ module Main
7
+ module_function
8
+
9
+ def process(pbm)
10
+ PageParser.process(pbm)
11
+ FormatParser.process(pbm)
12
+ ConstraintsParser.process(pbm)
13
+ end
14
+ end
15
+ end
16
+ end