at_coder_friends 0.6.3 → 0.6.8

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