Skip to content

Commit 1ad7f4c

Browse files
authored
Update Halstead (#115)
* Update Halstead metric * Print additional Halstead metrics * Update tests
1 parent 07329a1 commit 1ad7f4c

3 files changed

Lines changed: 108 additions & 56 deletions

File tree

src/halstead.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ impl Serialize for Stats<'_> {
1919
where
2020
S: Serializer,
2121
{
22-
let mut st = serializer.serialize_struct("halstead", 12)?;
23-
st.serialize_field("unique_operands", &self.u_operands())?;
24-
st.serialize_field("operands", &self.operands())?;
25-
st.serialize_field("unique_operators", &self.u_operators())?;
26-
st.serialize_field("operators", &self.operators())?;
22+
let mut st = serializer.serialize_struct("halstead", 14)?;
23+
st.serialize_field("n1", &self.u_operators())?;
24+
st.serialize_field("N1", &self.operators())?;
25+
st.serialize_field("n2", &self.u_operands())?;
26+
st.serialize_field("N2", &self.operands())?;
2727
st.serialize_field("length", &self.length())?;
28-
st.serialize_field("size", &self.size())?;
28+
st.serialize_field("estimated_program_length", &self.estimated_program_length())?;
29+
st.serialize_field("purity_ratio", &self.purity_ratio())?;
30+
st.serialize_field("vocabulary", &self.vocabulary())?;
2931
st.serialize_field("volume", &self.volume())?;
3032
st.serialize_field("difficulty", &self.difficulty())?;
3133
st.serialize_field("level", &self.level())?;
@@ -38,7 +40,37 @@ impl Serialize for Stats<'_> {
3840

3941
impl<'a> fmt::Display for Stats<'a> {
4042
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41-
write!(f, "unique operands: {}, operands: {}, unique operators: {}, operators: {}, length: {}, size: {}, volume: {}, difficulty: {}, level: {}, effort: {}, time: {}, bugs: {}", self.u_operands(), self.operands(), self.u_operators(), self.operators(), self.length(), self.size(), self.volume(), self.difficulty(), self.level(), self.effort(), self.time(), self.bugs())
43+
write!(
44+
f,
45+
"n1: {}, \
46+
N1: {}, \
47+
n2: {}, \
48+
N2: {}, \
49+
length: {}, \
50+
estimated program length: {}, \
51+
purity ratio: {}, \
52+
size: {}, \
53+
volume: {}, \
54+
difficulty: {}, \
55+
level: {}, \
56+
effort: {}, \
57+
time: {}, \
58+
bugs: {}",
59+
self.u_operators(),
60+
self.operators(),
61+
self.u_operands(),
62+
self.operands(),
63+
self.length(),
64+
self.estimated_program_length(),
65+
self.purity_ratio(),
66+
self.vocabulary(),
67+
self.volume(),
68+
self.difficulty(),
69+
self.level(),
70+
self.effort(),
71+
self.time(),
72+
self.bugs(),
73+
)
4274
}
4375
}
4476

@@ -78,13 +110,24 @@ impl<'a> Stats<'a> {
78110
}
79111

80112
#[inline(always)]
81-
pub fn size(&self) -> f64 {
113+
pub fn estimated_program_length(&self) -> f64 {
114+
self.u_operators() * self.u_operators().log2()
115+
+ self.u_operands() * self.u_operands().log2()
116+
}
117+
118+
#[inline(always)]
119+
pub fn purity_ratio(&self) -> f64 {
120+
self.estimated_program_length() / self.length()
121+
}
122+
123+
#[inline(always)]
124+
pub fn vocabulary(&self) -> f64 {
82125
self.u_operands() + self.u_operators()
83126
}
84127

85128
#[inline(always)]
86129
pub fn volume(&self) -> f64 {
87-
self.length() * self.size().log2()
130+
self.length() * self.vocabulary().log2()
88131
}
89132

90133
#[inline(always)]
@@ -268,7 +311,7 @@ impl Halstead for RustCode {
268311
| LT | GT | AMP | MutableSpecifier | DOTDOT | DOTDOTEQ | DASH | AMPAMP | PIPEPIPE
269312
| PIPE | CARET | EQEQ | BANGEQ | LTEQ | GTEQ | LTLT | GTGT | SLASH | PERCENT
270313
| PLUSEQ | DASHEQ | STAREQ | SLASHEQ | PERCENTEQ | AMPEQ | PIPEEQ | CARETEQ
271-
| LTLTEQ | GTGTEQ | Move | DOT => {
314+
| LTLTEQ | GTGTEQ | Move | DOT | PrimitiveType => {
272315
*stats.operators.entry(id).or_insert(0) += 1;
273316
}
274317
Identifier | StringLiteral | RawStringLiteral | IntegerLiteral | FloatLiteral
@@ -287,16 +330,17 @@ impl Halstead for CppCode {
287330
let id = node.kind_id();
288331

289332
match id.into() {
290-
DOT | LPAREN | COMMA | STAR | GTGT | COLON | Return | Break | Continue | If | Else
291-
| Switch | Case | Default | For | While | Goto | Do | Delete | New | Try | Catch
292-
| Throw | EQ | AMPAMP | PIPEPIPE | PLUS | PLUSPLUS | SLASH | PERCENT | PIPE | AMP
293-
| LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | PLUSEQ | BANG | STAREQ
294-
| SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET | CARETEQ | PIPEEQ | LBRACK
295-
| LBRACE | QMARK | COLONCOLON | TypeSpecifier | Sizeof => {
333+
DOT | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return | Break
334+
| Continue | If | Else | Switch | Case | Default | For | While | Goto | Do | Delete
335+
| New | Try | Catch | Throw | EQ | AMPAMP | PIPEPIPE | DASH | PLUS | PLUSPLUS
336+
| SLASH | PERCENT | PIPE | AMP | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ
337+
| GT | PLUSEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ
338+
| CARET | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | COLONCOLON | PrimitiveType
339+
| TypeSpecifier | Sizeof => {
296340
*stats.operators.entry(id).or_insert(0) += 1;
297341
}
298-
Identifier | TypeIdentifier | FieldIdentifier | PrimitiveType | RawStringLiteral
299-
| StringLiteral | NumberLiteral | True | False | Null | Nullptr | DOTDOTDOT => {
342+
Identifier | TypeIdentifier | FieldIdentifier | RawStringLiteral | StringLiteral
343+
| NumberLiteral | True | False | Null | Nullptr | DOTDOTDOT => {
300344
*stats.operands.entry(get_id(node, code)).or_insert(0) += 1;
301345
}
302346
_ => {}

src/metrics.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -219,24 +219,22 @@ impl<'a> FuncSpace<'a> {
219219
writeln!(stdout, "halstead")?;
220220

221221
let prefix = format!("{}{}", prefix, pref_child);
222+
223+
Self::dump_value("n1", stats.u_operators(), &prefix, false, stdout)?;
224+
Self::dump_value("N1", stats.operators(), &prefix, false, stdout)?;
225+
Self::dump_value("n2", stats.u_operands(), &prefix, false, stdout)?;
226+
Self::dump_value("N2", stats.operands(), &prefix, false, stdout)?;
227+
228+
Self::dump_value("length", stats.length(), &prefix, false, stdout)?;
222229
Self::dump_value(
223-
"unique operands",
224-
stats.u_operands(),
225-
&prefix,
226-
false,
227-
stdout,
228-
)?;
229-
Self::dump_value("operands", stats.operands(), &prefix, false, stdout)?;
230-
Self::dump_value(
231-
"unique operators",
232-
stats.u_operators(),
230+
"estimated program length",
231+
stats.estimated_program_length(),
233232
&prefix,
234233
false,
235234
stdout,
236235
)?;
237-
Self::dump_value("operators", stats.operators(), &prefix, false, stdout)?;
238-
Self::dump_value("length", stats.length(), &prefix, false, stdout)?;
239-
Self::dump_value("size", stats.size(), &prefix, false, stdout)?;
236+
Self::dump_value("purity ratio", stats.purity_ratio(), &prefix, false, stdout)?;
237+
Self::dump_value("vocabulary", stats.vocabulary(), &prefix, false, stdout)?;
240238
Self::dump_value("volume", stats.volume(), &prefix, false, stdout)?;
241239
Self::dump_value("difficulty", stats.difficulty(), &prefix, false, stdout)?;
242240
Self::dump_value("level", stats.level(), &prefix, false, stdout)?;

src/web/server.rs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -640,13 +640,15 @@ mod tests {
640640
"difficulty": 1.0,
641641
"effort": 4.754_887_502_163_468,
642642
"length": 3.0,
643+
"estimated_program_length": 2.0,
644+
"purity_ratio": 0.666_666_666_666_666_6,
643645
"level": 1.0,
644-
"operands": 1.0,
645-
"operators": 2.0,
646-
"size": 3.0,
646+
"N2": 1.0,
647+
"N1": 2.0,
648+
"vocabulary": 3.0,
647649
"time": 0.264_160_416_786_859_36,
648-
"unique_operands": 1.0,
649-
"unique_operators": 2.0,
650+
"n2": 1.0,
651+
"n1": 2.0,
650652
"volume": 4.754_887_502_163_468},
651653
"loc": {"cloc": 1.0, "lloc": 2.0, "sloc": 3.0}},
652654
"name": "test.py",
@@ -660,13 +662,15 @@ mod tests {
660662
"difficulty": 1.0,
661663
"effort": 4.754_887_502_163_468,
662664
"length": 3.0,
665+
"estimated_program_length": 2.0,
666+
"purity_ratio": 0.666_666_666_666_666_6,
663667
"level": 1.0,
664-
"operands": 1.0,
665-
"operators": 2.0,
666-
"size": 3.0,
668+
"N2": 1.0,
669+
"N1": 2.0,
670+
"vocabulary": 3.0,
667671
"time": 0.264_160_416_786_859_36,
668-
"unique_operands": 1.0,
669-
"unique_operators": 2.0,
672+
"n2": 1.0,
673+
"n1": 2.0,
670674
"volume": 4.754_887_502_163_468},
671675
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
672676
"name": "foo",
@@ -706,13 +710,15 @@ mod tests {
706710
"difficulty": 1.0,
707711
"effort": 4.754_887_502_163_468,
708712
"length": 3.0,
713+
"estimated_program_length": 2.0,
714+
"purity_ratio": 0.666_666_666_666_666_6,
709715
"level": 1.0,
710-
"operands": 1.0,
711-
"operators": 2.0,
712-
"size": 3.0,
716+
"N2": 1.0,
717+
"N1": 2.0,
718+
"vocabulary": 3.0,
713719
"time": 0.264_160_416_786_859_36,
714-
"unique_operands": 1.0,
715-
"unique_operators": 2.0,
720+
"n2": 1.0,
721+
"n1": 2.0,
716722
"volume": 4.754_887_502_163_468},
717723
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
718724
"name": "test.py",
@@ -748,13 +754,15 @@ mod tests {
748754
"difficulty": 1.0,
749755
"effort": 4.754_887_502_163_468,
750756
"length": 3.0,
757+
"estimated_program_length": 2.0,
758+
"purity_ratio": 0.666_666_666_666_666_6,
751759
"level": 1.0,
752-
"operands": 1.0,
753-
"operators": 2.0,
754-
"size": 3.0,
760+
"N2": 1.0,
761+
"N1": 2.0,
762+
"vocabulary": 3.0,
755763
"time": 0.264_160_416_786_859_36,
756-
"unique_operands": 1.0,
757-
"unique_operators": 2.0,
764+
"n2": 1.0,
765+
"n1": 2.0,
758766
"volume": 4.754_887_502_163_468},
759767
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
760768
"name": "test.py",
@@ -768,13 +776,15 @@ mod tests {
768776
"difficulty": 1.0,
769777
"effort": 4.754_887_502_163_468,
770778
"length": 3.0,
779+
"estimated_program_length": 2.0,
780+
"purity_ratio": 0.666_666_666_666_666_6,
771781
"level": 1.0,
772-
"operands": 1.0,
773-
"operators": 2.0,
774-
"size": 3.0,
782+
"N2": 1.0,
783+
"N1": 2.0,
784+
"vocabulary": 3.0,
775785
"time": 0.264_160_416_786_859_36,
776-
"unique_operands": 1.0,
777-
"unique_operators": 2.0,
786+
"n2": 1.0,
787+
"n1": 2.0,
778788
"volume": 4.754_887_502_163_468},
779789
"loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}},
780790
"name": "foo",

0 commit comments

Comments
 (0)