gettc 1.10 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gettc +60 -69
  3. data/dist/config.yml +1 -1
  4. data/dist/include/cpp/engine.rb +78 -86
  5. data/dist/include/cpp/topcoder +236 -236
  6. data/dist/include/go/engine.rb +53 -61
  7. data/dist/include/haskell/engine.rb +112 -122
  8. data/dist/include/java/engine.rb +187 -184
  9. data/dist/include/javascript/engine.rb +26 -30
  10. data/dist/include/javascript/topcoder.js +3 -3
  11. data/dist/include/javascript/topcoder/errors.js +5 -5
  12. data/dist/include/javascript/topcoder/reader.js +188 -165
  13. data/dist/include/javascript/topcoder/writer.js +37 -33
  14. data/dist/include/python/engine.rb +43 -52
  15. data/dist/include/python/topcoder/__init__.pyc +0 -0
  16. data/dist/include/python/topcoder/__pycache__/__init__.cpython-34.pyc +0 -0
  17. data/dist/include/python/topcoder/__pycache__/errors.cpython-34.pyc +0 -0
  18. data/dist/include/python/topcoder/__pycache__/reader.cpython-34.pyc +0 -0
  19. data/dist/include/python/topcoder/__pycache__/writer.cpython-34.pyc +0 -0
  20. data/dist/include/python/topcoder/errors.pyc +0 -0
  21. data/dist/include/python/topcoder/reader.pyc +0 -0
  22. data/dist/include/python/topcoder/writer.pyc +0 -0
  23. data/dist/include/ruby/engine.rb +47 -52
  24. data/dist/include/ruby/topcoder/reader.rb +205 -193
  25. data/dist/include/ruby/topcoder/writer.rb +39 -37
  26. data/dist/template/bin/runner.rb +146 -151
  27. data/dist/template/bin/runner.sh +96 -96
  28. data/dist/template/prob/{name}.html +1 -1
  29. data/dist/template/solve/cpp/{name}.cpp +3 -4
  30. data/dist/template/solve/cpp/{name}Solver.cpp +14 -14
  31. data/dist/template/solve/go/{name}/{name}.go +3 -3
  32. data/dist/template/solve/go/{name}Solver.go +2 -2
  33. data/dist/template/solve/haskell/{name}.hs +6 -6
  34. data/dist/template/solve/haskell/{name}Solver.hs +10 -10
  35. data/dist/template/solve/java/{name}.java +4 -4
  36. data/dist/template/solve/java/{name}Solver.java +4 -4
  37. data/dist/template/solve/javascript/{name}.js +4 -6
  38. data/dist/template/solve/javascript/{name}Solver.js +11 -9
  39. data/dist/template/solve/python/{name}.py +1 -1
  40. data/dist/template/solve/python/{name}Solver.py +2 -2
  41. data/dist/template/solve/ruby/{name}.rb +4 -4
  42. data/dist/template/solve/ruby/{name}Solver.rb +14 -17
  43. data/dist/template/util/check/check.cpp +19 -19
  44. data/{core/lib → lib}/gettc.rb +0 -0
  45. data/lib/gettc/account.rb +14 -0
  46. data/lib/gettc/download.rb +211 -0
  47. data/lib/gettc/generate.rb +156 -0
  48. data/lib/gettc/parse.rb +237 -0
  49. data/lib/gettc/print.rb +54 -0
  50. data/lib/gettc/problem.rb +39 -0
  51. data/lib/gettc/signature.rb +63 -0
  52. data/lib/gettc/types.rb +93 -0
  53. data/lib/version.rb +3 -0
  54. data/test/gettc/download_test.rb +61 -0
  55. data/test/gettc/generate_test.rb +70 -0
  56. data/test/gettc/parse_test.rb +78 -0
  57. data/test/gettc/signature_test.rb +71 -0
  58. data/test/gettc/types_test.rb +31 -0
  59. metadata +28 -23
  60. data/core/lib/gettc/download.rb +0 -130
  61. data/core/lib/gettc/generate.rb +0 -145
  62. data/core/lib/gettc/parse.rb +0 -233
  63. data/core/lib/gettc/print.rb +0 -56
  64. data/core/lib/gettc/problem.rb +0 -33
  65. data/core/lib/gettc/signature.rb +0 -55
  66. data/core/lib/gettc/types.rb +0 -83
  67. data/core/lib/version.rb +0 -3
  68. data/core/test/gettc/download_test.rb +0 -29
  69. data/core/test/gettc/generate_test.rb +0 -31
  70. data/core/test/gettc/parse_test.rb +0 -104
  71. data/core/test/gettc/signature_test.rb +0 -54
  72. data/core/test/gettc/types_test.rb +0 -28
@@ -0,0 +1,156 @@
1
+ require "fileutils"
2
+ require "pathname"
3
+ require "erb"
4
+
5
+ require "gettc/problem"
6
+ require "gettc/signature"
7
+ require "gettc/print"
8
+
9
+ module Gettc
10
+ GenerateError = Class.new StandardError
11
+
12
+ class ProblemDirExists < GenerateError
13
+ attr_accessor :dir
14
+
15
+ def initialize(dir, message = "Problem directory already exists")
16
+ @dir = dir
17
+ super "#{message}: (#{dir})"
18
+ end
19
+ end
20
+
21
+ class SourceDirNotExist < GenerateError
22
+ attr_accessor :dir
23
+
24
+ def initialize(dir, message = "Source directory does not exist")
25
+ @dir = dir
26
+ super "#{message}: (#{dir})"
27
+ end
28
+ end
29
+
30
+ class TargetDirNotExist < GenerateError
31
+ attr_accessor :dir
32
+
33
+ def initialize(dir, message = "Target directory does not exist")
34
+ @dir = dir
35
+ super "#{message}: (#{dir})"
36
+ end
37
+ end
38
+
39
+ class TemplateError < GenerateError
40
+ attr_accessor :dir
41
+
42
+ def initialize(err, source, message = "Template error")
43
+ @err = err
44
+ @source = source
45
+ super "#{message} (#{source})\n#{err}"
46
+ end
47
+ end
48
+
49
+ class Generator
50
+ def initialize(config_dir, target_dir)
51
+ @source_dir = File.join(config_dir, "template")
52
+ raise SourceDirNotExist.new(@source_dir) unless File.directory?(@source_dir)
53
+
54
+ @target_dir = target_dir
55
+ raise TargetDirNotExist.new(@target_dir) unless File.directory?(@target_dir)
56
+
57
+ load_engines(File.join(config_dir, "include"))
58
+ end
59
+
60
+ def generate(prob)
61
+ @prob = prob
62
+
63
+ @problem_dir = File.join(@target_dir, prob.name)
64
+ raise ProblemDirExists.new(@problem_dir) if File.exists?(@problem_dir)
65
+ FileUtils.mkdir(@problem_dir)
66
+
67
+ method_sig = @prob.definitions["Method signature"]
68
+ if method_sig.nil?
69
+ $stderr.puts "[Warning] No definition for method signature found"
70
+ else
71
+ vars = parse_method_signature(method_sig)
72
+ func = vars.shift
73
+ end
74
+ @context = binding
75
+
76
+ walk(@source_dir, @problem_dir)
77
+ end
78
+
79
+ private
80
+
81
+ def generate_images(images, images_dir)
82
+ images.each do |image|
83
+ filename = File.join(images_dir, image.name)
84
+ File.open(filename, "wb") { |f| f.write(image.content) }
85
+ end
86
+ end
87
+
88
+ def generate_test_case(cases, data_dir)
89
+ cases.each_with_index do |case_data, case_id|
90
+ File.write(File.join(data_dir, "#{case_id.to_s}.in"), case_data.input)
91
+ File.write(File.join(data_dir, "#{case_id.to_s}.out"), case_data.output)
92
+ end
93
+ end
94
+
95
+ def generate_template(source, target)
96
+ before = File.read(source)
97
+
98
+ begin
99
+ after = ERB.new(before).result(@context)
100
+ rescue SyntaxError, NameError => err
101
+ raise TemplateError.new(err, source)
102
+ end
103
+
104
+ begin
105
+ File.write(target, after)
106
+ rescue StandardError
107
+ raise target
108
+ end
109
+ end
110
+
111
+ def filter(target_dir, name)
112
+ case name
113
+ when "{images_d}"
114
+ generate_images(@prob.images, target_dir)
115
+ when "{examples_d}"
116
+ generate_test_case(@prob.examples, target_dir)
117
+ when "{systests_d}"
118
+ generate_test_case(@prob.systests, target_dir)
119
+ else
120
+ return name.gsub(/\{(\w*)\}/) { |match| @prob.name if $1 == "name" }
121
+ end
122
+ nil
123
+ end
124
+
125
+ def load_engines(include_dir)
126
+ return unless File.exists?(include_dir)
127
+ Dir.foreach(include_dir) do |name|
128
+ if File.directory?(child = File.join(include_dir, name))
129
+ if File.exists?(engine = File.join(child, "engine.rb"))
130
+ engine = "./#{engine}" unless Pathname.new(engine).absolute?
131
+ require engine
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def walk(source_parent, target_parent)
138
+ Dir.foreach(source_parent) do |name|
139
+ next if [".", ".."].include?(name)
140
+
141
+ target_name = filter(target_parent, name)
142
+ next if target_name.nil?
143
+
144
+ source_child = File.join(source_parent, name)
145
+ target_child = File.join(target_parent, target_name)
146
+
147
+ if File.directory?(source_child)
148
+ FileUtils.mkdir(target_child) unless File.exists?(target_child)
149
+ walk(source_child, target_child)
150
+ elsif File.file?(source_child)
151
+ generate_template(source_child, target_child)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,237 @@
1
+ require "gettc/problem"
2
+ require "gettc/download"
3
+
4
+ require "uri"
5
+ require "pathname"
6
+ require "hpricot"
7
+ require "logger"
8
+
9
+ module Gettc
10
+ class Parser
11
+ def initialize(downloader)
12
+ @downloader = downloader
13
+ @problem = Problem.new
14
+ end
15
+
16
+ def parse(problem_id)
17
+ doc = Hpricot(@downloader.download_statement(problem_id))
18
+
19
+ @problem = Problem.new
20
+ @problem.id = problem_id
21
+ @problem.name = parse_name(doc.search("tr/td.statTextBig").html)
22
+
23
+ html = doc.search("td.problemText/table").html
24
+
25
+ has_notes = true
26
+ has_constraints = true
27
+ has_examples = true
28
+
29
+ _, x = self.class.indexes(html, self.class.h3("Problem Statement"))
30
+ y, z = self.class.indexes(html, self.class.h3("Definition"))
31
+ parse_statement(html[x .. y])
32
+
33
+ x, y = self.class.indexes(html, self.class.h3("Notes"))
34
+ if x.nil?
35
+ has_notes = false
36
+ x, y = self.class.indexes(html, self.class.h3("Constraints"))
37
+ if x.nil?
38
+ has_constraints = false
39
+ x, y = self.class.indexes(html, self.class.h3("Examples"))
40
+ if x.nil?
41
+ has_examples = false
42
+ x = -2
43
+ end
44
+ end
45
+ end
46
+
47
+ parse_definitions(html[z .. x])
48
+
49
+ if has_notes
50
+ z, x = self.class.indexes(html, self.class.h3("Constraints"))
51
+ if z.nil?
52
+ has_constraints = false
53
+ z, x = self.class.indexes(html, self.class.h3("Examples"))
54
+ if z.nil?
55
+ has_examples = false
56
+ z = - 2
57
+ end
58
+ end
59
+ parse_notes(html[y .. z])
60
+ x, y = z, x
61
+ end
62
+
63
+ if has_constraints
64
+ z, x = self.class.indexes(html, self.class.h3("Examples"))
65
+ if z.nil?
66
+ has_examples = false
67
+ z = -2
68
+ end
69
+ parse_constraints(html[y .. z])
70
+ end
71
+
72
+ parse_examples(html[x .. -2]) if has_examples
73
+ parse_details(doc)
74
+
75
+ @problem
76
+ end
77
+
78
+ private
79
+
80
+ def self.indexes(str, substr)
81
+ from = str.index(substr)
82
+ return nil if from.nil?
83
+ return from - 1, from + substr.size
84
+ end
85
+
86
+ def self.h3(tag)
87
+ "<h3>#{tag}</h3>"
88
+ end
89
+
90
+ def self.filter_inout(text)
91
+ text.gsub("{", "[").gsub("}", "]").strip
92
+ end
93
+
94
+ def self.get_param_value(url, param_key)
95
+ if (match_data = url.match("[&|&amp;]#{param_key}=(\\d+)"))
96
+ match_data[1] if match_data.size > 1
97
+ end
98
+ end
99
+
100
+ def filter(html)
101
+ html = html.force_encoding("ISO-8859-1")
102
+ .encode("utf-8", {
103
+ invaid: :replace,
104
+ undef: :replace,
105
+ replace: ""
106
+ })
107
+
108
+ html.gsub!(/<b>(\w*)<\/b>/) { |match| "*#{$1}*" }
109
+ html.gsub!(/<sup>(\w*)<\/sup>/) { |match| "^(#{$1})" }
110
+ html.gsub!("&#160;", "")
111
+ html.gsub!("&nbsp;", " ")
112
+
113
+ text = Hpricot(html).to_plain_text
114
+ text.gsub!(/\[img:(http:\/\/[^\]]*)\]/) do |match|
115
+ url = $1
116
+ image = Image.new
117
+ image.name = Pathname.new(url).basename
118
+ begin
119
+ image.content = @downloader.download(url)
120
+ @problem.images << image
121
+ "![image](images/#{image.name})"
122
+ rescue HttpError
123
+ "![image](#{url})"
124
+ end
125
+ end
126
+
127
+ text
128
+ end
129
+
130
+ def parse_list_table(html)
131
+ result = []
132
+ Hpricot(html).search("/tr").each do |tr|
133
+ tds = tr.search("/td.statText")
134
+ result << filter(tds[1].html) if tds.size == 2
135
+ end
136
+ result
137
+ end
138
+
139
+ def parse_name(html)
140
+ @problem.name = filter(html.sub("Problem Statement for", ""))
141
+ end
142
+
143
+ def parse_statement(html)
144
+ @problem.statement = filter(html)
145
+ end
146
+
147
+ def parse_definitions(html)
148
+ Hpricot(html).search("/tr/td.statText/table/tr").each do |tr|
149
+ tds = tr.search("/td.statText")
150
+ @problem.definitions[tds[0].to_plain_text[0 .. -2]] = tds[1].to_plain_text if tds.size == 2
151
+ end
152
+ end
153
+
154
+ def parse_notes(html)
155
+ @problem.notes = parse_list_table(html)
156
+ end
157
+
158
+ def parse_constraints(html)
159
+ @problem.constraints = parse_list_table(html)
160
+ end
161
+
162
+ def parse_input(html)
163
+ text = ""
164
+ Hpricot(html).search("/table/tr/td.statText") do |td|
165
+ input = td.to_plain_text.strip
166
+ if text.empty?
167
+ text = input
168
+ else
169
+ text << ",\n" << input
170
+ end
171
+ end
172
+ self.class.filter_inout(text)
173
+ end
174
+
175
+ def parse_output(html)
176
+ self.class.filter_inout(Hpricot(html).to_plain_text.sub("Returns: ", ""))
177
+ end
178
+
179
+ def parse_reason(html)
180
+ filter(html)
181
+ end
182
+
183
+ def parse_examples(html)
184
+ tds = Hpricot(html).search("/tr/td.statText/table/tr/td.statText")
185
+ i = 0
186
+ while i < tds.size
187
+ example = Case.new
188
+ example.input = parse_input(tds[i].html)
189
+ example.output = parse_output(tds[i += 1].html)
190
+ example.reason = parse_reason(tds[i += 1].html)
191
+ @problem.examples << example
192
+ i += 1
193
+ end
194
+ end
195
+
196
+ def parse_systests(html)
197
+ _, y = self.class.indexes(html, "<!-- System Testing -->")
198
+ z, _ = self.class.indexes(html, "<!-- End System Testing -->")
199
+ return [] unless y && z
200
+
201
+ Hpricot(html[y .. z]).search("/table/tr[@valign=top]").each_with_object([]) do |tr, memo|
202
+ tds = tr.search("/td.statText")
203
+ next unless tds.size == 3
204
+
205
+ test = Case.new
206
+ test.input = self.class.filter_inout(tds[0].to_plain_text)
207
+ test.output = self.class.filter_inout(tds[1].to_plain_text)
208
+ @problem.systests << test
209
+ end
210
+ end
211
+
212
+ def download_systests(round_id)
213
+ detail_html = @downloader.download_detail(@problem.id, round_id)
214
+
215
+ Hpricot(detail_html).search("a[@href^=/stat?c=problem_solution]") do |elem|
216
+ url = elem.attributes["href"]
217
+
218
+ if solution_id = self.class.get_param_value(url, "cr")
219
+ parse_systests(@downloader.download_solution(@problem.id, round_id, solution_id))
220
+ return unless @problem.systests.empty?
221
+ end
222
+ end
223
+ end
224
+
225
+ def parse_details(doc)
226
+ doc.search("a[@href^=/tc?module=ProblemDetail]") do |elem|
227
+ @problem.url = elem.attributes["href"]
228
+ @problem.source = filter(elem.html)
229
+
230
+ if round_id = self.class.get_param_value(@problem.url, "rd")
231
+ download_systests(round_id)
232
+ return unless @problem.systests.empty?
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,54 @@
1
+ require "gettc/problem"
2
+ require "rdiscount"
3
+
4
+ module Gettc
5
+ class Problem
6
+ def to_md
7
+ out = "# [#{@name}](#{@url})\n"
8
+ out << "*#{@source}*\n\n"
9
+
10
+ out << "## Statement\n"
11
+ out << @statement << "\n\n"
12
+
13
+ unless @definitions.empty?
14
+ out << "## Definitions\n"
15
+ @definitions.each { |k, v| out << "- *#{k}*: `#{v}`\n" }
16
+ out << "\n"
17
+ end
18
+
19
+ unless @notes.empty?
20
+ out << "## Notes\n"
21
+ @notes.each { |note| out << "- #{note}\n" }
22
+ out << "\n"
23
+ end
24
+
25
+ unless @constraints.empty?
26
+ out << "## Constraints\n"
27
+ @constraints.each { |constraint| out << "- #{constraint}\n" }
28
+ out << "\n"
29
+ end
30
+
31
+ unless @examples.empty?
32
+ out << "## Examples\n"
33
+
34
+ @examples.each_index do |i|
35
+ example = @examples[i]
36
+ out << "### Example #{i + 1}\n"
37
+ out << "#### Input\n<c>"
38
+ out << example.input.gsub("\n", "<br />")
39
+ out << "</c>\n"
40
+ out << "#### Output\n<c>"
41
+ out << example.output.gsub("\n", "<br />")
42
+ out << "</c>\n"
43
+ out << "#### Reason\n#{example.reason}\n\n" unless example.reason.empty?
44
+ end
45
+ end
46
+
47
+ out
48
+ end
49
+
50
+ def to_html
51
+ RDiscount.new(to_md).to_html
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ module Gettc
2
+ class Case
3
+ attr_accessor :input, :output, :reason
4
+
5
+ def initialize
6
+ @input = ""
7
+ @output = ""
8
+ @reason = ""
9
+ end
10
+ end
11
+
12
+ class Image
13
+ attr_accessor :name, :content
14
+
15
+ def initialize
16
+ @name = ""
17
+ @content = ""
18
+ end
19
+ end
20
+
21
+ class Problem
22
+ attr_accessor :id, :name, :url, :source, :statement, :definitions, :notes,
23
+ :constraints, :examples, :systests, :images
24
+
25
+ def initialize
26
+ @id = 0
27
+ @name = ""
28
+ @url = ""
29
+ @source = ""
30
+ @statement = ""
31
+ @definitions = {}
32
+ @notes = []
33
+ @constraints = []
34
+ @examples = []
35
+ @systests = []
36
+ @images = []
37
+ end
38
+ end
39
+ end