metric_fu-Saikuro 1.1.1.0 → 1.1.2

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,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