diff --git a/src/halstead.rs b/src/halstead.rs index 92a4868b2..a18f886dc 100644 --- a/src/halstead.rs +++ b/src/halstead.rs @@ -19,13 +19,15 @@ impl Serialize for Stats<'_> { where S: Serializer, { - let mut st = serializer.serialize_struct("halstead", 12)?; - st.serialize_field("unique_operands", &self.u_operands())?; - st.serialize_field("operands", &self.operands())?; - st.serialize_field("unique_operators", &self.u_operators())?; - st.serialize_field("operators", &self.operators())?; + let mut st = serializer.serialize_struct("halstead", 14)?; + st.serialize_field("n1", &self.u_operators())?; + st.serialize_field("N1", &self.operators())?; + st.serialize_field("n2", &self.u_operands())?; + st.serialize_field("N2", &self.operands())?; st.serialize_field("length", &self.length())?; - st.serialize_field("size", &self.size())?; + st.serialize_field("estimated_program_length", &self.estimated_program_length())?; + st.serialize_field("purity_ratio", &self.purity_ratio())?; + st.serialize_field("vocabulary", &self.vocabulary())?; st.serialize_field("volume", &self.volume())?; st.serialize_field("difficulty", &self.difficulty())?; st.serialize_field("level", &self.level())?; @@ -38,7 +40,37 @@ impl Serialize for Stats<'_> { impl<'a> fmt::Display for Stats<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - 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()) + write!( + f, + "n1: {}, \ + N1: {}, \ + n2: {}, \ + N2: {}, \ + length: {}, \ + estimated program length: {}, \ + purity ratio: {}, \ + size: {}, \ + volume: {}, \ + difficulty: {}, \ + level: {}, \ + effort: {}, \ + time: {}, \ + bugs: {}", + self.u_operators(), + self.operators(), + self.u_operands(), + self.operands(), + self.length(), + self.estimated_program_length(), + self.purity_ratio(), + self.vocabulary(), + self.volume(), + self.difficulty(), + self.level(), + self.effort(), + self.time(), + self.bugs(), + ) } } @@ -78,13 +110,24 @@ impl<'a> Stats<'a> { } #[inline(always)] - pub fn size(&self) -> f64 { + pub fn estimated_program_length(&self) -> f64 { + self.u_operators() * self.u_operators().log2() + + self.u_operands() * self.u_operands().log2() + } + + #[inline(always)] + pub fn purity_ratio(&self) -> f64 { + self.estimated_program_length() / self.length() + } + + #[inline(always)] + pub fn vocabulary(&self) -> f64 { self.u_operands() + self.u_operators() } #[inline(always)] pub fn volume(&self) -> f64 { - self.length() * self.size().log2() + self.length() * self.vocabulary().log2() } #[inline(always)] @@ -268,7 +311,7 @@ impl Halstead for RustCode { | LT | GT | AMP | MutableSpecifier | DOTDOT | DOTDOTEQ | DASH | AMPAMP | PIPEPIPE | PIPE | CARET | EQEQ | BANGEQ | LTEQ | GTEQ | LTLT | GTGT | SLASH | PERCENT | PLUSEQ | DASHEQ | STAREQ | SLASHEQ | PERCENTEQ | AMPEQ | PIPEEQ | CARETEQ - | LTLTEQ | GTGTEQ | Move | DOT => { + | LTLTEQ | GTGTEQ | Move | DOT | PrimitiveType => { *stats.operators.entry(id).or_insert(0) += 1; } Identifier | StringLiteral | RawStringLiteral | IntegerLiteral | FloatLiteral @@ -287,16 +330,17 @@ impl Halstead for CppCode { let id = node.kind_id(); match id.into() { - DOT | LPAREN | COMMA | STAR | GTGT | COLON | Return | Break | Continue | If | Else - | Switch | Case | Default | For | While | Goto | Do | Delete | New | Try | Catch - | Throw | EQ | AMPAMP | PIPEPIPE | PLUS | PLUSPLUS | SLASH | PERCENT | PIPE | AMP - | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | PLUSEQ | BANG | STAREQ - | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET | CARETEQ | PIPEEQ | LBRACK - | LBRACE | QMARK | COLONCOLON | TypeSpecifier | Sizeof => { + DOT | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return | Break + | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do | Delete + | New | Try | Catch | Throw | EQ | AMPAMP | PIPEPIPE | DASH | PLUS | PLUSPLUS + | SLASH | PERCENT | PIPE | AMP | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ + | GT | PLUSEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ + | CARET | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | COLONCOLON | PrimitiveType + | TypeSpecifier | Sizeof => { *stats.operators.entry(id).or_insert(0) += 1; } - Identifier | TypeIdentifier | FieldIdentifier | PrimitiveType | RawStringLiteral - | StringLiteral | NumberLiteral | True | False | Null | Nullptr | DOTDOTDOT => { + Identifier | TypeIdentifier | FieldIdentifier | RawStringLiteral | StringLiteral + | NumberLiteral | True | False | Null | Nullptr | DOTDOTDOT => { *stats.operands.entry(get_id(node, code)).or_insert(0) += 1; } _ => {} diff --git a/src/metrics.rs b/src/metrics.rs index d75aa1529..340d0c97f 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -219,24 +219,22 @@ impl<'a> FuncSpace<'a> { writeln!(stdout, "halstead")?; let prefix = format!("{}{}", prefix, pref_child); + + Self::dump_value("n1", stats.u_operators(), &prefix, false, stdout)?; + Self::dump_value("N1", stats.operators(), &prefix, false, stdout)?; + Self::dump_value("n2", stats.u_operands(), &prefix, false, stdout)?; + Self::dump_value("N2", stats.operands(), &prefix, false, stdout)?; + + Self::dump_value("length", stats.length(), &prefix, false, stdout)?; Self::dump_value( - "unique operands", - stats.u_operands(), - &prefix, - false, - stdout, - )?; - Self::dump_value("operands", stats.operands(), &prefix, false, stdout)?; - Self::dump_value( - "unique operators", - stats.u_operators(), + "estimated program length", + stats.estimated_program_length(), &prefix, false, stdout, )?; - Self::dump_value("operators", stats.operators(), &prefix, false, stdout)?; - Self::dump_value("length", stats.length(), &prefix, false, stdout)?; - Self::dump_value("size", stats.size(), &prefix, false, stdout)?; + Self::dump_value("purity ratio", stats.purity_ratio(), &prefix, false, stdout)?; + Self::dump_value("vocabulary", stats.vocabulary(), &prefix, false, stdout)?; Self::dump_value("volume", stats.volume(), &prefix, false, stdout)?; Self::dump_value("difficulty", stats.difficulty(), &prefix, false, stdout)?; Self::dump_value("level", stats.level(), &prefix, false, stdout)?; diff --git a/src/web/server.rs b/src/web/server.rs index 2680d5c04..3206cf2d3 100644 --- a/src/web/server.rs +++ b/src/web/server.rs @@ -640,13 +640,15 @@ mod tests { "difficulty": 1.0, "effort": 4.754_887_502_163_468, "length": 3.0, + "estimated_program_length": 2.0, + "purity_ratio": 0.666_666_666_666_666_6, "level": 1.0, - "operands": 1.0, - "operators": 2.0, - "size": 3.0, + "N2": 1.0, + "N1": 2.0, + "vocabulary": 3.0, "time": 0.264_160_416_786_859_36, - "unique_operands": 1.0, - "unique_operators": 2.0, + "n2": 1.0, + "n1": 2.0, "volume": 4.754_887_502_163_468}, "loc": {"cloc": 1.0, "lloc": 2.0, "sloc": 3.0}}, "name": "test.py", @@ -660,13 +662,15 @@ mod tests { "difficulty": 1.0, "effort": 4.754_887_502_163_468, "length": 3.0, + "estimated_program_length": 2.0, + "purity_ratio": 0.666_666_666_666_666_6, "level": 1.0, - "operands": 1.0, - "operators": 2.0, - "size": 3.0, + "N2": 1.0, + "N1": 2.0, + "vocabulary": 3.0, "time": 0.264_160_416_786_859_36, - "unique_operands": 1.0, - "unique_operators": 2.0, + "n2": 1.0, + "n1": 2.0, "volume": 4.754_887_502_163_468}, "loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}}, "name": "foo", @@ -706,13 +710,15 @@ mod tests { "difficulty": 1.0, "effort": 4.754_887_502_163_468, "length": 3.0, + "estimated_program_length": 2.0, + "purity_ratio": 0.666_666_666_666_666_6, "level": 1.0, - "operands": 1.0, - "operators": 2.0, - "size": 3.0, + "N2": 1.0, + "N1": 2.0, + "vocabulary": 3.0, "time": 0.264_160_416_786_859_36, - "unique_operands": 1.0, - "unique_operators": 2.0, + "n2": 1.0, + "n1": 2.0, "volume": 4.754_887_502_163_468}, "loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}}, "name": "test.py", @@ -748,13 +754,15 @@ mod tests { "difficulty": 1.0, "effort": 4.754_887_502_163_468, "length": 3.0, + "estimated_program_length": 2.0, + "purity_ratio": 0.666_666_666_666_666_6, "level": 1.0, - "operands": 1.0, - "operators": 2.0, - "size": 3.0, + "N2": 1.0, + "N1": 2.0, + "vocabulary": 3.0, "time": 0.264_160_416_786_859_36, - "unique_operands": 1.0, - "unique_operators": 2.0, + "n2": 1.0, + "n1": 2.0, "volume": 4.754_887_502_163_468}, "loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}}, "name": "test.py", @@ -768,13 +776,15 @@ mod tests { "difficulty": 1.0, "effort": 4.754_887_502_163_468, "length": 3.0, + "estimated_program_length": 2.0, + "purity_ratio": 0.666_666_666_666_666_6, "level": 1.0, - "operands": 1.0, - "operators": 2.0, - "size": 3.0, + "N2": 1.0, + "N1": 2.0, + "vocabulary": 3.0, "time": 0.264_160_416_786_859_36, - "unique_operands": 1.0, - "unique_operators": 2.0, + "n2": 1.0, + "n1": 2.0, "volume": 4.754_887_502_163_468}, "loc": {"cloc": 0.0, "lloc": 2.0, "sloc": 2.0}}, "name": "foo",