codercmp 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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