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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c37cfc3181ae4abcc3d837e5a312987af2e9758e
4
- data.tar.gz: b6573770d47b1d73cf94b4a048cb9b7cbcd49b26
3
+ metadata.gz: cfcd8c547d0916bc1db5c76b5c698e410ddb3541
4
+ data.tar.gz: 03e06ac13b32c2d55166cf1cefa83ad9cfbe3fa2
5
5
  SHA512:
6
- metadata.gz: 5a21187eab2ff6217744414daa64251e8b1602bbc5e2d8c5e05d6f9b1dff6f9cfd209a5d8136900e9ab65f03289d3338743e01f7eb4043b94908956b09b4863b
7
- data.tar.gz: a3557e02966c8189d06c1133cbc519cb1a53dd31e385d433eba628232d3557ae1c1f9e96d2edcbf406c92954e8c358adb3e4ee9c2e4d1c4a041a87b52da95e58
6
+ metadata.gz: d73018f888e13b7a37a19b8945b879513caf4952ff5bdcbe9412c413a84d05c4c055fb00166e97fe3b5420637b0c87c767796f9c2835e82c879451f854b2fc9d
7
+ data.tar.gz: 602aa37bf5f315a9bccfbd3e676f19b0651c4687fbe351e65d670359c956b74e230ebcedade158928cd1486092947af1bfb34c1504632b7389d1029da2afb70a
@@ -2,6 +2,7 @@ inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
4
  Exclude:
5
+ - 'templates/**/*'
5
6
  - 'spec/fixtures/**/*'
6
7
  - 'regression/**/*'
7
8
  TargetRubyVersion: 2.3
@@ -2,10 +2,7 @@ Metrics/AbcSize:
2
2
  Max: 20
3
3
 
4
4
  Metrics/ClassLength:
5
- Max: 135
6
-
7
- Metrics/ModuleLength:
8
- Max: 120
5
+ Max: 125
9
6
 
10
7
  Metrics/MethodLength:
11
8
  Max: 15
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- at_coder_friends (0.5.2)
4
+ at_coder_friends (0.6.0)
5
5
  colorize (~> 0.8.1)
6
6
  launchy (~> 2.4.3)
7
7
  mechanize (~> 2.0)
data/README.md CHANGED
@@ -87,7 +87,7 @@ See [CONFIGURATION.md](docs/CONFIGURATION.md) for details.
87
87
  ## Notes
88
88
 
89
89
  - Compilation is not supported.
90
- - Source generator supports only ruby and C++.
90
+ - Source generator supports only Ruby and C++ in default.
91
91
 
92
92
  ## For Sublime Text user
93
93
 
@@ -1,5 +1,8 @@
1
1
  user: ~
2
2
  password: ~
3
+ generators:
4
+ - RubyBuiltin
5
+ - CxxBuiltin
3
6
  ext_settings:
4
7
  'awk':
5
8
  submit_lang: '3506' # Awk (mawk)
@@ -20,9 +20,14 @@ Its contents are merged with the default settings.
20
20
  ```.at_coder_friends.yml```で新しい設定項目の追加や、既存の設定項目の変更をすると、
21
21
  その内容がデフォルト設定にマージされます。
22
22
 
23
+ [/config/default.yml](/config/default.yml)
24
+
23
25
  ```YAML
24
26
  user: ~
25
27
  password: ~
28
+ generators:
29
+ - RubyBuiltin
30
+ - CxxBuiltin
26
31
  ext_settings:
27
32
  'awk':
28
33
  submit_lang: '3506' # Awk (mawk)
@@ -115,15 +120,43 @@ ext_settings:
115
120
 
116
121
  - user
117
122
  AtCoder username
118
- If omitted, you are prompted to enter it on the first run.
123
+ If omitted, you are prompted to enter it on the first login.
119
124
  AtCoderのユーザ名
120
- 省略した場合、初回の実行時に入力を求められます
125
+ 省略した場合、初回のログイン時に入力を求められます
121
126
 
122
127
  - password
123
128
  AtCoder password
124
- If omitted, you are prompted to enter it on the first run.
129
+ If omitted, you are prompted to enter it on the first login.
125
130
  AtCoderのパスワード
126
- 省略した場合、初回の実行時に入力を求められます
131
+ 省略した場合、初回のログイン時に入力を求められます
132
+
133
+ - generators
134
+ List of source generator class names
135
+ In default, ```RubyBuiltin``` and ```CxxBuiltin``` are available.
136
+ For those other than above, corresponding plugin will be used
137
+ if it has been installed (?).
138
+ ソーススケルトンを生成するジェネレータのクラス名(リスト形式)
139
+ 既定の状態では「RubyBuiltin」と「CxxBuiltin」が利用でき、
140
+ 上記以外については、対応するプラグインがインストールされていれば
141
+ それが使われます(?)
142
+
143
+ For example, if ```RubyAlt``` is specified as generator name,
144
+ following plugin will be used:
145
+
146
+ | | |
147
+ |-----------------|---------------------------------------------------|
148
+ |Gem Name |```at_coder_friends-generator-ruby_alt``` |
149
+ |Require Statement|```require 'at_coder_friends/generator/ruby_alt'```|
150
+ |Main Class Name |```AtCoderFriends::Generator::RubyAlt``` |
151
+
152
+ - generator_settings
153
+ - _(generator name)_
154
+ Settings for each generator
155
+ For details, see the manual of each generator.
156
+ [Settings for built-in generators](#builtin-generator-settings)
157
+ ジェネレータ毎の設定
158
+ 詳細は各ジェネレータのマニュアルを参照してください
159
+ [組込ジェネレータの設定](#builtin-generator-settings)
127
160
 
128
161
  - ext_settings
129
162
  Extension specific settings
@@ -159,10 +192,23 @@ ext_settings:
159
192
  Execution command for Linux(optional)
160
193
  Linux専用実行コマンド(あれば設定)
161
194
 
195
+ <a id="builtin-generator-settings"></a>
196
+ ### Settings for RubyBuiltin generator
197
+ | Setting | Description | Default |
198
+ |---------|-------------|---------|
199
+ |default_template|Source template file path|[/templates/ruby_builtin_default.rb](/templates/ruby_builtin_default.rb)|
200
+ |interactive_template|Source template file path for interactive problems|[/templates/ruby_builtin_interactive.rb](/templates/ruby_builtin_interactive.rb)|
201
+
202
+ ### Settings for CxxBuiltin generator
203
+ | Setting | Description | Default |
204
+ |---------|-------------|---------|
205
+ |default_template|Source template file path|[/templates/cxx_builtin_default.cxx](/templates/cxx_builtin_default.cxx)|
206
+ |interactive_template|Source template file path for interactive problems|[/templates/cxx_builtin_interactive.cxx](/templates/cxx_builtin_interactive.cxx)|
207
+
162
208
  ### Language ID list (2019/09/16)
163
209
 
164
210
  | Language ID | Description |
165
- |---|---|
211
+ |-------------|-------------|
166
212
  |3003|C++14 (GCC 5.4.1)|
167
213
  |3001|Bash (GNU bash v4.3.11)|
168
214
  |3002|C (GCC 5.4.1)|
@@ -229,13 +275,32 @@ during execution.
229
275
  test_cmd 文字列中の以下の変数には、
230
276
  実行時に対象ファイルのパス情報が展開されます。
231
277
 
232
- | Variable | Descripton |
233
- |---|---|
234
- |{dir}|the directory name of the file<br>ファイルの置かれているディレクトリ名|
235
- |{base}|the file name excluding the extension<br>拡張子を除いたファイル名|
278
+ | Variable | Descripton |
279
+ |----------|----------------------------------------------------------------|
280
+ |{dir} |the directory name of the file<br>ファイルの置かれているディレクトリ名|
281
+ |{base} |the file name excluding the extension<br>拡張子を除いたファイル名 |
236
282
 
237
283
  ## Examples of overwriting settings
238
284
 
285
+ - Use only Ruby source generator
286
+ Rubyのソースジェネレータのみ使用
287
+ ```YAML
288
+ generators:
289
+ - RubyBuiltin
290
+ ```
291
+ - Do not use source generator
292
+ ソースジェネレータを使用しない
293
+ ```YAML
294
+ generators: ~
295
+ ```
296
+ - change C++ source template
297
+ C++のソース雛型を変更
298
+ ```YAML
299
+ generator_settings:
300
+ CxxBuiltin:
301
+ default_template: /path/to/template
302
+ ```
303
+
239
304
  - Test .py with local Python
240
305
  .pyをローカル環境のPythonでテスト
241
306
  ```YAML
@@ -15,12 +15,17 @@ require 'at_coder_friends/scraping/custom_test'
15
15
  require 'at_coder_friends/scraping/submission'
16
16
  require 'at_coder_friends/scraping/tasks'
17
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'
18
+ require 'at_coder_friends/parser/section_wrapper'
19
+ require 'at_coder_friends/parser/sections'
20
+ require 'at_coder_friends/parser/sample_data'
21
+ require 'at_coder_friends/parser/input_format'
22
+ require 'at_coder_friends/parser/constraints'
23
+ require 'at_coder_friends/parser/interactive'
24
+ require 'at_coder_friends/parser/binary'
21
25
  require 'at_coder_friends/parser/main'
22
- require 'at_coder_friends/ruby_generator'
23
- require 'at_coder_friends/cxx_generator'
26
+ require 'at_coder_friends/generator/cxx_builtin'
27
+ require 'at_coder_friends/generator/ruby_builtin'
28
+ require 'at_coder_friends/generator/main'
24
29
  require 'at_coder_friends/emitter'
25
30
  require 'at_coder_friends/context'
26
31
  require 'at_coder_friends/cli'
@@ -92,17 +92,14 @@ module AtCoderFriends
92
92
  raise AppError, "#{path} is not empty." \
93
93
  if Dir.exist?(path) && !Dir["#{path}/*"].empty?
94
94
 
95
- rb_gen = RubyGenerator.new
96
- cxx_gen = CxxGenerator.new
97
95
  ctx.scraping_agent.fetch_all do |pbm|
98
96
  Parser::Main.process(pbm)
99
- rb_gen.process(pbm)
100
- cxx_gen.process(pbm)
97
+ ctx.generator.process(pbm)
101
98
  ctx.emitter.emit(pbm)
102
99
  end
103
100
  end
104
101
 
105
- def test_one(id = 1)
102
+ def test_one(id = '001')
106
103
  ctx.sample_test_runner.test_one(id)
107
104
  end
108
105
 
@@ -22,6 +22,10 @@ module AtCoderFriends
22
22
  @scraping_agent ||= Scraping::Agent.new(self)
23
23
  end
24
24
 
25
+ def generator
26
+ @generator ||= Generator::Main.new(self)
27
+ end
28
+
25
29
  def sample_test_runner
26
30
  @sample_test_runner ||= TestRunner::Sample.new(self)
27
31
  end
@@ -14,8 +14,8 @@ module AtCoderFriends
14
14
  end
15
15
 
16
16
  def emit(pbm)
17
- pbm.smps.each { |smp| emit_sample(pbm, smp) }
18
- pbm.srcs.each { |src| emit_source(pbm, src) }
17
+ pbm.samples.each { |smp| emit_sample(pbm, smp) }
18
+ pbm.sources.each { |src| emit_source(pbm, src) }
19
19
  end
20
20
 
21
21
  def emit_sample(pbm, smp)
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ module Generator
5
+ module CxxBuiltinConstants
6
+ ACF_HOME = File.realpath(File.join(__dir__, '..', '..', '..'))
7
+ TMPL_DIR = File.join(ACF_HOME, 'templates')
8
+ DEFAULT_TMPL = File.join(TMPL_DIR, 'cxx_builtin_default.cxx')
9
+ INTERACTIVE_TMPL = File.join(TMPL_DIR, 'cxx_builtin_interactive.cxx')
10
+ SCANF_FMTS = [
11
+ 'scanf("%<fmt>s", %<addr>s);',
12
+ 'REP(i, %<sz1>s) scanf("%<fmt>s", %<addr>s);',
13
+ 'REP(i, %<sz1>s) REP(j, %<sz2>s) scanf("%<fmt>s", %<addr>s);'
14
+ ].freeze
15
+ FMT_FMTS = { number: '%d', string: '%s', char: '%s' }.freeze
16
+ ADDR_FMTS = {
17
+ single: {
18
+ number: '&%<v>s',
19
+ string: '%<v>s'
20
+ },
21
+ harray: {
22
+ number: '%<v>s + i',
23
+ string: '%<v>s[i]',
24
+ char: '%<v>s'
25
+ },
26
+ varray: {
27
+ number: '%<v>s + i',
28
+ string: '%<v>s[i]'
29
+ },
30
+ matrix: {
31
+ number: '&%<v>s[i][j]',
32
+ string: '%<v>s[i][j]',
33
+ char: '%<v>s[i]'
34
+ }
35
+ }.freeze
36
+ DEFAULT_OUTPUT = <<~TEXT
37
+ int ans = 0;
38
+ printf("%d\\n", ans);
39
+ TEXT
40
+ BINARY_OUTPUT_FMT = <<~TEXT
41
+ bool cond = false;
42
+ puts(cond ? "%s" : "%s");
43
+ TEXT
44
+ end
45
+
46
+ # generates C++ source code from problem description
47
+ class CxxBuiltin
48
+ include CxxBuiltinConstants
49
+
50
+ attr_reader :cfg, :pbm
51
+
52
+ def initialize(cfg = nil)
53
+ @cfg = cfg || {}
54
+ end
55
+
56
+ def process(pbm)
57
+ src = generate(pbm)
58
+ pbm.add_src(:cxx, src)
59
+ end
60
+
61
+ def generate(pbm)
62
+ @pbm = pbm
63
+ src = File.read(select_template)
64
+ src = embed_lines(src, '/*** URL ***/', [pbm.url])
65
+ src = embed_lines(src, '/*** CONSTS ***/', gen_consts)
66
+ src = embed_lines(src, '/*** DCLS ***/', gen_decls)
67
+ src = embed_lines(src, '/*** INPUTS ***/', gen_inputs)
68
+ embed_lines(src, '/*** OUTPUT ***/', gen_output.split("\n"))
69
+ end
70
+
71
+ def embed_lines(src, pat, lines)
72
+ re = Regexp.escape(pat)
73
+ src.gsub(
74
+ /^(.*)#{re}(.*)$/,
75
+ lines.map { |s| '\1' + s + '\2' }.join("\n")
76
+ )
77
+ end
78
+
79
+ def select_template(interactive = pbm.options.interactive)
80
+ interactive ? interactive_template : default_template
81
+ end
82
+
83
+ def default_template
84
+ cfg['default_template'] || DEFAULT_TMPL
85
+ end
86
+
87
+ def interactive_template
88
+ cfg['interactive_template'] || INTERACTIVE_TMPL
89
+ end
90
+
91
+ def gen_consts(constraints = pbm.constraints)
92
+ constraints
93
+ .select { |c| c.type == :max }
94
+ .map { |c| "const int #{c.name.upcase}_MAX = #{c.value};" }
95
+ end
96
+
97
+ def gen_decls(inpdefs = pbm.formats)
98
+ inpdefs.map { |inpdef| gen_decl(inpdef) }.flatten
99
+ end
100
+
101
+ def gen_decl(inpdef)
102
+ case inpdef.container
103
+ when :single
104
+ gen_single_decl(inpdef)
105
+ when :harray
106
+ gen_harray_decl(inpdef)
107
+ when :varray
108
+ gen_varray_decl(inpdef)
109
+ when :matrix
110
+ gen_matrix_decl(inpdef)
111
+ end
112
+ end
113
+
114
+ def gen_single_decl(inpdef)
115
+ names = inpdef.names
116
+ case inpdef.item
117
+ when :number
118
+ dcl = names.join(', ')
119
+ "int #{dcl};"
120
+ when :string
121
+ names.map { |v| "char #{v}[#{v.upcase}_MAX + 1];" }
122
+ end
123
+ end
124
+
125
+ def gen_harray_decl(inpdef)
126
+ v = inpdef.names[0]
127
+ sz = gen_arr_size(inpdef.size)[0]
128
+ case inpdef.item
129
+ when :number
130
+ "int #{v}[#{sz}];"
131
+ when :string
132
+ "char #{v}[#{sz}][#{v.upcase}_MAX + 1];"
133
+ when :char
134
+ "char #{v}[#{sz} + 1];"
135
+ end
136
+ end
137
+
138
+ def gen_varray_decl(inpdef)
139
+ names = inpdef.names
140
+ sz = gen_arr_size(inpdef.size)[0]
141
+ case inpdef.item
142
+ when :number
143
+ names.map { |v| "int #{v}[#{sz}];" }
144
+ when :string
145
+ names.map { |v| "char #{v}[#{sz}][#{v.upcase}_MAX + 1];" }
146
+ end
147
+ end
148
+
149
+ def gen_matrix_decl(inpdef)
150
+ v = inpdef.names[0]
151
+ sz1, sz2 = gen_arr_size(inpdef.size)
152
+ case inpdef.item
153
+ when :number
154
+ "int #{v}[#{sz1}][#{sz2}];"
155
+ when :string
156
+ "char #{v}[#{sz1}][#{sz2}][#{v.upcase}_MAX + 1];"
157
+ when :char
158
+ "char #{v}[#{sz1}][#{sz2} + 1];"
159
+ end
160
+ end
161
+
162
+ def gen_arr_size(szs)
163
+ szs.map { |sz| sz =~ /\D/ ? "#{sz.upcase}_MAX" : sz }
164
+ end
165
+
166
+ def gen_inputs(inpdefs = pbm.formats)
167
+ inpdefs.map { |inpdef| gen_input(inpdef) }.flatten
168
+ end
169
+
170
+ # rubocop:disable Metrics/AbcSize
171
+ def gen_input(inpdef)
172
+ dim = inpdef.size.size - (inpdef.item == :char ? 1 : 0)
173
+ scanf = SCANF_FMTS[dim]
174
+ sz1, sz2 = inpdef.size
175
+ fmt = FMT_FMTS[inpdef.item] * inpdef.names.size
176
+ addr_fmt = ADDR_FMTS[inpdef.container][inpdef.item]
177
+ addr = inpdef.names.map { |v| format(addr_fmt, v: v) }.join(', ')
178
+ format(scanf, sz1: sz1, sz2: sz2, fmt: fmt, addr: addr)
179
+ end
180
+ # rubocop:enable Metrics/AbcSize
181
+
182
+ def gen_output(vs = pbm.options.binary_values)
183
+ if vs
184
+ format(BINARY_OUTPUT_FMT, *vs)
185
+ else
186
+ DEFAULT_OUTPUT
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AtCoderFriends
4
+ module Generator
5
+ # entry point of source generation
6
+ class Main
7
+ attr_reader :ctx
8
+
9
+ def initialize(ctx)
10
+ @ctx = ctx
11
+ @cache = {}
12
+ end
13
+
14
+ def process(pbm)
15
+ generators = ctx.config.dig('generators') || []
16
+ generators.each do |gen_name|
17
+ gen_obj = load_obj(gen_name)
18
+ gen_obj.process(pbm)
19
+ end
20
+ end
21
+
22
+ def load_obj(gen_name)
23
+ @cache[gen_name] ||= begin
24
+ gen_class = load_class(gen_name)
25
+ gen_cnf = config_for(gen_name)
26
+ gen_class.new(gen_cnf)
27
+ end
28
+ end
29
+
30
+ def load_class(gen_name)
31
+ unless AtCoderFriends::Generator.const_defined?(gen_name)
32
+ require "at_coder_friends/generator/#{to_snake(gen_name)}"
33
+ end
34
+ AtCoderFriends::Generator.const_get(gen_name)
35
+ rescue LoadError
36
+ raise AppError, "plugin load error : generator #{gen_name} not found."
37
+ end
38
+
39
+ def config_for(gen_name)
40
+ ctx.config.dig('generator_settings', gen_name) || {}
41
+ end
42
+
43
+ def to_snake(str)
44
+ str
45
+ .gsub(/::/, '/')
46
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
47
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
48
+ .tr('-', '_')
49
+ .downcase
50
+ end
51
+ end
52
+ end
53
+ end