codercmp 0.9.7

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.
@@ -0,0 +1,62 @@
1
+ require_relative 'java'
2
+ module CoderCompanion
3
+ module Java
4
+ class JavaParser < CoderCompanion::LanguageParser
5
+
6
+ JAVA_EXTENSIONS = ['.java']
7
+
8
+ def initialize
9
+ Treetop.load(File.join(File.dirname(__FILE__), 'java_grammar'))
10
+ @parser = CoderCompanion::JavaParser.new
11
+ @tree_walker = CoderCompanion::Java::JavaTreeWalker.new
12
+ end
13
+
14
+ def parse(files)
15
+ results = []
16
+ files.each do |file|
17
+ results << parse_file(file)
18
+ end
19
+ return results
20
+ end
21
+
22
+ def parse_file(file)
23
+ CoderCompanion::j_print_inline CoderCompanion::success_format '.'
24
+ data = File.open(file).read
25
+ tree = @parser.parse(data)
26
+
27
+ if tree
28
+ #puts tree.inspect
29
+ end
30
+
31
+ # If the AST is nil then there was an error during parsing
32
+ # we need to report a simple error message to help the user
33
+ # if(tree.nil?)
34
+ # raise Exception, "Parse error at offset: #{@@parser.index}"
35
+ # end
36
+
37
+ if !tree
38
+ #puts @parser.terminal_failures
39
+ error_string = "\nParse error"
40
+ error_string += "\nExpected: #{(@parser.terminal_failures.collect {|f| f.expected_string}).join(", ")}"
41
+ error_string += "\nwhile parsing file: #{file}"
42
+ error_string += "\nat line #{@parser.failure_line}"
43
+ error_string += "\nat column #{@parser.failure_column}"
44
+ error_string += "\nIf your code is compilable, report a bug at:"
45
+ error_string += "\nhttp://codercompanion.com/contact"
46
+ raise CoderCompanion::CoderCompanionException, error_string
47
+ end
48
+
49
+ ast = self.build_tree(tree)
50
+ {:type => 'source', :body => ast, :file_path => file}
51
+ end
52
+
53
+ def is_valid_file(ext)
54
+ return JAVA_EXTENSIONS.include?(ext)
55
+ end
56
+
57
+ def build_tree(root_node)
58
+ root_node.build
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,114 @@
1
+ module CoderCompanion
2
+ module Java
3
+ class JavaTreeWalker < CoderCompanion::TreeWalker
4
+ def is_array_or_list(string)
5
+ ret = ""
6
+ if string.match(/(?<=<)([\w]+)(?=>)/)
7
+ ret = string.match(/(?<=<)([\w]+)(?=>)/)[0]
8
+ elsif string.match(/([\w]+)(?=\[\]\[\])/)
9
+ ret = string.match(/([\w]+)(?=\[\]\[\])/)[0]
10
+ elsif string.match(/([\w]+)(?=\[\])/ )
11
+ ret = string.match(/([\w]+)(?=\[\])/ )[0]
12
+ else
13
+ ret = remove_generics(string)
14
+ end
15
+ ret
16
+ end
17
+
18
+ def remove_generics(value)
19
+ loop do
20
+ has_generics = value.match(/[<>]/)
21
+ if has_generics
22
+ value = value.gsub(/(<[^<>]*>)/, '')
23
+ else
24
+ break
25
+ end
26
+ end
27
+ return value
28
+ end
29
+
30
+ def create_uploadable(results)
31
+ obj = {}
32
+ results.each do |source|
33
+ cache_handler = CoderCompanion.cache_handler
34
+ modified = cache_handler.is_file_modified?(source[:file_path])
35
+ cache_handler.update_cache_path(source[:file_path])
36
+ source[:body].each do |element|
37
+ if (element[:type] == "class_definition" || element[:type] == "enum_definition" || element[:type] == "interface_definition")
38
+ obj[element[:value]] = create_class_obj(element, element[:value], obj, modified)
39
+ obj[element[:value]][:modified] = modified
40
+ end
41
+ end
42
+ end
43
+ return obj
44
+ end
45
+
46
+
47
+
48
+ def create_class_obj(param, name_of_class, obj, modified)
49
+ class_object = {}
50
+ if param[:inherits_from] != ""
51
+ class_object[:inheritsfrom] = [param[:inherits_from]]
52
+ end
53
+ if param[:implements] && param[:implements].length > 0
54
+ class_object[:implements] = param[:implements]
55
+ end
56
+ method_list = []
57
+ variable_list = []
58
+ constant_list = []
59
+ hasa = []
60
+ ownsa = []
61
+ associatedto = []
62
+ enum_constants = []
63
+ class_type = nil
64
+ if param[:type]
65
+ class_type = param[:type].gsub(/_definition/, '')
66
+ end
67
+ param[:class_elements].each do |class_el|
68
+ case class_el[:type]
69
+ when "enum_constant"
70
+ variable_list << class_el[:value]
71
+ when "method_definition"
72
+ method_list << class_el[:value] + ":" + class_el[:return_type]
73
+ if class_el[:params].count > 0
74
+ class_el[:params].each do |p|
75
+ param_type = is_array_or_list(p[:type])
76
+ if !hasa.index(param_type) && !associatedto.index(param_type)
77
+ associatedto << param_type
78
+ end
79
+ end
80
+ end
81
+ when "variable_definition"
82
+ return_type = is_array_or_list(class_el[:return_type])
83
+ variable_type = class_el[:variable_type]
84
+ variable_string = class_el[:value] + ":" + class_el[:return_type]
85
+ if variable_type.match(/constant/)
86
+ constant_list << variable_string
87
+ else
88
+ variable_list << variable_string
89
+ end
90
+ generic_less = remove_generics(return_type)
91
+ hasa << generic_less if !hasa.index(generic_less)
92
+ associatedto - [generic_less] if associatedto.index(generic_less)
93
+
94
+ when "class_definition", "enum_definition", "interface_definition"
95
+ name = name_of_class + "::" + class_el[:value]
96
+ ownsa << name
97
+ associatedto - [name] if associatedto.index(name)
98
+ obj[name] = create_class_obj(class_el, name, obj, modified)
99
+ obj[name][:modified] = modified
100
+ end
101
+ end
102
+ class_object[:classname] = name_of_class
103
+ class_object[:variables] = variable_list
104
+ class_object[:methods] = method_list
105
+ class_object[:constants] = constant_list
106
+ class_object[:hasa] = hasa if hasa.count > 0
107
+ class_object[:ownsa] = ownsa if ownsa.count > 0
108
+ class_object[:isassociatedto] = associatedto if associatedto.count > 0
109
+ class_object[:classtype] = class_type unless class_type.nil?
110
+ return {:classobject => class_object}
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,54 @@
1
+ module CoderCompanion
2
+ class LanguageParser
3
+
4
+ # @return [TreeWalker]
5
+ attr_reader :tree_walker
6
+
7
+ # Parses and creates the json that will be uploaded
8
+ # @param [String] dir_name
9
+ # @return [Hash]
10
+ def create_project_json(dir_name)
11
+ files = get_parseable_files(dir_name)
12
+ CoderCompanion::j_print CoderCompanion::notification_format("Parsing files...")
13
+ results = parse(files)
14
+ CoderCompanion::j_print CoderCompanion::success_format("\nSuccessfully parsed source code")
15
+ tree_walker.create_uploadable(results)
16
+ end
17
+
18
+ # Parse the files
19
+ # @param [Array<String>] files
20
+ def parse(files)
21
+ raise NotImplementedError, "#{self.class}: 'parse' must be overridden"
22
+ end
23
+
24
+ private
25
+
26
+ # @param [String] dir_name
27
+ # @return [Array<String>]
28
+ def get_parseable_files(dir_name)
29
+ files = construct_array(dir_name, [])
30
+ return files
31
+ end
32
+
33
+ def construct_array(dir_name, res_arr)
34
+ Dir.foreach(dir_name) do |item|
35
+ next if item == '.' or item == '..'
36
+ path = File.join(dir_name, item).gsub(/^(\.\/)?/, '') # Remove the leading ./ in the file name
37
+ next if CoderCompanion.excluded.include?(path)
38
+ if File.directory?(path)
39
+ construct_array(path, res_arr)
40
+ else
41
+ if(is_valid_file(File.extname(path) ) )
42
+ CoderCompanion::j_print CoderCompanion::notification_format("Queued for parsing: #{path}")
43
+ res_arr << path
44
+ end
45
+ end
46
+ end
47
+ return res_arr
48
+ end
49
+
50
+ def is_valid_file(ext)
51
+ raise NotImplementedError, "#{self.class}: 'is_valid_file' must be overridden"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ module CoderCompanion
2
+ module Languages
3
+ JAVA = {key: 'Java', annotation_required: false}
4
+ RUBY = {key: 'Ruby', annotation_required: true, valid_annotation: [CoderCompanion::Ruby::RubyAnnotations::YARD]}
5
+ CPP = {key: 'C++', annotation_required: false}
6
+
7
+ class << self
8
+ # Returns the supported languages
9
+ # @return [Array<Hash>]
10
+ def supported_languages
11
+ [JAVA, RUBY] #update this when we support other languages
12
+ end
13
+
14
+ # Tells us if a specific language is supported
15
+ # @param [String] language the language to check
16
+ # @return [Boolean]
17
+ def is_supported?(language)
18
+ supported_languages.each {|lang| return true if lang[:key] == language}
19
+ false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module CoderCompanion
2
+ class ParserFactory
3
+
4
+ private_class_method :new
5
+
6
+ # @param [String] language
7
+ # @return [CoderCompanion::LanguageParser]
8
+ def self.get_parser(language)
9
+ case(language)
10
+ when Languages::RUBY[:key]
11
+ return CoderCompanion::Ruby::RubyParser.new
12
+ =begin #Uncomment when we add c++ and java
13
+ when Languages::CPP[:key]
14
+ return CoderCompanion::Cpp::CppParser.new
15
+ =end
16
+ when Languages::JAVA[:key]
17
+ return CoderCompanion::Java::JavaParser.new
18
+ else
19
+ message = CoderCompanion::error_format("The language (#{language}) specified in your config file is either" +
20
+ " invalid or currently not supported. To see a list of supported languages run: codercmp --language\n") +
21
+ CoderCompanion::error_format("More information at: ") +
22
+ CoderCompanion::error_url_format("http://codercompanion.com/#/how-it-works")
23
+ raise CoderCompanionException.new message
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ module CoderCompanion
2
+ module Ruby
3
+ module RubyAnnotations
4
+ YARD = "Yard"
5
+ RDOC = "RDoc"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,45 @@
1
+ require 'byebug'
2
+ require 'rdoc/rdoc'
3
+ module CoderCompanion
4
+ module Ruby
5
+ class RubyParser < LanguageParser
6
+
7
+ # @return [Array<String>]
8
+ RUBY_EXTENSTIONS = ['.rb']
9
+
10
+ def initialize
11
+ @tree_walker = CoderCompanion::Ruby::RubyTreeWalker.new
12
+ end
13
+
14
+ # @param [Array<String>] files
15
+ # @return [Array<Hash>]
16
+ def parse(files)
17
+ # initialize rdoc if we are parsing with it
18
+ results = []
19
+ files.each do |file|
20
+ results << parse_file(file)
21
+ end
22
+ return results
23
+ end
24
+
25
+ private
26
+
27
+ # Checks if the file with extension is valid for parsing
28
+ #
29
+ # @param [String] ext
30
+ # @return [Boolean]
31
+ def is_valid_file(ext)
32
+ return RUBY_EXTENSTIONS.include?(ext)
33
+ end
34
+
35
+ # @param [String] file
36
+ # @return [Hash]
37
+ def parse_file(file)
38
+ YARD::Registry.clear
39
+ YARD.parse(file)
40
+ results = YARD::Registry.all(:root, :module, :class, :method, :classvariable)
41
+ {:type => 'source', :body => results, :file_path => file}
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,229 @@
1
+ module CoderCompanion
2
+ module Ruby
3
+ class RubyTreeWalker < CoderCompanion::TreeWalker
4
+
5
+ # @param [Array<Hash>] results
6
+ # @return [Hash]
7
+ def create_uploadable(results)
8
+ return build(results)
9
+ end
10
+
11
+ # @param results [Array<Hash>] the results from parsing the code
12
+ # @return [Hash]
13
+ def build(results)
14
+ obj = {}
15
+ results.each do |source|
16
+ cache_handler = CoderCompanion.cache_handler
17
+ modified = cache_handler.is_file_modified?(source[:file_path])
18
+ cache_handler.update_cache_path(source[:file_path])
19
+
20
+ if source[:body][0].root?
21
+ source[:body][0].children.each do |child|
22
+
23
+ # puts child
24
+ if (child.type == :class || child.type == :module)
25
+ child_name = child.path
26
+ obj[child_name] = build_class_or_module(child, modified, obj)
27
+ end
28
+ end
29
+ end
30
+ # puts res
31
+
32
+ end
33
+
34
+ post_process_object(obj)
35
+ #ap obj
36
+ return obj
37
+ end
38
+
39
+
40
+ private
41
+
42
+ # For all inherited update with the fully qualified namespace
43
+ # @param [Hash] object
44
+ # @return [nil]
45
+ def post_process_object(object)
46
+ object.each do |outer_key, outer_value|
47
+ class_object = outer_value[:classobject]
48
+ post_process_class_attribute :inheritsfrom, object, outer_key, outer_value, class_object
49
+ post_process_class_attribute :hasa, object, outer_key, outer_value, class_object
50
+ post_process_class_attribute :isassociatedto, object, outer_key, outer_value, class_object
51
+ end
52
+ end
53
+
54
+ # @param attrib [Symbol] The attribute to post process e.g. inheritsfrom, hasa, etc
55
+ def post_process_class_attribute attrib, object, outer_key, outer_value, class_object
56
+ index = 0
57
+ class_object[attrib].each do |entry|
58
+
59
+ # Keep track of the best fit for inheritance
60
+ most_likely_match = nil
61
+ max = -1
62
+ object.each do |inner_key, inner_value|
63
+ if inner_key.match(/::#{entry}$/)
64
+ new_num = compare_namespaces outer_key, inner_key
65
+
66
+ # If the new comparison has more matches than the previous
67
+ # it becaomes the new most_likely_match
68
+ if new_num > max
69
+ max = new_num
70
+ most_likely_match = inner_key
71
+ end
72
+ end
73
+ end
74
+
75
+ class_object[attrib][index] = most_likely_match if most_likely_match
76
+ index += 1
77
+ end
78
+
79
+ end
80
+
81
+ # Compares an namespaces to determine to correct match for the inheritance
82
+ #
83
+ # @param [String] subclass
84
+ # @param [String] parentclass
85
+ # @return [Number]
86
+ def compare_namespaces subclass, parentclass
87
+ subclass_namespace = subclass.split('::')
88
+ parentclass_namespace = parentclass.split('::')
89
+
90
+ count = 0
91
+ index = 0
92
+ subclass_namespace.each do |section|
93
+ count += 1 if section == parentclass_namespace[index]
94
+ index += 1
95
+ end
96
+
97
+ return count
98
+ end
99
+
100
+ def build_class_or_module(object, modified, ret_obj)
101
+ ret = ret_obj[object.path] ? ret_obj[object.path][:classobject] : {}
102
+ method_list = ret.count > 0 ? ret[:methods] : []
103
+ variable_list = ret.count > 0 ? ret[:variables] : []
104
+ constant_list = ret.count > 0 ? ret[:constants] : []
105
+ hasa = ret.count > 0 ? ret[:hasa] : []
106
+ ownsa = ret.count > 0 ? ret[:ownsa] : []
107
+ isassociatedto = ret.count > 0 ? ret[:isassociatedto] : []
108
+ inherits_from = ret.count > 0 ? ret[:inheritsfrom] : []
109
+ class_type = ret.count > 0 ? ret[:classtype] : object.type
110
+
111
+ i = 0;
112
+
113
+ object.inheritance_tree.each do |obj|
114
+ inherits_from << obj.path.to_s if i != 0
115
+ i += 1
116
+ end
117
+
118
+ object.instance_attributes.each do |key, val|
119
+ # puts key
120
+ type = '*'
121
+ type = val[:read].tag('return').type.to_s if val[:read] && val[:read].tag('return')
122
+ type = val[:write].tag('return').type.to_s if val[:write] && val[:write].tag('return')
123
+ variable_list << key.to_s + ' : ' + type
124
+
125
+ hasa << type
126
+ end
127
+
128
+ object.cvars.each do |cvar|
129
+ return_type = cvar.tag('return') ? ' : ' + cvar.tag('return').type.to_s : ''
130
+ variable_list << cvar.name.to_s.gsub(/@@/, '') + return_type
131
+ end
132
+
133
+ object.constants.each do |constant|
134
+ return_type = constant.tag('return') ? ' : ' + constant.tag("return").type.to_s : ''
135
+ constant_list << constant.name.to_s + return_type
136
+ end
137
+
138
+ object.children.each do |child|
139
+ case child.type
140
+ when :method
141
+ is_instance_attr = false
142
+ is_getter = true
143
+ method_name = child.name
144
+ params = ''
145
+ first_param = true
146
+ return_type = '*'
147
+ object.instance_attributes.each do |key, value|
148
+ # if value[:read] =~ method_name.to_s.gsub('=', '')
149
+ # puts value[:read]
150
+ # end
151
+ if value[:read]
152
+ if value[:read].name.to_s == method_name.to_s
153
+ is_instance_attr = true
154
+ is_getter = true
155
+ break
156
+ end
157
+ end
158
+ if value[:write]
159
+ if value[:write].name.to_s == method_name.to_s
160
+ is_instance_attr = true
161
+ is_getter = false
162
+ break
163
+ end
164
+ end
165
+ end
166
+ parameter_array = child.parameters
167
+ parameter_array.each do |par|
168
+ param_name = par[0]
169
+ type = '*'
170
+
171
+ child.tags('param').each do |str|
172
+ type = str.types[0] if str.name == param_name && str.types
173
+ if type != '*'
174
+ isassociatedto << type if !isassociatedto.index(type) && !ownsa.index(type) && !hasa.index(type)
175
+ break
176
+ end
177
+ end
178
+
179
+ if first_param
180
+ first_param = false
181
+ else
182
+ params += ', '
183
+ end
184
+ params += type + ' ' if type
185
+ params += param_name
186
+
187
+ end
188
+
189
+ # If there is a return type return the types concat'ed by | (ruby doesn't restrict your return type)
190
+ if child.tag('return') && child.tag('return').types
191
+ return_type = child.tag('return') ? child.tag('return').types.join(" | ") : return_type
192
+ end
193
+
194
+ if is_instance_attr && is_getter
195
+ method_list << child.name.to_s + '() : ' + return_type
196
+ elsif is_instance_attr && !is_getter
197
+ param_name = child.parameters[0][0]
198
+ method_list << child.name.to_s + '(' + return_type + ' ' + param_name + ') : nil'
199
+
200
+ isassociatedto.splice(isassociatedto.index(return_type)) if isassociatedto.index(return_type)
201
+ hasa << return_type if !ownsa.index(return_type)
202
+ else
203
+ method_list << child.name.to_s + '(' + params + ') : ' + return_type
204
+ end
205
+
206
+ when :class, :module
207
+ # puts 'here'
208
+ child_name = child.path
209
+
210
+ hasa.splice(hasa.index(child_name)) if hasa.index(child_name)
211
+ isassociatedto.splice(isassociatedto.index(child_name)) if isassociatedto.index(child_name)
212
+ ownsa << child_name
213
+ ret_obj[child_name] = build_class_or_module(child, modified, ret_obj)
214
+ end
215
+ end
216
+ ret[:classname] = object.path
217
+ ret[:inheritsfrom] = inherits_from
218
+ ret[:methods] = method_list
219
+ ret[:variables] = variable_list
220
+ ret[:constants] = constant_list
221
+ ret[:isassociatedto] = isassociatedto
222
+ ret[:hasa] = hasa
223
+ ret[:ownsa] = ownsa
224
+ ret[:classtype] = class_type
225
+ return {:classobject => ret, :modified => modified}
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,32 @@
1
+ module CoderCompanion
2
+ class S3Client
3
+
4
+ # @return [Aws::S3::Client]
5
+ attr_reader :client
6
+
7
+ # @return [Aws::S3::Resource]
8
+ attr_reader :resource
9
+
10
+ # @return [String]
11
+ attr_reader :s3_bucket
12
+
13
+ def initialize params
14
+ @client = Aws::S3::Client.new(
15
+ :access_key_id => params[:access_key_id],
16
+ :secret_access_key => params[:secret_access_key],
17
+ :session_token => params[:session_token],
18
+ :region => params[:region]
19
+ )
20
+ @resource = Aws::S3::Resource.new(client: @client)
21
+ @s3_bucket = params[:s3_bucket]
22
+ end
23
+
24
+ # @param local_file_path [String]
25
+ def put_object local_file_path, key
26
+ path = File.expand_path(local_file_path)
27
+ obj = resource.bucket(s3_bucket).object(key)
28
+ response = obj.upload_file(path, { server_side_encryption: "AES256"})
29
+ return response
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,8 @@
1
+ module CoderCompanion
2
+ class TreeWalker
3
+ # @param [Array<Hash>] results The result hash to be converted into an uploadeable
4
+ def create_uploadable(results)
5
+ raise NotImplementedError, "#{self.class}: 'create_uploadable' must be overriden"
6
+ end
7
+ end
8
+ end