metric_fu-Saikuro 1.1.1.0 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ class BaseFormater
2
+ attr_accessor :warnings, :errors, :current
3
+
4
+ def initialize(out, filter = nil)
5
+ @out = out
6
+ @filter = filter
7
+ reset_data
8
+ end
9
+
10
+ def warn_error?(num, marker)
11
+ klass = ""
12
+
13
+ if @filter.error?(num)
14
+ klass = ' class="error"'
15
+ @errors<< [@current, marker, num]
16
+ elsif @filter.warn?(num)
17
+ klass = ' class="warning"'
18
+ @warnings<< [@current, marker, num]
19
+ end
20
+
21
+ klass
22
+ end
23
+
24
+ def reset_data
25
+ @warnings = Array.new
26
+ @errors = Array.new
27
+ @current = ""
28
+ end
29
+
30
+ end
@@ -0,0 +1,12 @@
1
+ class EndableParseState < ParseState
2
+ def initialize(lexer,parent=nil)
3
+ super(lexer,parent)
4
+ STDOUT.puts "Starting #{self.class}" if $VERBOSE
5
+ end
6
+
7
+ def do_end_token(token)
8
+ end_debug
9
+ @run = false
10
+ nil
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ class Filter
2
+ attr_accessor :limit, :error, :warn
3
+
4
+ def initialize(limit = -1, error = 11, warn = 8)
5
+ @limit = limit
6
+ @error = error
7
+ @warn = warn
8
+ end
9
+
10
+ def ignore?(count)
11
+ count < @limit
12
+ end
13
+
14
+ def warn?(count)
15
+ count >= @warn
16
+ end
17
+
18
+ def error?(count)
19
+ count >= @error
20
+ end
21
+
22
+ end
@@ -0,0 +1,82 @@
1
+ module HTMLStyleSheet
2
+ def HTMLStyleSheet.style_sheet
3
+ out = StringIO.new
4
+
5
+ out.puts "<style>"
6
+ out.puts 'body {'
7
+ out.puts ' margin: 20px;'
8
+ out.puts ' padding: 0;'
9
+ out.puts ' font-size: 12px;'
10
+ out.puts ' font-family: bitstream vera sans, verdana, arial, sans serif;'
11
+ out.puts ' background-color: #efefef;'
12
+ out.puts '}'
13
+ out.puts ''
14
+ out.puts 'table { '
15
+ out.puts ' border-collapse: collapse;'
16
+ out.puts ' /*border-spacing: 0;*/'
17
+ out.puts ' border: 1px solid #666;'
18
+ out.puts ' background-color: #fff;'
19
+ out.puts ' margin-bottom: 20px;'
20
+ out.puts '}'
21
+ out.puts ''
22
+ out.puts 'table, th, th+th, td, td+td {'
23
+ out.puts ' border: 1px solid #ccc;'
24
+ out.puts '}'
25
+ out.puts ''
26
+ out.puts 'table th {'
27
+ out.puts ' font-size: 12px;'
28
+ out.puts ' color: #fc0;'
29
+ out.puts ' padding: 4px 0;'
30
+ out.puts ' background-color: #336;'
31
+ out.puts '}'
32
+ out.puts ''
33
+ out.puts 'th, td {'
34
+ out.puts ' padding: 4px 10px;'
35
+ out.puts '}'
36
+ out.puts ''
37
+ out.puts 'td { '
38
+ out.puts ' font-size: 13px;'
39
+ out.puts '}'
40
+ out.puts ''
41
+ out.puts '.class_name {'
42
+ out.puts ' font-size: 17px;'
43
+ out.puts ' margin: 20px 0 0;'
44
+ out.puts '}'
45
+ out.puts ''
46
+ out.puts '.class_complexity {'
47
+ out.puts 'margin: 0 auto;'
48
+ out.puts '}'
49
+ out.puts ''
50
+ out.puts '.class_complexity>.class_complexity {'
51
+ out.puts ' margin: 0;'
52
+ out.puts '}'
53
+ out.puts ''
54
+ out.puts '.class_total_complexity, .class_total_lines, .start_token_count, .file_count {'
55
+ out.puts ' font-size: 13px;'
56
+ out.puts ' font-weight: bold;'
57
+ out.puts '}'
58
+ out.puts ''
59
+ out.puts '.class_total_complexity, .class_total_lines {'
60
+ out.puts ' color: #c00;'
61
+ out.puts '}'
62
+ out.puts ''
63
+ out.puts '.start_token_count, .file_count {'
64
+ out.puts ' color: #333;'
65
+ out.puts '}'
66
+ out.puts ''
67
+ out.puts '.warning {'
68
+ out.puts ' background-color: yellow;'
69
+ out.puts '}'
70
+ out.puts ''
71
+ out.puts '.error {'
72
+ out.puts ' background-color: #f00;'
73
+ out.puts '}'
74
+ out.puts "</style>"
75
+
76
+ out.string
77
+ end
78
+
79
+ def style_sheet
80
+ HTMLStyleSheet.style_sheet
81
+ end
82
+ end
@@ -0,0 +1,45 @@
1
+ class HTMLTokenCounterFormater < TokenCounterFormater
2
+ include HTMLStyleSheet
3
+
4
+ def start(new_out=nil)
5
+ reset_data
6
+ @out = new_out if new_out
7
+ @out.puts "<html>"
8
+ @out.puts style_sheet
9
+ @out.puts "<body>"
10
+ end
11
+
12
+ def start_count(number_of_files)
13
+ @out.puts "<div class=\"start_token_count\">"
14
+ @out.puts "Number of files: #{number_of_files}"
15
+ @out.puts "</div>"
16
+ end
17
+
18
+ def start_file(file_name)
19
+ @current = file_name
20
+ @out.puts "<div class=\"file_count\">"
21
+ @out.puts "<p class=\"file_name\">"
22
+ @out.puts "File: #{file_name}"
23
+ @out.puts "</p>"
24
+ @out.puts "<table width=\"100%\" border=\"1\">"
25
+ @out.puts "<tr><th>Line</th><th>Tokens</th></tr>"
26
+ end
27
+
28
+ def line_token_count(line_number,number_of_tokens)
29
+ return if @filter.ignore?(number_of_tokens)
30
+ klass = warn_error?(number_of_tokens, line_number)
31
+ @out.puts "<tr><td>#{line_number}</td><td#{klass}>#{number_of_tokens}</td></tr>"
32
+ end
33
+
34
+ def end_file
35
+ @out.puts "</table>"
36
+ end
37
+
38
+ def end_count
39
+ end
40
+
41
+ def end
42
+ @out.puts "</body>"
43
+ @out.puts "</html>"
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ class ParseBlock < EndableParseState
2
+
3
+ def initialize(lexer,parent=nil)
4
+ super(lexer,parent)
5
+ @complexity = 1
6
+ @lbraces = Array.new
7
+ end
8
+
9
+ # Because the token for a block and hash right brace is the same,
10
+ # we need to track the hash left braces to determine when an end is
11
+ # encountered.
12
+ def parse_token(token)
13
+ if token.is_a?(TkLBRACE)
14
+ @lbraces.push(true)
15
+ elsif token.is_a?(TkRBRACE)
16
+ if @lbraces.empty?
17
+ do_right_brace_token(token)
18
+ #do_end_token(token)
19
+ else
20
+ @lbraces.pop
21
+ end
22
+ else
23
+ super(token)
24
+ end
25
+ end
26
+
27
+ def do_right_brace_token(token)
28
+ # we are done ? what about a hash in a block :-/
29
+ @run = false
30
+ nil
31
+ end
32
+
33
+ end
@@ -0,0 +1,27 @@
1
+ class ParseClass < EndableParseState
2
+ def initialize(lexer,parent=nil)
3
+ super(lexer,parent)
4
+ @type_name = "Class"
5
+ end
6
+
7
+ def do_constant_token(token)
8
+ @name = token.name if @name.empty?
9
+ nil
10
+ end
11
+
12
+ def compute_state(formater)
13
+ # Seperate the Module and Class Children out
14
+ cnm_children, @children = @children.partition do |child|
15
+ child.kind_of?(ParseClass)
16
+ end
17
+
18
+ formater.start_class_compute_state(@type_name,@name,self.calc_complexity,self.calc_lines)
19
+ super(formater)
20
+ formater.end_class_compute_state(@name)
21
+
22
+ cnm_children.each do |child|
23
+ child.name = @name + "::" + child.name
24
+ child.compute_state(formater)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # Read and consume tokens in comments until a new line.
2
+ class ParseComment < ParseState
3
+
4
+ # While in a comment state do not count the tokens.
5
+ def count_tokens?
6
+ false
7
+ end
8
+
9
+ def parse_token(token)
10
+ if token.is_a?(TkNL)
11
+ @lines += 1
12
+ @run = false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ class ParseCond < EndableParseState
2
+ def initialize(lexer,parent=nil)
3
+ super(lexer,parent)
4
+ @complexity = 1
5
+ end
6
+ end
@@ -0,0 +1,59 @@
1
+ class ParseDef < EndableParseState
2
+
3
+ def initialize(lexer,parent=nil)
4
+ super(lexer,parent)
5
+ @complexity = 1
6
+ @looking_for_name = true
7
+ @first_space = true
8
+ end
9
+
10
+ # This way I don't need to list all possible overload
11
+ # tokens.
12
+ def create_def_name(token)
13
+ case token
14
+ when TkSPACE
15
+ # mark first space so we can stop at next space
16
+ if @first_space
17
+ @first_space = false
18
+ else
19
+ @looking_for_name = false
20
+ end
21
+ when TkNL,TkLPAREN,TkfLPAREN,TkSEMICOLON
22
+ # we can also stop at a new line or left parenthesis
23
+ @looking_for_name = false
24
+ when TkDOT
25
+ @name<< "."
26
+ when TkCOLON2
27
+ @name<< "::"
28
+ when TkASSIGN
29
+ @name<< "="
30
+ when TkfLBRACK
31
+ @name<< "["
32
+ when TkRBRACK
33
+ @name<< "]"
34
+ else
35
+ begin
36
+ @name<< token.name.to_s
37
+ rescue Exception => err
38
+ #what is this?
39
+ STDOUT.puts @@token_counter.current_file
40
+ STDOUT.puts @name
41
+ STDOUT.puts token.inspect
42
+ STDOUT.puts err.message
43
+ exit 1
44
+ end
45
+ end
46
+ end
47
+
48
+ def parse_token(token)
49
+ if @looking_for_name
50
+ create_def_name(token)
51
+ end
52
+ super(token)
53
+ end
54
+
55
+ def compute_state(formater)
56
+ formater.def_compute_state(@name, self.calc_complexity, self.calc_lines)
57
+ super(formater)
58
+ end
59
+ end
@@ -0,0 +1,24 @@
1
+ class ParseDoCond < ParseCond
2
+ def initialize(lexer,parent=nil)
3
+ super(lexer,parent)
4
+ @looking_for_new_line = true
5
+ end
6
+
7
+ # Need to consume the do that can appear at the
8
+ # end of these control structures.
9
+ def parse_token(token)
10
+ if @looking_for_new_line
11
+ if token.is_a?(TkDO)
12
+ nil
13
+ else
14
+ if token.is_a?(TkNL)
15
+ @looking_for_new_line = false
16
+ end
17
+ super(token)
18
+ end
19
+ else
20
+ super(token)
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,6 @@
1
+ class ParseModule < ParseClass
2
+ def initialize(lexer,parent=nil)
3
+ super(lexer,parent)
4
+ @type_name = "Module"
5
+ end
6
+ end
@@ -0,0 +1,286 @@
1
+ # Main class and structure used to compute the
2
+ # cyclomatic complexity of Ruby programs.
3
+ class ParseState
4
+ include RubyToken
5
+ attr_accessor :name, :children, :complexity, :parent, :lines
6
+
7
+ @@top_state = nil
8
+ def ParseState.make_top_state()
9
+ @@top_state = ParseState.new(nil)
10
+ @@top_state.name = "__top__"
11
+ @@top_state
12
+ end
13
+
14
+ @@token_counter = TokenCounter.new
15
+ def ParseState.set_token_counter(counter)
16
+ @@token_counter = counter
17
+ end
18
+ def ParseState.get_token_counter
19
+ @@token_counter
20
+ end
21
+
22
+ def initialize(lexer,parent=nil)
23
+ @name = ""
24
+ @children = Array.new
25
+ @complexity = 0
26
+ @parent = parent
27
+ @lexer = lexer
28
+ @run = true
29
+ # To catch one line def statements, We always have one line.
30
+ @lines = 0
31
+ @last_token_line_and_char = Array.new
32
+ end
33
+
34
+ def top_state?
35
+ self == @@top_state
36
+ end
37
+
38
+ def lexer=(lexer)
39
+ @run = true
40
+ @lexer = lexer
41
+ end
42
+
43
+ def make_state(type,parent = nil)
44
+ cstate = type.new(@lexer,self)
45
+ parent.children<< cstate
46
+ cstate
47
+ end
48
+
49
+ def calc_complexity
50
+ complexity = @complexity
51
+ children.each do |child|
52
+ complexity += child.calc_complexity
53
+ end
54
+ complexity
55
+ end
56
+
57
+ def calc_lines
58
+ lines = @lines
59
+ children.each do |child|
60
+ lines += child.calc_lines
61
+ end
62
+ lines
63
+ end
64
+
65
+ def compute_state(formater)
66
+ if top_state?
67
+ compute_state_for_global(formater)
68
+ end
69
+
70
+ @children.each do |s|
71
+ s.compute_state(formater)
72
+ end
73
+ end
74
+
75
+ def compute_state_for_global(formater)
76
+ global_def, @children = @children.partition do |s|
77
+ !s.kind_of?(ParseClass)
78
+ end
79
+ return if global_def.empty?
80
+ gx = global_def.inject(0) { |c,s| s.calc_complexity }
81
+ gl = global_def.inject(0) { |c,s| s.calc_lines }
82
+ formater.start_class_compute_state("Global", "", gx, gl)
83
+ global_def.each do |s|
84
+ s.compute_state(formater)
85
+ end
86
+ formater.end_class_compute_state("")
87
+ end
88
+
89
+ # Count the tokens parsed if true else ignore them.
90
+ def count_tokens?
91
+ true
92
+ end
93
+
94
+ def parse
95
+ while @run do
96
+ tok = @lexer.token
97
+ @run = false if tok.nil?
98
+ if lexer_loop?(tok)
99
+ STDERR.puts "Lexer loop at line : #{@lexer.line_no} char #{@lexer.char_no}."
100
+ @run = false
101
+ end
102
+ @last_token_line_and_char<< [@lexer.line_no.to_i, @lexer.char_no.to_i, tok]
103
+ if $VERBOSE
104
+ puts "DEBUG: #{@lexer.line_no} #{tok.class}:#{tok.name if tok.respond_to?(:name)}"
105
+ end
106
+ @@token_counter.count_token(@lexer.line_no, tok) if count_tokens?
107
+ parse_token(tok)
108
+ end
109
+ end
110
+
111
+ # Ruby-Lexer can go into a loop if the file does not end with a newline.
112
+ def lexer_loop?(token)
113
+ return false if @last_token_line_and_char.empty?
114
+ loop_flag = false
115
+ last = @last_token_line_and_char.last
116
+ line = last[0]
117
+ char = last[1]
118
+ ltok = last[2]
119
+
120
+ if ( (line == @lexer.line_no.to_i) &&
121
+ (char == @lexer.char_no.to_i) &&
122
+ (ltok.class == token.class) )
123
+ # We are potentially in a loop
124
+ if @last_token_line_and_char.size >= 3
125
+ loop_flag = true
126
+ end
127
+ else
128
+ # Not in a loop so clear stack
129
+ @last_token_line_and_char = Array.new
130
+ end
131
+
132
+ loop_flag
133
+ end
134
+
135
+ def do_begin_token(token)
136
+ make_state(EndableParseState, self)
137
+ end
138
+
139
+ def do_class_token(token)
140
+ make_state(ParseClass,self)
141
+ end
142
+
143
+ def do_module_token(token)
144
+ make_state(ParseModule,self)
145
+ end
146
+
147
+ def do_def_token(token)
148
+ make_state(ParseDef,self)
149
+ end
150
+
151
+ def do_constant_token(token)
152
+ nil
153
+ end
154
+
155
+ def do_identifier_token(token)
156
+ if (token.name == "__END__" && token.char_no.to_i == 0)
157
+ # The Ruby code has stopped and the rest is data so cease parsing.
158
+ @run = false
159
+ end
160
+ nil
161
+ end
162
+
163
+ def do_right_brace_token(token)
164
+ nil
165
+ end
166
+
167
+ def do_end_token(token)
168
+ end_debug
169
+ nil
170
+ end
171
+
172
+ def do_block_token(token)
173
+ make_state(ParseBlock,self)
174
+ end
175
+
176
+ def do_conditional_token(token)
177
+ make_state(ParseCond,self)
178
+ end
179
+
180
+ def do_conditional_do_control_token(token)
181
+ make_state(ParseDoCond,self)
182
+ end
183
+
184
+ def do_case_token(token)
185
+ make_state(EndableParseState, self)
186
+ end
187
+
188
+ def do_one_line_conditional_token(token)
189
+ # This is an if with no end
190
+ @complexity += 1
191
+ #STDOUT.puts "got IF_MOD: #{self.to_yaml}" if $VERBOSE
192
+ #if state.type != "class" && state.type != "def" && state.type != "cond"
193
+ #STDOUT.puts "Changing IF_MOD Parent" if $VERBOSE
194
+ #state = state.parent
195
+ #@run = false
196
+ nil
197
+ end
198
+
199
+ def do_else_token(token)
200
+ STDOUT.puts "Ignored/Unknown Token:#{token.class}" if $VERBOSE
201
+ nil
202
+ end
203
+
204
+ def do_comment_token(token)
205
+ make_state(ParseComment, self)
206
+ end
207
+
208
+ def do_symbol_token(token)
209
+ make_state(ParseSymbol, self)
210
+ end
211
+
212
+ def parse_token(token)
213
+ state = nil
214
+ case token
215
+ when TkCLASS
216
+ state = do_class_token(token)
217
+ when TkMODULE
218
+ state = do_module_token(token)
219
+ when TkDEF
220
+ state = do_def_token(token)
221
+ when TkCONSTANT
222
+ # Nothing to do with a constant at top level?
223
+ state = do_constant_token(token)
224
+ when TkIDENTIFIER,TkFID
225
+ # Nothing to do at top level?
226
+ state = do_identifier_token(token)
227
+ when TkRBRACE
228
+ # Nothing to do at top level
229
+ state = do_right_brace_token(token)
230
+ when TkEND
231
+ state = do_end_token(token)
232
+ # At top level this might be an error...
233
+ when TkDO,TkfLBRACE
234
+ state = do_block_token(token)
235
+ when TkIF,TkUNLESS
236
+ state = do_conditional_token(token)
237
+ when TkWHILE,TkUNTIL,TkFOR
238
+ state = do_conditional_do_control_token(token)
239
+ when TkELSIF #,TkELSE
240
+ @complexity += 1
241
+ when TkELSE
242
+ # Else does not increase complexity
243
+ when TkCASE
244
+ state = do_case_token(token)
245
+ when TkWHEN
246
+ @complexity += 1
247
+ when TkBEGIN
248
+ state = do_begin_token(token)
249
+ when TkRESCUE
250
+ # Maybe this should add complexity and not begin
251
+ @complexity += 1
252
+ when TkIF_MOD, TkUNLESS_MOD, TkUNTIL_MOD, TkWHILE_MOD, TkQUESTION
253
+ state = do_one_line_conditional_token(token)
254
+ when TkNL
255
+ #
256
+ @lines += 1
257
+ when TkRETURN
258
+ # Early returns do not increase complexity as the condition that
259
+ # calls the return is the one that increases it.
260
+ when TkCOMMENT
261
+ state = do_comment_token(token)
262
+ when TkSYMBEG
263
+ state = do_symbol_token(token)
264
+ when TkError
265
+ STDOUT.puts "Lexer received an error for line #{@lexer.line_no} char #{@lexer.char_no}"
266
+ else
267
+ state = do_else_token(token)
268
+ end
269
+ state.parse if state
270
+ end
271
+
272
+ def end_debug
273
+ STDOUT.puts "got an end: #{@name} in #{self.class.name}" if $VERBOSE
274
+ if @parent.nil?
275
+ STDOUT.puts "DEBUG: Line #{@lexer.line_no}"
276
+ STDOUT.puts "DEBUG: #{@name}; #{self.class}"
277
+ # to_yaml can cause an infinite loop?
278
+ #STDOUT.puts "TOP: #{@@top_state.to_yaml}"
279
+ #STDOUT.puts "TOP: #{@@top_state.inspect}"
280
+
281
+ # This may not be an error?
282
+ #exit 1
283
+ end
284
+ end
285
+
286
+ end