All lexems (rules ~ SLangParser.y) described by objects (OOP approach). At the result of parsing we will have "object tree".
For example:
IfStatement will contain 3 objects:
> ifBlock (condition is true),
> elseBlock (condition is false),
> elsifBlocks[] collection.
Classical metric considering number of decisions to be made on possible branching, more precisely – number of possible paths through the code. This metric may be calculated in respect to the block of code (sequence of statements).
This metric is based on number of possible paths considering branching in our program, which is represented by if
and loop
statements. Each if
statement consists of block executing if condition is true (minimum 1 path) and if condition is false (minimum 1 path, may be ommitted if no actions could be done in it). Also, we may have some number of elsif
sub-blocks. Each sub-block of a given if
statement ("on true", "on false", "else if"s) has it's own complexity, calculated earlier.
CC of block = multiplication of complexities of consequent branchings on the same level of nesting (if no branching in the block: CC = 1).
CC of if
= sum of complexities of each sub-block.
CC of while
= 1 (condition is initially false) + complexity of sub-block of main loop body
partial class Block
{
int CC;
void CalcCC()
{
int CC = 1;
foreach (Statement s in this.statements)
{
if (IsBranching(s)) // If or While
{
CC *= s.CC;
}
}
this.CC = CC;
}
}
partial class If
{
int CC;
void CalcCC()
{
int CC = this.mainBlock.CC;
if (HasElseBlock(this))
{
CC += this.elseBlock.CC;
}
foreach (Block b in this.elsifBlocks)
{
CC += b.CC;
}
this.CC = CC;
}
}
partial class Loop
{
int CC;
void CalcCC()
{
int CC = 1 + this.loopBlock.CC;
this.CC = CC;
}
}
It is a compound measure taking account of both size and vocabulary.
Program complexity is assumed as a function of the number of unique operators and operands, and the total number of occurrences, formally:
It could be implemented by adding static field in each significant for rule class (decide by yourself, is using one common predecessor's field applicable for your case, because this approach has self pros and cons in terms of reusability by other metrics). At the end of the parsing we can just count
...
static int creationCounter = 0;
...
CoolOperator() {
creationCounter += 1;
...
}
...
This is the sum of the complexities of all the routines within considered unit.
Calculation is based on the results of calculation of CC.
The complexity of inner units are not considered in the parent unit.
partial class Unit
{
int WMU;
void CalcWMU()
{
int WMU = 0;
foreach (UnitMember m in this.members)
{
if (IsRoutine(m))
{
WMU += m.cc;
}
}
this.WMU = WMU;
}
}
See
Weighted Methods per Unit (WMU)
, but considered just for methods without access level specifiers.
This metric measures the position on a unit within the inheritance hierarchy.
Calculated after parsing is complete.
partial class Unit
{
List<Unit> parents; // collected during parsing
List<Unit> children; // initially empty
Set<Unit> descendants { get; };
/// <summary>
/// Go through inheritance tree and calculate set
/// of descendants for each node.
/// After that we can get number of descendants
/// for each node.
/// </summary>
void FindDescendants()
{
descendants.UnionWith(children);
foreach (Unit c in children)
{
c.FindDescendants();
descendants.UnionWith(c.descendants);
}
}
// Probably deprecated due to newer method with
int CalcDepth()
{
if (!children.Any())
{
return 1;
}
else
{
int max = 0;
foreach (Unit c in children)
{
int current = c.CalcDepth();
if (max < current)
{
max = current;
}
}
return max;
}
}
void InformParents()
{
foreach (Unit p in parents)
{
p.AddChild(this);
p.InformParents();
}
}
void AddChild(ref Unit u)
{
children.Add(u);
}
/// <summary>
/// Reverse function finding paths from root to
/// all childs that will give all routes in
/// inheritance tree.
/// Instead of finding all paths from root node, we find
/// paths to root node from each of leaf nodes.
/// </summary>
List<List<Unit>> inheritancePaths()
{
List<List<Unit>> allPaths;
List<Unit> leafNodes = GetLeafNodes();
foreach (Unit leaf in leafNodes)
{
allPaths.UnionWith(leaf.pathsToLeaf());
}
int minDepth = Integer.MAX_VALUE;
int maxDepth = 0;
int averageDepth = 0;
foreach(List<Unit> path in allPaths)
{
if(path.Count<minDepth){
minDepth = path.Count;
}
if(path.Count>maxDepth){
maxDepth = path.Count;
}
averageDepth+=path.Count;
}
averageDepth /= allPaths.Count;
//TODO: return / use calculated values
return allPaths;
}
List<List<Unit>> pathsToLeaf()
{
if (!parents.Any())
{
return new List<List<Unit>>();
}
else
{
List<List<Unit>> childPaths;
foreach (Unit c in parents)
{
childPaths.UnionWith(c.pathsToLeaf());
}
foreach (List<Unit> path in childPaths)
{
path.Add(this);
}
return childPaths;
}
}
}
As the name states, this metric counts the number of direct childs or sub-units for this unit.
Calculated in DIT.
Measures the total number of units to which a given unit is linked.
Probably not our work (it is for semantic analysis)
partial class Unit
{
//
}
Number of member routines that can be executed in response to a message received by that unit, summed for all distinct messages.
Probably not our work (it is for semantic analysis)
partial class Unit
{
//
}
How much sharing of data members occurs between the member routines of the unit. More sharing means higher cohesion.
Probably not our work (it is for semantic analysis)
partial class Unit
{
//
}
Maximum height of hierarchy of nested structures. MHH of the module is MHH of it's block of statements (outer scope).
partial class Unit
{
int MHH;
void CalcMHH()
{
int membersMHH = 0;
foreach (UnitMember m in this.members)
{
if (IsNesting(m) && membersMHH < m.MHH)
{
membersMHH = m.MHH;
}
}
this.MHH = membersMHH + 1;
}
}
partial class Routine
{
int MHH;
void CalcMHH()
{
this.MHH = this.block.MHH;
}
}
partial class Block
{
int MHH;
void CalcMHH()
{
int statementsMHH = 0;
foreach (Statement s in this.statements)
{
if (IsNesting(s) && statementsMHH < s.MHH)
{
statementsMHH = s.MHH;
}
}
this.MHH = statementsMHH + 1;
}
}
// Same things for each nested statement...
Average height of hierarchy of nested structures.
partial class Unit
{
// TODO
// Similar to inheritance hierarchy claculation but
}
Average count of units inherited from given unit.
Can be attached to DIT calculation
Number of units from another module that depend on units from given module.
Probably Calculated in semantic analysis
Number of units that use given class.
Probably Calculated in semantic analysis
elements of language
Number of Units, Routines, Attributes (w/ default, hidden, hidden final)
Number of abstract Units
Number of concrete Units
Number of public fields and routines of unit
Number of all fields and routins of unit
Number of statements
Number of declarations
Number of units
Number of routines
Number of all public routines
Number of all fields and routines of all units
Implemented as counters in parser
Calculated during lexical analysis
Calculated in parser
This metric includes looking for routines usage in program execution path. Need semantics to be considered.