diff --git a/src/checker.rs b/src/checker.rs index b7f9ff92e..d450bcb52 100644 --- a/src/checker.rs +++ b/src/checker.rs @@ -3,6 +3,82 @@ use regex::bytes::Regex; use crate::*; +#[inline(always)] +fn is_child(node: &Node, id: u16) -> bool { + node.object() + .children(&mut node.object().walk()) + .any(|child| child.kind_id() == id) +} + +#[inline(always)] +fn has_sibling(node: &Node, id: u16) -> bool { + node.object().parent().map_or(false, |parent| { + node.object() + .children(&mut parent.walk()) + .any(|child| child.kind_id() == id) + }) +} + +macro_rules! check_if_func { + ($node: ident) => { + count_specific_ancestors!( + $node, + VariableDeclarator | AssignmentExpression | LabeledStatement | Pair, + StatementBlock | ReturnStatement | NewExpression | Arguments + ) > 0 + || is_child($node, Identifier as u16) + }; +} + +macro_rules! check_if_arrow_func { + ($node: ident) => { + count_specific_ancestors!( + $node, + VariableDeclarator | AssignmentExpression | LabeledStatement, + StatementBlock | ReturnStatement | NewExpression | CallExpression + ) > 0 + || has_sibling($node, PropertyIdentifier as u16) + }; +} + +macro_rules! is_js_func { + ($node: ident) => { + match $node.object().kind_id().into() { + FunctionDeclaration | MethodDefinition => true, + Function => check_if_func!($node), + ArrowFunction => check_if_arrow_func!($node), + _ => false, + } + }; +} + +macro_rules! is_js_closure { + ($node: ident) => { + match $node.object().kind_id().into() { + GeneratorFunction | GeneratorFunctionDeclaration => true, + Function => !check_if_func!($node), + ArrowFunction => !check_if_arrow_func!($node), + _ => false, + } + }; +} + +macro_rules! is_js_func_and_closure_checker { + ($grammar: ident) => { + #[inline(always)] + fn is_func(node: &Node) -> bool { + use $grammar::*; + is_js_func!(node) + } + + #[inline(always)] + fn is_closure(node: &Node) -> bool { + use $grammar::*; + is_js_closure!(node) + } + }; +} + pub trait Checker { fn is_comment(node: &Node) -> bool; @@ -19,6 +95,7 @@ pub trait Checker { fn is_string(node: &Node) -> bool; fn is_call(node: &Node) -> bool; fn is_func(node: &Node) -> bool; + fn is_closure(node: &Node) -> bool; fn is_func_space(node: &Node) -> bool; fn is_non_arg(node: &Node) -> bool; @@ -32,6 +109,7 @@ impl Checker for PreprocCode { mk_checker!(is_string, StringLiteral, RawStringLiteral); mk_checker!(is_call,); mk_checker!(is_func,); + mk_checker!(is_closure,); mk_checker!(is_func_space,); mk_checker!(is_non_arg,); } @@ -41,6 +119,7 @@ impl Checker for CcommentCode { mk_checker!(is_string, StringLiteral, RawStringLiteral); mk_checker!(is_call,); mk_checker!(is_func,); + mk_checker!(is_closure,); mk_checker!(is_func_space,); mk_checker!(is_non_arg,); @@ -66,8 +145,11 @@ impl Checker for CppCode { is_func, FunctionDefinition, FunctionDefinition2, - FunctionDefinition3 + FunctionDefinition3, + FunctionDefinition4, + FunctionDefinitionRepeat1 ); + mk_checker!(is_closure, LambdaExpression); mk_checker!( is_func_space, TranslationUnit, @@ -106,6 +188,7 @@ impl Checker for PythonCode { mk_checker!(is_string, String, ConcatenatedString); mk_checker!(is_call, Call); mk_checker!(is_func, FunctionDefinition); + mk_checker!(is_closure, Lambda); mk_checker!(is_func_space, Module, FunctionDefinition, ClassDefinition); mk_checker!(is_non_arg, LPAREN, COMMA, RPAREN); } @@ -115,6 +198,7 @@ impl Checker for JavaCode { mk_checker!(is_string, StringLiteral); mk_checker!(is_call, MethodInvocation); mk_checker!(is_func, MethodDeclaration); + mk_checker!(is_closure,); mk_checker!(is_func_space, Program, ClassDeclaration); mk_checker!(is_non_arg,); } @@ -123,15 +207,7 @@ impl Checker for MozjsCode { mk_checker!(is_comment, Comment); mk_checker!(is_string, String, TemplateString); mk_checker!(is_call, CallExpression); - mk_checker!( - is_func, - Function, - GeneratorFunction, - FunctionDeclaration, - GeneratorFunctionDeclaration, - MethodDefinition, - ArrowFunction - ); + mk_checker!( is_func_space, Program, @@ -145,6 +221,8 @@ impl Checker for MozjsCode { ArrowFunction ); + is_js_func_and_closure_checker!(Mozjs); + #[inline(always)] fn is_else_if(node: &Node) -> bool { if node.object().kind_id() != ::BaseLang::IfStatement { @@ -162,15 +240,6 @@ impl Checker for JavascriptCode { mk_checker!(is_comment, Comment); mk_checker!(is_string, String, TemplateString); mk_checker!(is_call, CallExpression); - mk_checker!( - is_func, - Function, - GeneratorFunction, - FunctionDeclaration, - GeneratorFunctionDeclaration, - MethodDefinition, - ArrowFunction - ); mk_checker!( is_func_space, Program, @@ -183,6 +252,9 @@ impl Checker for JavascriptCode { ClassDeclaration, ArrowFunction ); + + is_js_func_and_closure_checker!(Javascript); + mk_else_if!(IfStatement); mk_checker!(is_non_arg, LPAREN, COMMA, RPAREN); } @@ -191,15 +263,6 @@ impl Checker for TypescriptCode { mk_checker!(is_comment, Comment); mk_checker!(is_string, String, TemplateString); mk_checker!(is_call, CallExpression); - mk_checker!( - is_func, - Function, - GeneratorFunction, - FunctionDeclaration, - GeneratorFunctionDeclaration, - MethodDefinition, - ArrowFunction - ); mk_checker!( is_func_space, Program, @@ -213,6 +276,8 @@ impl Checker for TypescriptCode { ArrowFunction ); + is_js_func_and_closure_checker!(Typescript); + #[inline(always)] fn is_else_if(node: &Node) -> bool { if node.object().kind_id() != ::BaseLang::IfStatement { @@ -230,15 +295,6 @@ impl Checker for TsxCode { mk_checker!(is_comment, Comment); mk_checker!(is_string, String, TemplateString); mk_checker!(is_call, CallExpression); - mk_checker!( - is_func, - Function, - GeneratorFunction, - FunctionDeclaration, - GeneratorFunctionDeclaration, - MethodDefinition, - ArrowFunction - ); mk_checker!( is_func_space, Program, @@ -252,6 +308,9 @@ impl Checker for TsxCode { ClassDeclaration, ArrowFunction ); + + is_js_func_and_closure_checker!(Tsx); + mk_else_if!(IfStatement); mk_checker!(is_non_arg, LPAREN, COMMA, RPAREN); } @@ -282,7 +341,8 @@ impl Checker for RustCode { } mk_checker!(is_string, StringLiteral, RawStringLiteral); mk_checker!(is_call, CallExpression); - mk_checker!(is_func, FunctionItem, ClosureExpression); + mk_checker!(is_func, FunctionItem); + mk_checker!(is_closure, ClosureExpression); mk_checker!( is_func_space, SourceFile, diff --git a/src/metrics/nom.rs b/src/metrics/nom.rs index 21c326ad9..13eb0a2fe 100644 --- a/src/metrics/nom.rs +++ b/src/metrics/nom.rs @@ -68,189 +68,29 @@ impl Stats { } } -#[inline(always)] -fn is_child(node: &Node, id: u16) -> bool { - node.object() - .children(&mut node.object().walk()) - .any(|child| child.kind_id() == id) -} - -#[inline(always)] -fn has_sibling(node: &Node, id: u16) -> bool { - let parent = node.object().parent(); - if let Some(parent) = parent { - node.object() - .children(&mut parent.walk()) - .any(|child| child.kind_id() == id) - } else { - false - } -} - -macro_rules! function { - ($node: ident, $stats: ident) => { - // If a function node has named ancestors or child then it is a function, - // otherwise a closure. - if count_specific_ancestors!( - $node, - VariableDeclarator | AssignmentExpression | LabeledStatement | Pair, - StatementBlock | ReturnStatement | NewExpression | Arguments - ) > 0 - || is_child($node, Identifier as u16) - { - $stats.functions += 1; - } else { - $stats.closures += 1; - } - }; -} - -macro_rules! arrow_function { - ($node: ident, $stats: ident) => { - // If an arrow function node has named ancestors then it is a function, - // otherwise a closure. - if count_specific_ancestors!( - $node, - VariableDeclarator | AssignmentExpression | LabeledStatement, - StatementBlock | ReturnStatement | NewExpression | CallExpression - ) > 0 - || has_sibling($node, PropertyIdentifier as u16) - { - $stats.functions += 1; - } else { - $stats.closures += 1; - } - }; -} - -macro_rules! js_grammar { - ($grammar: ident, $node: ident, $stats: ident) => { - use $grammar::*; - - match $node.object().kind_id().into() { - FunctionDeclaration | MethodDefinition => { - $stats.functions += 1; - } - GeneratorFunction | GeneratorFunctionDeclaration => { - $stats.closures += 1; - } - Function => { - function!($node, $stats); - } - ArrowFunction => { - arrow_function!($node, $stats); - } - _ => {} - } - }; -} - -macro_rules! typescript_grammar { - ($grammar: ident, $node: ident, $stats: ident) => { - use $grammar::*; - - match $node.object().kind_id().into() { - FunctionDeclaration | MethodDefinition => { - $stats.functions += 1; - } - GeneratorFunction | GeneratorFunctionDeclaration => { - $stats.closures += 1; - } - Function => { - function!($node, $stats); - } - ArrowFunction => { - arrow_function!($node, $stats); - } - _ => {} - } - }; -} - #[doc(hidden)] pub trait Nom where Self: Checker, { - fn compute(_node: &Node, _stats: &mut Stats) {} -} - -impl Nom for PythonCode { fn compute(node: &Node, stats: &mut Stats) { - use Python::*; - - match node.object().kind_id().into() { - FunctionDefinition => { - stats.functions += 1; - } - Lambda => { - stats.closures += 1; - } - _ => {} + if Self::is_func(node) { + stats.functions += 1; + return; } - } -} - -impl Nom for MozjsCode { - fn compute(node: &Node, stats: &mut Stats) { - js_grammar!(Mozjs, node, stats); - } -} - -impl Nom for JavascriptCode { - fn compute(node: &Node, stats: &mut Stats) { - js_grammar!(Javascript, node, stats); - } -} - -impl Nom for TypescriptCode { - fn compute(node: &Node, stats: &mut Stats) { - typescript_grammar!(Typescript, node, stats); - } -} - -impl Nom for TsxCode { - fn compute(node: &Node, stats: &mut Stats) { - typescript_grammar!(Tsx, node, stats); - } -} - -impl Nom for RustCode { - fn compute(node: &Node, stats: &mut Stats) { - use Rust::*; - - match node.object().kind_id().into() { - FunctionItem => { - stats.functions += 1; - } - ClosureExpression => { - stats.closures += 1; - } - _ => {} - } - } -} - -impl Nom for CppCode { - fn compute(node: &Node, stats: &mut Stats) { - use Cpp::*; - - match node.object().kind_id().into() { - FunctionDefinition - | FunctionDefinition2 - | FunctionDefinition3 - | FunctionDefinition4 - | FunctionDefinitionRepeat1 => { - stats.functions += 1; - } - LambdaExpression => { - stats.closures += 1; - } - _ => {} + if Self::is_closure(node) { + stats.closures += 1; } } } +impl Nom for PythonCode {} +impl Nom for MozjsCode {} +impl Nom for JavascriptCode {} +impl Nom for TypescriptCode {} +impl Nom for TsxCode {} +impl Nom for RustCode {} +impl Nom for CppCode {} impl Nom for PreprocCode {} impl Nom for CcommentCode {} impl Nom for JavaCode {}