at_coder_friends 0.6.3 → 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +3 -2
  3. data/.travis.yml +1 -3
  4. data/CHANGELOG.md +37 -0
  5. data/Gemfile.lock +53 -48
  6. data/README.md +1 -1
  7. data/at_coder_friends.gemspec +5 -7
  8. data/config/default.yml +146 -72
  9. data/docs/CONFIGURATION.md +222 -136
  10. data/lib/at_coder_friends.rb +1 -0
  11. data/lib/at_coder_friends/cli.rb +8 -0
  12. data/lib/at_coder_friends/config_loader.rb +11 -3
  13. data/lib/at_coder_friends/context.rb +10 -6
  14. data/lib/at_coder_friends/emitter.rb +2 -2
  15. data/lib/at_coder_friends/generator/base.rb +3 -2
  16. data/lib/at_coder_friends/generator/main.rb +2 -1
  17. data/lib/at_coder_friends/parser/constraints.rb +1 -0
  18. data/lib/at_coder_friends/parser/input_format.rb +2 -0
  19. data/lib/at_coder_friends/parser/input_type.rb +3 -2
  20. data/lib/at_coder_friends/parser/modulo.rb +1 -1
  21. data/lib/at_coder_friends/parser/sections.rb +3 -3
  22. data/lib/at_coder_friends/path_info.rb +51 -0
  23. data/lib/at_coder_friends/path_util.rb +0 -31
  24. data/lib/at_coder_friends/scraping/agent.rb +11 -2
  25. data/lib/at_coder_friends/scraping/custom_test.rb +5 -6
  26. data/lib/at_coder_friends/scraping/submission.rb +6 -7
  27. data/lib/at_coder_friends/scraping/tasks.rb +8 -3
  28. data/lib/at_coder_friends/test_runner/base.rb +17 -4
  29. data/lib/at_coder_friends/test_runner/judge.rb +7 -9
  30. data/lib/at_coder_friends/test_runner/sample.rb +2 -4
  31. data/lib/at_coder_friends/verifier.rb +2 -3
  32. data/lib/at_coder_friends/version.rb +1 -1
  33. data/templates/cxx_builtin.cxx.erb +26 -35
  34. data/templates/ruby_builtin.rb.erb +17 -18
  35. metadata +7 -16
  36. data/tasks/regression/check_const.rake +0 -137
  37. data/tasks/regression/check_diff.rake +0 -30
  38. data/tasks/regression/check_fmt.rake +0 -45
  39. data/tasks/regression/check_parse.rake +0 -69
  40. data/tasks/regression/list_handler.rb +0 -46
  41. data/tasks/regression/regression.rb +0 -38
  42. data/tasks/regression/report_handler.rb +0 -20
  43. data/tasks/regression/section_list.rake +0 -53
  44. data/tasks/regression/setup.rake +0 -48
@@ -7,11 +7,15 @@ module AtCoderFriends
7
7
  # - configuration
8
8
  # - application modules
9
9
  class Context
10
- attr_reader :options, :path
10
+ attr_reader :options, :path_info
11
11
 
12
12
  def initialize(options, path)
13
13
  @options = options
14
- @path = File.expand_path(path)
14
+ @path_info = PathInfo.new(File.expand_path(path))
15
+ end
16
+
17
+ def path
18
+ path_info.path
15
19
  end
16
20
 
17
21
  def config
@@ -26,6 +30,10 @@ module AtCoderFriends
26
30
  @generator ||= Generator::Main.new(self)
27
31
  end
28
32
 
33
+ def emitter
34
+ @emitter ||= Emitter.new(self)
35
+ end
36
+
29
37
  def sample_test_runner
30
38
  @sample_test_runner ||= TestRunner::Sample.new(self)
31
39
  end
@@ -38,10 +46,6 @@ module AtCoderFriends
38
46
  @verifier ||= Verifier.new(self)
39
47
  end
40
48
 
41
- def emitter
42
- @emitter ||= Emitter.new(self)
43
- end
44
-
45
49
  def post_process
46
50
  @scraping_agent&.save_session
47
51
  end
@@ -9,8 +9,8 @@ module AtCoderFriends
9
9
  include PathUtil
10
10
 
11
11
  def initialize(ctx)
12
- @src_dir = ctx.path
13
- @smp_dir = smp_dir(@src_dir)
12
+ @src_dir = ctx.path_info.src_dir
13
+ @smp_dir = ctx.path_info.smp_dir
14
14
  end
15
15
 
16
16
  def emit(pbm)
@@ -21,8 +21,9 @@ module AtCoderFriends
21
21
  def generate(pbm)
22
22
  @pbm = pbm
23
23
  src = File.read(select_template)
24
- src = ERB.new(src).result(binding)
25
- render(src)
24
+ src = ERB.new(src, safe_level = nil, trim_mode = '-').result(binding)
25
+ src = render(src) if respond_to?(:render)
26
+ src
26
27
  end
27
28
 
28
29
  def select_template
@@ -19,7 +19,8 @@ module AtCoderFriends
19
19
  gen_obj.process(pbm)
20
20
  rescue StandardError => e
21
21
  puts "an error occurred in generator:#{gen_name}."
22
- p e
22
+ puts e.to_s
23
+ puts e.backtrace
23
24
  end
24
25
  end
25
26
  end
@@ -64,6 +64,7 @@ module AtCoderFriends
64
64
  .gsub('\\lvert', '|')
65
65
  .gsub('\\rvert', '|')
66
66
  .gsub('\\mathit', '')
67
+ .gsub('\\mathrm', '')
67
68
  .gsub('\\times', '*')
68
69
  .gsub(/\\begin(\{[^{}]*\})*/, '')
69
70
  .gsub(/\\end(\{[^{}]*\})*/, '')
@@ -56,11 +56,13 @@ module AtCoderFriends
56
56
  .gsub('>', '>')
57
57
  .gsub('&lt;', '<')
58
58
  .gsub('\\ ', ' ')
59
+ .gsub(/\\hspace\{\d+pt\}/, ' ')
59
60
  .gsub('\\(', '')
60
61
  .gsub('\\)', '')
61
62
  .gsub('\\lvert', '|')
62
63
  .gsub('\\rvert', '|')
63
64
  .gsub('\\mathit', '')
65
+ .gsub('\\mathrm', '')
64
66
  .gsub('\\times', '*')
65
67
  .gsub(/\\begin(\{[^{}]*\})*/, '')
66
68
  .gsub(/\\end(\{[^{}]*\})*/, '')
@@ -6,10 +6,11 @@ module AtCoderFriends
6
6
  module InputType
7
7
  module_function
8
8
 
9
- NUMBER_PAT = /\A[+-]?[0-9]+\z/.freeze
9
+ NUMBER_PAT = /\A[+-]?[0-9]{1,19}\z/.freeze
10
+ DECIMAL_PAT = /\A[+-]?[0-9]{1,19}(\.[0-9]+)?\z/.freeze
10
11
  TYPE_TBL = [
11
12
  [:number, NUMBER_PAT],
12
- [:decimal, /\A[+-]?[0-9]+(\.[0-9]+)?\z/]
13
+ [:decimal, DECIMAL_PAT]
13
14
  ].freeze
14
15
 
15
16
  def process(pbm)
@@ -21,7 +21,7 @@ module AtCoderFriends
21
21
  |\\\(([^()]+)\\\)
22
22
  |\$([^$]+)\$
23
23
  |\{([^{}]+)\}
24
- |([\d,]+)
24
+ |([\d,^+]+)
25
25
  |([一二三四五六七八九十百千万億]+)
26
26
  )
27
27
  }x.freeze
@@ -63,7 +63,7 @@ module AtCoderFriends
63
63
  {
64
64
  key: Problem::SECTION_IN_SMP,
65
65
  pattern: /
66
- \A(
66
+ \A(?:
67
67
  入力例\s*(?<no>\d+)?
68
68
  |入力\s*(?<no>\d+)
69
69
  |Sample\s*Input\s*(?<no>\d+)?
@@ -75,14 +75,14 @@ module AtCoderFriends
75
75
  {
76
76
  key: Problem::SECTION_OUT_SMP,
77
77
  pattern: /
78
- \A(
78
+ \A(?:
79
79
  出力例\s*(?<no>\d+)?
80
80
  |出力\s*(?<no>\d+)
81
81
  |入力例\s*(?<no>\d+)?\s*に対する出力例
82
82
  |Sample\s*Output\s*(?<no>\d+)?
83
83
  |Output\s*Example\s*(?<no>\d+)?
84
84
  |Output\s*(?<no>\d+)
85
- |Output\s*for\s*(the)?\s*Sample\s*Input\s*(?<no>\d+)?
85
+ |Output\s*for\s*(?:the)?\s*Sample\s*Input\s*(?<no>\d+)?
86
86
  )\z
87
87
  /xi
88
88
  },
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ # holds target path information
5
+ class PathInfo
6
+ SMP_DIR = 'data'
7
+ CASES_DIR = 'cases'
8
+ TMP_DIR = '.tmp'
9
+
10
+ attr_reader :path, :dir
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ # in setup command, path is directory name (existent/non-existent)
15
+ # in other commands(test, submit, verify), path is existent file name
16
+ @dir = File.file?(path) ? File.dirname(path) : path
17
+ end
18
+
19
+ def contest_name
20
+ File.basename(dir).delete('#').downcase
21
+ end
22
+
23
+ def components
24
+ # overwrites @dir here for non-existent files (test purpose)
25
+ @dir, prg = File.split(path)
26
+ base, ext = prg.split('.')
27
+ q = base.gsub(/_[^#_]+\z/, '')
28
+ [path, dir, prg, base, ext, q]
29
+ end
30
+
31
+ def src_dir
32
+ dir
33
+ end
34
+
35
+ def smp_dir
36
+ File.join(dir, SMP_DIR)
37
+ end
38
+
39
+ def cases_dir
40
+ File.join(dir, CASES_DIR)
41
+ end
42
+
43
+ def cases_out_dir
44
+ File.join(dir, TMP_DIR, CASES_DIR)
45
+ end
46
+
47
+ def tmp_dir
48
+ File.join(dir, TMP_DIR)
49
+ end
50
+ end
51
+ end
@@ -3,37 +3,6 @@
3
3
  module AtCoderFriends
4
4
  # Common methods and behaviors for dealing with paths.
5
5
  module PathUtil
6
- module_function
7
-
8
- SMP_DIR = 'data'
9
- CASES_DIR = 'cases'
10
- TMP_DIR = '.tmp'
11
-
12
- def contest_name(path)
13
- dir = File.file?(path) ? File.dirname(path) : path
14
- File.basename(dir).delete('#').downcase
15
- end
16
-
17
- def split_prg_path(path)
18
- dir, prg = File.split(path)
19
- base, ext = prg.split('.')
20
- q = base.split('_')[0]
21
- [path, dir, prg, base, ext, q]
22
- end
23
-
24
- def smp_dir(dir)
25
- File.join(dir, SMP_DIR)
26
- end
27
-
28
- def cases_dir(dir)
29
- File.join(dir, CASES_DIR)
30
- end
31
-
32
- def tmp_dir(path)
33
- dir = File.dirname(path)
34
- File.join(dir, '.tmp')
35
- end
36
-
37
6
  def makedirs_unless(dir)
38
7
  FileUtils.makedirs(dir) unless Dir.exist?(dir)
39
8
  end
@@ -7,7 +7,6 @@ module AtCoderFriends
7
7
  module Scraping
8
8
  # common functions for scraping
9
9
  class Agent
10
- include AtCoderFriends::PathUtil
11
10
  include Session
12
11
  include Authentication
13
12
  include Tasks
@@ -29,7 +28,7 @@ module AtCoderFriends
29
28
  end
30
29
 
31
30
  def contest
32
- @contest ||= contest_name(ctx.path)
31
+ @contest ||= ctx.path_info.contest_name
33
32
  end
34
33
 
35
34
  def common_url(path)
@@ -41,6 +40,10 @@ module AtCoderFriends
41
40
  end
42
41
 
43
42
  def lang_id(ext)
43
+ [lang_id_conf(ext)].flatten
44
+ end
45
+
46
+ def lang_id_conf(ext)
44
47
  ctx.config.dig('ext_settings', ext, 'submit_lang') || (
45
48
  msg = <<~MSG
46
49
  submit_lang for .#{ext} is not specified.
@@ -51,6 +54,12 @@ module AtCoderFriends
51
54
  )
52
55
  end
53
56
 
57
+ def find_lang(page, langs)
58
+ langs.find do |lng|
59
+ page.search("div#select-lang select option[value=#{lng}]")[0]
60
+ end || langs[0]
61
+ end
62
+
54
63
  def lang_list_txt
55
64
  lang_list
56
65
  &.map { |opt| "#{opt[:v]} - #{opt[:t]}" }
@@ -6,20 +6,19 @@ module AtCoderFriends
6
6
  module Scraping
7
7
  # run tests on custom_test page
8
8
  module CustomTest
9
- include AtCoderFriends::PathUtil
10
-
11
9
  def code_test(infile)
12
- path, _dir, _prg, _base, ext, _q = split_prg_path(ctx.path)
13
- lang = lang_id(ext)
10
+ path, _dir, _prg, _base, ext, _q = ctx.path_info.components
11
+ langs = lang_id(ext)
14
12
  src = File.read(path, encoding: Encoding::UTF_8)
15
13
  data = File.read(infile)
16
14
 
17
- post_custom_test(lang, src, data)
15
+ post_custom_test(langs, src, data)
18
16
  check_custom_test
19
17
  end
20
18
 
21
- def post_custom_test(lang, src, data)
19
+ def post_custom_test(langs, src, data)
22
20
  page = fetch_with_auth(contest_url('custom_test'))
21
+ lang = find_lang(page, langs)
23
22
  script = page.search('script').text
24
23
  csrf_token = script.scan(/var csrfToken = "(.*)"/)[0][0]
25
24
 
@@ -4,22 +4,21 @@ module AtCoderFriends
4
4
  module Scraping
5
5
  # submit sources on submit page
6
6
  module Submission
7
- include AtCoderFriends::PathUtil
8
-
9
7
  def submit
10
- path, _dir, prg, _base, ext, q = split_prg_path(ctx.path)
8
+ path, _dir, prg, _base, ext, q = ctx.path_info.components
11
9
  puts "***** submit #{prg} *****"
12
- lang = lang_id(ext)
10
+ langs = lang_id(ext)
13
11
  src = File.read(path, encoding: Encoding::UTF_8)
14
12
 
15
- post_submit(q, lang, src)
13
+ post_submit(q, langs, src)
16
14
  end
17
15
 
18
- def post_submit(q, lang, src)
16
+ def post_submit(q, langs, src)
19
17
  page = fetch_with_auth(contest_url('submit'))
18
+ lang = find_lang(page, langs)
20
19
  form = page.forms[1]
21
20
  form.field_with(name: 'data.TaskScreenName') do |sel|
22
- option = sel.options.find { |op| op.text.start_with?(q) }
21
+ option = sel.options.find { |op| op.text =~ /\A#{q}\W/ }
23
22
  option&.select || (raise AppError, "unknown problem:#{q}.")
24
23
  end
25
24
  form.add_field!('data.LanguageId', lang)
@@ -7,9 +7,14 @@ module AtCoderFriends
7
7
  def fetch_all
8
8
  puts "***** fetch_all #{contest} *****"
9
9
  fetch_assignments.map do |q, url|
10
- pbm = fetch_problem(q, url)
11
- yield pbm if block_given?
12
- pbm
10
+ begin
11
+ pbm = fetch_problem(q, url)
12
+ yield pbm if block_given?
13
+ pbm
14
+ rescue StandardError => e
15
+ puts e.to_s
16
+ puts e.backtrace
17
+ end
13
18
  end
14
19
  end
15
20
 
@@ -8,6 +8,7 @@ module AtCoderFriends
8
8
  # run tests for the specified program.
9
9
  class Base
10
10
  include PathUtil
11
+
11
12
  STATUS_STR = {
12
13
  OK: '<< OK >>'.green,
13
14
  WA: '!!!!! WA !!!!!'.red,
@@ -19,7 +20,7 @@ module AtCoderFriends
19
20
 
20
21
  def initialize(ctx)
21
22
  @ctx = ctx
22
- @path, @dir, @prg, @base, @ext, @q = split_prg_path(ctx.path)
23
+ @path, @dir, @prg, @base, @ext, @q = ctx.path_info.components
23
24
  @detail = true
24
25
  end
25
26
 
@@ -32,11 +33,23 @@ module AtCoderFriends
32
33
  end
33
34
 
34
35
  def test_loc
35
- test_cmd ? 'local' : 'remote'
36
+ if test_cmd
37
+ 'local'
38
+ elsif ctx.scraping_agent.respond_to?(:code_test)
39
+ 'remote'
40
+ else
41
+ raise AppError, "test_cmd for .#{ext} is not specified."
42
+ end
36
43
  end
37
44
 
38
45
  def test_mtd
39
- test_cmd ? :local_test : :remote_test
46
+ if test_cmd
47
+ :local_test
48
+ elsif ctx.scraping_agent.respond_to?(:code_test)
49
+ :remote_test
50
+ else
51
+ raise AppError, "test_cmd for .#{ext} is not specified."
52
+ end
40
53
  end
41
54
 
42
55
  def run_test(id, infile, outfile, expfile)
@@ -63,7 +76,7 @@ module AtCoderFriends
63
76
  end
64
77
 
65
78
  def local_test(infile, outfile)
66
- system("#{test_cmd} < #{infile} > #{outfile}")
79
+ system("#{test_cmd} < \"#{infile}\" > \"#{outfile}\"")
67
80
  end
68
81
 
69
82
  def remote_test(infile, outfile)
@@ -4,20 +4,18 @@ module AtCoderFriends
4
4
  module TestRunner
5
5
  # run test cases for the specified program with judge input/output.
6
6
  class Judge < Base
7
- include PathUtil
8
-
9
7
  attr_reader :data_dir, :result_dir
10
8
 
11
9
  def initialize(ctx)
12
10
  super(ctx)
13
- @data_dir = cases_dir(dir)
14
- @result_dir = cases_dir(tmp_dir(path))
11
+ @data_dir = ctx.path_info.cases_dir
12
+ @result_dir = ctx.path_info.cases_out_dir
15
13
  end
16
14
 
17
15
  def judge_all
18
16
  puts "***** judge_all #{prg} (#{test_loc}) *****"
19
- results = Dir["#{data_dir}/#{q}/in/*.txt"].sort.map do |infile|
20
- id = File.basename(infile, '.txt')
17
+ results = Dir["#{data_dir}/#{q}/in/*"].sort.map do |infile|
18
+ id = File.basename(infile)
21
19
  judge(id, false)
22
20
  end
23
21
  !results.empty? && results.all?
@@ -30,9 +28,9 @@ module AtCoderFriends
30
28
 
31
29
  def judge(id, detail = true)
32
30
  @detail = detail
33
- infile = "#{data_dir}/#{q}/in/#{id}.txt"
34
- outfile = "#{result_dir}/#{q}/result/#{id}.txt"
35
- expfile = "#{data_dir}/#{q}/out/#{id}.txt"
31
+ infile = "#{data_dir}/#{q}/in/#{id}"
32
+ outfile = "#{result_dir}/#{q}/result/#{id}"
33
+ expfile = "#{data_dir}/#{q}/out/#{id}"
36
34
  run_test(id, infile, outfile, expfile)
37
35
  end
38
36
  end