#!/usr/local/bin/ruby # ABC metric # # Assignments, Branches, and Calls # # A simple way to measure the complexity of a function or method. begin require 'rubygems' require_gem 'ParseTree' rescue LoadError require 'parse_tree' end require 'sexp_processor' old_classes = [] ObjectSpace.each_object(Module) do |klass| old_classes << klass end ARGV.each do |name| require name end new_classes = [] ObjectSpace.each_object(Module) do |klass| new_classes << klass end score = {} new_classes -= old_classes klasses = Sexp.from_array(ParseTree.new.parse_tree(*new_classes)) klasses.each do |klass| klass.shift # :class klassname = klass.shift klass.shift # superclass methods = klass methods.each do |defn| a=b=c=0 defn.shift methodname = defn.shift tokens = defn.structure.flatten tokens.each do |token| case token when :attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn then a += 1 when :and, :case, :else, :if, :iter, :or, :rescue, :until, :when, :while then b += 1 when :call, :fcall, :super, :vcall, :yield then c += 1 when :args, :argscat, :array, :begin, :block, :block_arg, :block_pass, :bool, :cfunc, :colon2, :const, :cvar, :defined, :defn, :dregx, :dstr, :dvar, :dxstr, :ensure, :false, :fbody, :gvar, :hash, :ivar, :lit, :long, :lvar, :match2, :match3, :nil, :not, :nth_ref, :return, :scope, :self, :splat, :str, :to_ary, :true, :unknown, :value, :void, :zarray, :zarray, :zclass, :zsuper then # ignore else puts "unhandled token #{token.inspect}" if $VERBOSE end end key = ["#{klassname}.#{methodname}", a, b, c] val = a+b+c score[key] = val end end puts "Method = assignments + branches + calls = total" puts count = 1 ta = tb = tc = tval = 0 score.sort_by { |k,v| v }.reverse.each do |key,val| name, a, b, c = *key ta += a tb += b tc += c tval += val printf "%3d) %-50s = %2d + %2d + %2d = %3d\n", count, name, a, b, c, val count += 1 end printf "%3d) %-50s = %2d + %2d + %2d = %3d\n", count, "Total", ta, tb, tc, tval