# SLang Metrics
## Implementation approach (TODO: translate)
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.
```
## Metrics possible for collection
### Cyclomatic complexity (CC)
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).
#### Pseudocode (description):
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
```C#
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;
}
}
```
### Software Sciences (SS)
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:
> $$V = N*log_2n$$
$N$ -- total number of occurrences of operators $+$ operands
$n$ -- number of unique operators $+$ operands (the vocabulary)
#### Pseudocode (description): (TODO: review)
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 $N$ and $n$ at the significant classes and get SS.
```C#
...
static int creationCounter = 0;
...
CoolOperator() {
creationCounter += 1;
...
}
...
```
### Weighted Routines per Unit (WRU)
This is the sum of the complexities of all the routines within considered unit.
#### Pseudocode (description):
Calculation is based on the results of calculation of CC.
The complexity of inner units are not considered in the parent unit.
```C#
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;
}
}
```
### Non-Private Weighted Routines per Unit
> See `Weighted Methods per Unit (WMU)`, but considered just for methods without access level specifiers.
### Depth of Inheritance Tree (DIT)
This metric measures the position on a unit within the inheritance hierarchy.
#### Pseudocode (description):
Calculated after parsing is complete.
```C#
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;
}
}
}
```
### Number of Descendants (NOD)
As the name states, this metric counts the number of direct childs or sub-units for this unit.
Calculated in DIT.
### Coupling between Object units (CBO)
Measures the total number of units to which a given unit is linked.
_Probably not our work (it is for semantic analysis)_
#### Pseudocode (description):
```C#
partial class Unit
{
//
}
```
### Response For a Class (RFC)
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)_
#### Pseudocode (description):
```C#
partial class Unit
{
//
}
```
### Lack of Cohesion in Methods (LCOM)
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)_
#### Pseudocode (description):
```C#
partial class Unit
{
//
}
```
### Maximum Hierarchy Height (MHH)
Maximum height of hierarchy of nested structures. MHH of the module is MHH of it's block of statements (outer scope).
#### Pseudocode (description):
```C#
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 Hierarchy Height (AHH)
Average height of hierarchy of nested structures.
#### Pseudocode (description):
```C#
partial class Unit
{
// TODO
// Similar to inheritance hierarchy claculation but
}
```
### Average Number of Derived Units
Average count of units inherited from given unit.
> Can be attached to DIT calculation
### Afferent Coupling
Number of units from another module that depend on units from given module.
> Probably Calculated in semantic analysis
### Efferent Coupling
Number of units that use given class.
> Probably Calculated in semantic analysis
### Number of `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
### Program size: lines of code, number of symbols, commented lines
> Calculated during lexical analysis
### Number Of Overwritten Routines
> Calculated in parser
### NPath Complexity
> This metric includes looking for routines usage in program execution path. Need semantics to be considered.
## References
1. Measuring Complexity in C++ Application
Software -- f. g. wilkie and b. hylands, 1998 https://onlinelibrary.wiley.com/doi/abs/10.1002/%28SICI%291097-024X%2819980425%2928%3A5%3C513%3A%3AAID-SPE165%3E3.0.CO%3B2-H
2. PHP metrics -- https://codengineering.ru/post/41
3. Maintainanility Index http://www.projectcodemeter.com/cost_estimation/help/GL_maintainability.htm
4. Software Sciences http://www.projectcodemeter.com/cost_estimation/help/GL_halstead.htm