Skip to content

Commit

Permalink
Add metrics for C/C++
Browse files Browse the repository at this point in the history
  • Loading branch information
calixteman committed Dec 4, 2019
1 parent bdf4087 commit 23843b3
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 9 deletions.
9 changes: 8 additions & 1 deletion src/checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,18 @@ impl Checker for CppCode {
RawStringLiteral
);
mk_checker!(is_call, CallExpression);
mk_checker!(is_func, FunctionDefinition);
mk_checker!(
is_func,
FunctionDefinition,
FunctionDefinition2,
FunctionDefinition3
);
mk_checker!(
is_func_space,
TranslationUnit,
FunctionDefinition,
FunctionDefinition2,
FunctionDefinition3,
StructSpecifier,
ClassSpecifier,
NamespaceDefinition
Expand Down
28 changes: 26 additions & 2 deletions src/cyclomatic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,34 @@ impl Cyclomatic for RustCode {
}
}

impl Cyclomatic for CCode {
fn compute(node: &Node, stats: &mut Stats) {
use C::*;

match node.kind_id().into() {
If | For | While | Case | ConditionalExpression | AMPAMP | PIPEPIPE => {
stats.cyclomatic += 1.;
}
_ => {}
}
}
}

impl Cyclomatic for CppCode {
fn compute(node: &Node, stats: &mut Stats) {
use Cpp::*;

match node.kind_id().into() {
If | For | While | Case | Catch | ConditionalExpression | AMPAMP | PIPEPIPE => {
stats.cyclomatic += 1.;
}
_ => {}
}
}
}

impl Cyclomatic for PreprocCode {}
impl Cyclomatic for CcommentCode {}
impl Cyclomatic for CCode {}
impl Cyclomatic for CppCode {}
impl Cyclomatic for CSharpCode {}
impl Cyclomatic for JavaCode {}
impl Cyclomatic for GoCode {}
Expand Down
2 changes: 2 additions & 0 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum NodeKind {
Trait,
Impl,
Unit,
Namespace,
}

impl fmt::Display for NodeKind {
Expand All @@ -21,6 +22,7 @@ impl fmt::Display for NodeKind {
NodeKind::Trait => "trait",
NodeKind::Impl => "impl",
NodeKind::Unit => "unit",
NodeKind::Namespace => "namespace",
};
write!(f, "{}", s)
}
Expand Down
90 changes: 88 additions & 2 deletions src/getter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use tree_sitter::Node;

use crate::enums::NodeKind;
use crate::traits::Search;

use crate::*;

pub trait Getter {
Expand Down Expand Up @@ -114,10 +116,94 @@ impl Getter for RustCode {
}
}

impl Getter for CCode {
fn get_func_name<'a>(node: &Node, code: &'a [u8]) -> Option<&'a str> {
Self::get_func_space_name(node, code)
}

fn get_func_space_name<'a>(node: &Node, code: &'a [u8]) -> Option<&'a str> {
// we're in a function_definition so need to get the declarator
if let Some(declarator) = node.child_by_field_name("declarator") {
if let Some(fd) = declarator.first_occurence(|id| C::FunctionDeclarator == id) {
if let Some(first) = fd.child(0) {
if first.kind_id() == C::Identifier {
let code = &code[first.start_byte()..first.end_byte()];
return std::str::from_utf8(code).ok();
}
}
}
}
Some("<anonymous>")
}

fn get_kind(node: &Node) -> NodeKind {
use C::*;

let typ = node.kind_id();
match typ.into() {
FunctionDefinition => NodeKind::Function,
TranslationUnit => NodeKind::Unit,
_ => NodeKind::Unknown,
}
}
}

impl Getter for CppCode {
fn get_func_name<'a>(node: &Node, code: &'a [u8]) -> Option<&'a str> {
Self::get_func_space_name(node, code)
}

fn get_func_space_name<'a>(node: &Node, code: &'a [u8]) -> Option<&'a str> {
let typ = node.kind_id();
match typ.into() {
Cpp::FunctionDefinition | Cpp::FunctionDefinition2 | Cpp::FunctionDefinition3 => {
// we're in a function_definition so need to get the declarator
if let Some(declarator) = node.child_by_field_name("declarator") {
if let Some(fd) = declarator.first_occurence(|id| {
Cpp::FunctionDeclarator == id
|| Cpp::FunctionDeclarator2 == id
|| Cpp::FunctionDeclarator3 == id
}) {
if let Some(first) = fd.child(0) {
match first.kind_id().into() {
Cpp::ScopedIdentifier | Cpp::Identifier => {
let code = &code[first.start_byte()..first.end_byte()];
return std::str::from_utf8(code).ok();
}
_ => {}
}
}
}
}
}
_ => {
if let Some(name) = node.child_by_field_name("name") {
let code = &code[name.start_byte()..name.end_byte()];
return std::str::from_utf8(code).ok();
}
}
}

Some("<anonymous>")
}

fn get_kind(node: &Node) -> NodeKind {
use Cpp::*;

let typ = node.kind_id();
match typ.into() {
FunctionDefinition | FunctionDefinition2 | FunctionDefinition3 => NodeKind::Function,
StructSpecifier => NodeKind::Struct,
ClassSpecifier => NodeKind::Class,
NamespaceDefinition => NodeKind::Namespace,
TranslationUnit => NodeKind::Unit,
_ => NodeKind::Unknown,
}
}
}

impl Getter for PreprocCode {}
impl Getter for CcommentCode {}
impl Getter for CCode {}
impl Getter for CppCode {}
impl Getter for CSharpCode {}
impl Getter for JavaCode {}
impl Getter for GoCode {}
Expand Down
50 changes: 48 additions & 2 deletions src/halstead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,56 @@ impl Halstead for RustCode {
}
}

impl Halstead for CCode {
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats<'a>) {
use C::*;

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 | 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
| TypeSpecifier | Sizeof => {
*stats.operators.entry(id).or_insert(0) += 1;
}
Identifier | TypeIdentifier | FieldIdentifier | PrimitiveType | StringLiteral
| NumberLiteral | True | False | Null | DOTDOTDOT => {
*stats.operands.entry(get_id(node, code)).or_insert(0) += 1;
}
_ => {}
}
}
}

impl Halstead for CppCode {
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats<'a>) {
use Cpp::*;

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 => {
*stats.operators.entry(id).or_insert(0) += 1;
}
Identifier | TypeIdentifier | FieldIdentifier | PrimitiveType | RawStringLiteral
| StringLiteral | NumberLiteral | True | False | Null | Nullptr | DOTDOTDOT => {
*stats.operands.entry(get_id(node, code)).or_insert(0) += 1;
}
_ => {}
}
}
}

impl Halstead for PreprocCode {}
impl Halstead for CcommentCode {}
impl Halstead for CCode {}
impl Halstead for CppCode {}
impl Halstead for CSharpCode {}
impl Halstead for JavaCode {}
impl Halstead for GoCode {}
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub use crate::macros::*;
pub mod enums;
pub use crate::enums::*;

pub mod node;
pub use crate::node::*;

pub mod web;

#[macro_use]
Expand Down
32 changes: 32 additions & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::traits::Search;
use tree_sitter::Node;

impl<'a> Search<'a> for Node<'a> {
fn first_occurence(&self, pred: fn(u16) -> bool) -> Option<Node<'a>> {
let mut cursor = self.walk();
let mut stack = Vec::new();
let mut children = Vec::new();

stack.push(*self);

while let Some(node) = stack.pop() {
if pred(node.kind_id()) {
return Some(node);
}
cursor.reset(node);
if cursor.goto_first_child() {
loop {
children.push(cursor.node());
if !cursor.goto_next_sibling() {
break;
}
}
for child in children.drain(..).rev() {
stack.push(child);
}
}
}

None
}
}
34 changes: 32 additions & 2 deletions src/sloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,40 @@ impl SourceLoc for RustCode {
}
}

impl SourceLoc for CCode {
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
use C::*;

let start = init(node, stats, is_func_space);

match node.kind_id().into() {
Comment | StringLiteral | ExpressionStatement | CompoundStatement
| LabeledStatement | DeclarationList | FieldDeclarationList => {}
_ => {
stats.lines.insert(start);
}
}
}
}

impl SourceLoc for CppCode {
fn compute(node: &Node, _code: &[u8], stats: &mut Stats, is_func_space: bool) {
use Cpp::*;

let start = init(node, stats, is_func_space);

match node.kind_id().into() {
Comment | RawStringLiteral | StringLiteral | ExpressionStatement
| CompoundStatement | LabeledStatement | DeclarationList | FieldDeclarationList => {}
_ => {
stats.lines.insert(start);
}
}
}
}

impl SourceLoc for PreprocCode {}
impl SourceLoc for CcommentCode {}
impl SourceLoc for CCode {}
impl SourceLoc for CppCode {}
impl SourceLoc for CSharpCode {}
impl SourceLoc for JavaCode {}
impl SourceLoc for GoCode {}
Expand Down
4 changes: 4 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ pub trait Callback {

fn call<T: TSParserTrait>(cfg: Self::Cfg, parser: &T) -> Self::Res;
}

pub trait Search<'a> {
fn first_occurence(&self, pred: fn(u16) -> bool) -> Option<Node<'a>>;
}
1 change: 1 addition & 0 deletions src/ts_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ impl<T: TSLanguage + Checker + Getter + Alterator + Cyclomatic + Halstead + Sour
"comment" => T::is_comment,
"error" => T::is_error,
"string" => T::is_string,
"function" => T::is_func,
_ => |_: &Node| -> bool { true },
});
}
Expand Down

0 comments on commit 23843b3

Please sign in to comment.