at_coder_friends 0.5.0 → 0.5.1

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