Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ dependency(
DEPENDENCY_LINK_TARGETS tree-sitter-typescript
)

dependency(
DEPENDENCY_NAME tree-sitter-cpp
DEPENDENCY_RESOURCE kilo52/tree-sitter-cpp
DEPENDENCY_VERSION v0.23.4-patch-cmakeliststxt
DEPENDENCY_LINK_TARGETS tree-sitter-cpp
)

dependency(
DEPENDENCY_NAME utf8proc
DEPENDENCY_RESOURCE JuliaStrings/utf8proc
Expand Down
2 changes: 1 addition & 1 deletion Dependencies.cmake.sha256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8648e7957901a3029837287b247ad68b2b2dce86ab816eaa4fc18505449f6701
56e55b81f62920d03317875cd291ba2c7dcfe72d01ae3f85cf75dbf072d811d8
6 changes: 6 additions & 0 deletions cmake/ConfigTree-sitter-cpp.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Configuration options for the Tree-Sitter-Cpp dependency

# Define the program variable to avoid errors when custom command
# is executed since the CLI might not actually be available.
set(TREE_SITTER_CLI "")
set(BUILD_TESTING OFF)
1 change: 1 addition & 0 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ target_sources(
"c/lang_javascript.c"
"c/lang_typescript.c"
"c/lang_bash.c"
"c/lang_cpp.c"
"c/logical.c"
"c/physical.c"
"c/statistics.c"
Expand Down
21 changes: 21 additions & 0 deletions src/lib/c/factories.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ TSParser* createParserPython(void);
TSParser* createParserJavaScript(void);
TSParser* createParserTypeScript(void);
TSParser* createParserBash(void);
TSParser* createParserCpp(void);

void evaluateNodeC(TSNode node, NodeEvalTrace* trace);
void evaluateNodeJava(TSNode node, NodeEvalTrace* trace);
void evaluateNodePython(TSNode node, NodeEvalTrace* trace);
void evaluateNodeJavaScript(TSNode node, NodeEvalTrace* trace);
void evaluateNodeTypeScript(TSNode node, NodeEvalTrace* trace);
void evaluateNodeBash(TSNode node, NodeEvalTrace* trace);
void evaluateNodeCpp(TSNode node, NodeEvalTrace* trace);

TSParser* createParser(RcnTextFormat language) {
switch (language) {
Expand All @@ -50,6 +52,8 @@ TSParser* createParser(RcnTextFormat language) {
return createParserTypeScript();
case RCN_LANG_BASH:
return createParserBash();
case RCN_LANG_CPP:
return createParserCpp();
default:
return NULL;
}
Expand All @@ -69,6 +73,8 @@ NodeVisitor createEvaluationFunction(RcnTextFormat language) {
return evaluateNodeTypeScript;
case RCN_LANG_BASH:
return evaluateNodeBash;
case RCN_LANG_CPP:
return evaluateNodeCpp;
default:
return NULL;
}
Expand All @@ -81,6 +87,7 @@ const char* getInlineSourceCommentString(RcnTextFormat language) {
return "#";
case RCN_LANG_C:
case RCN_LANG_JAVA:
case RCN_LANG_CPP:
case RCN_LANG_JAVASCRIPT:
case RCN_LANG_TYPESCRIPT:
default:
Expand Down Expand Up @@ -165,6 +172,20 @@ SourceFormatDetection detectSourceFormat(const RcnSourceFile* file) {
} else if (strcmp(extension, "txt") == 0) {
detection.isSupportedFormat = true;
detection.format = RCN_TEXT_UNFORMATTED;
} else if (strcmp(extension, "cpp") == 0
|| strcmp(extension, "cc") == 0
|| strcmp(extension, "cxx") == 0
|| strcmp(extension, "c++") == 0
|| strcmp(extension, "hpp") == 0
|| strcmp(extension, "hxx") == 0
|| strcmp(extension, "cppm") == 0
|| strcmp(extension, "ccm") == 0
|| strcmp(extension, "cxxm") == 0
|| strcmp(extension, "c++m") == 0
|| strcmp(extension, "ixx") == 0) {
detection.isSupportedFormat = true;
detection.isProgrammingLanguage = true;
detection.format = RCN_LANG_CPP;
}

return detection;
Expand Down
246 changes: 246 additions & 0 deletions src/lib/c/lang_cpp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
* Copyright (C) 2026 Raven Computing
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdint.h>

#include "tree_sitter/api.h"

#include "reckon/reckon.h"
#include "reckon_export.h"
#include "evaluation.h"

RECKON_NO_EXPORT const TSLanguage* tree_sitter_cpp(void);

/**
* These are the symbol identifiers as defined by the C++ language parser
* of tree-sitter. We have only copied the symbol identifiers that we are
* interested in evaluating or counting. Others do not contribute to the weight
* of a node in the AST.
*/
enum SymbolIdentifiersCpp {
sym_preproc_directive = 19,
sym_preproc_include = 222,
sym_preproc_def = 223,
sym_preproc_function_def = 224,
sym_preproc_if = 227,
sym_preproc_ifdef = 228,
sym_preproc_else = 229,
sym_preproc_elif = 230,
sym_preproc_elifdef = 231,
sym_function_definition = 254,
sym_declaration = 255,
sym_type_definition = 256,
sym__type_definition_type = 257,
sym__type_definition_declarators = 258,
sym__declaration_modifiers = 259,
sym__declaration_specifiers = 260,
sym_linkage_specification = 261,
sym_attribute_specifier = 262,
sym_attribute = 263,
sym_declaration_list = 270,
sym__declarator = 271,
sym__type_declarator = 273,
sym__abstract_declarator = 274,
sym_attributed_declarator = 279,
sym_attributed_type_declarator = 281,
sym_type_specifier = 299,
sym_enum_specifier = 301,
sym_struct_specifier = 303,
sym_union_specifier = 304,
sym_field_declaration = 307,
sym_enumerator = 309,
sym_attributed_statement = 312,
sym_statement = 313,
sym__top_level_statement = 314,
sym_labeled_statement = 315,
sym__top_level_expression_statement = 316,
sym_expression_statement = 317,
sym_if_statement = 318,
sym_else_clause = 319,
sym_switch_statement = 320,
sym_case_statement = 321,
sym_while_statement = 322,
sym_do_statement = 323,
sym_for_statement = 324,
sym_return_statement = 326,
sym_break_statement = 327,
sym_continue_statement = 328,
sym_goto_statement = 329,
sym_expression = 334,
sym_class_specifier = 379,
sym_inline_method_definition = 400,
sym_constructor_or_destructor_definition = 405,
sym_friend_declaration = 410,
sym_template_declaration = 386,
sym_template_instantiation = 387,
sym_namespace_definition = 430,
sym_namespace_alias_definition = 431,
sym_using_declaration = 434,
sym_alias_declaration = 435,
sym_static_assert_declaration = 436,
sym_concept_definition = 437,
sym_for_range_loop = 438,
sym_co_return_statement = 443,
sym_co_yield_statement = 444,
sym_throw_statement = 445,
sym_try_statement = 446,
sym_catch_clause = 447,
};

TSParser* createParserCpp(void) {
TSParser* parser = ts_parser_new();
if (parser) {
if (!ts_parser_set_language(parser, tree_sitter_cpp())) {
// LCOV_EXCL_START
ts_parser_delete(parser);
return NULL;
// LCOV_EXCL_STOP
}
}
return parser;
}

static RcnCount evaluateNodeWeightCppImpl(TSNode node, NodeEvalTrace* trace) {
RcnCount weight = 0;
TSSymbol sym = ts_node_grammar_symbol(node);
switch (sym) {
case sym_for_statement:
case sym_for_range_loop:
trace->idxLastForSym = trace->idx;
weight += 1;
break;
case sym_declaration:
trace->lnLastDecl = currentLine(node);
// Do not count variable declarations inside for-statement
// Check if the following is present:
// for_statement / for_range_loop
// for
// (
// declaration
if (trace->idxLastForSym != (trace->idx - 3)) {
weight += 1;
}
break;
case sym_do_statement:
weight += 2;
break;
case sym_type_definition:
trace->idxLastTypeDef = trace->idx;
weight += 1;
break;
case sym_struct_specifier:
case sym_class_specifier:
if (trace->idxLastTypeDef == (trace->idx - 2)) {
break;
}
if (trace->lnLastDecl == currentLine(node)) {
break;
}
if (trace->lnLastExpr == currentLine(node)) {
break;
}
weight += 1;
break;
case sym_enum_specifier:
case sym_union_specifier:
if (trace->idxLastTypeDef == (trace->idx - 2)
|| trace->lnLastDecl == currentLine(node)) {
break;
}
weight += 1;
break;
case sym__top_level_expression_statement:
case sym_expression_statement:
trace->lnLastExpr = currentLine(node);
weight += 1;
break;
case sym_if_statement:
// else-if counts as one
// Nodes are: else_clause -> else -> if_statement
if (trace->idxLastElse == (trace->idx - 2)) {
break;
}
weight += 1;
break;
case sym_else_clause:
trace->idxLastElse = trace->idx;
weight += 1;
break;
case sym_preproc_directive:
case sym_preproc_include:
case sym_preproc_def:
case sym_preproc_function_def:
case sym_preproc_if:
case sym_preproc_ifdef:
case sym_preproc_else:
case sym_preproc_elif:
case sym_preproc_elifdef:
case sym_function_definition:
case sym__type_definition_type:
case sym__type_definition_declarators:
case sym__declaration_modifiers:
case sym__declaration_specifiers:
case sym_linkage_specification:
case sym_attribute_specifier:
case sym_attribute:
case sym_declaration_list:
case sym__declarator:
case sym__type_declarator:
case sym__abstract_declarator:
case sym_attributed_declarator:
case sym_attributed_type_declarator:
case sym_type_specifier:
case sym_field_declaration:
case sym_enumerator:
case sym_attributed_statement:
case sym_statement:
case sym__top_level_statement:
case sym_labeled_statement:
case sym_switch_statement:
case sym_case_statement:
case sym_while_statement:
case sym_return_statement:
case sym_break_statement:
case sym_continue_statement:
case sym_goto_statement:
case sym_expression:
case sym_inline_method_definition:
case sym_constructor_or_destructor_definition:
case sym_friend_declaration:
case sym_template_declaration:
case sym_template_instantiation:
case sym_namespace_definition:
case sym_namespace_alias_definition:
case sym_using_declaration:
case sym_alias_declaration:
case sym_static_assert_declaration:
case sym_concept_definition:
case sym_co_return_statement:
case sym_co_yield_statement:
case sym_throw_statement:
case sym_try_statement:
case sym_catch_clause:
weight += 1;
default:
break;
}
return weight;
}

void evaluateNodeCpp(TSNode node, NodeEvalTrace* trace) {
trace->result->count += evaluateNodeWeightCppImpl(node, trace);
trace->idx++;
}
13 changes: 12 additions & 1 deletion src/lib/include/reckon/reckon.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extern "C" {
* The total number of supported text formats, including
* supported programming languages.
*/
#define RECKON_NUM_SUPPORTED_FORMATS 16
#define RECKON_NUM_SUPPORTED_FORMATS 17

/**
* Macro to create a format option bitmask.
Expand Down Expand Up @@ -197,6 +197,11 @@ typedef enum RcnTextFormat {
*/
RCN_LANG_PYTHON,

/**
* Source files for the C++ programming language.
*/
RCN_LANG_CPP,

/**
* Source files for the JavaScript programming language.
*/
Expand Down Expand Up @@ -806,6 +811,12 @@ typedef enum RcnFormatOption {
*/
RCN_OPT_LANG_PYTHON = RECKON_MK_FRMT_OPT(RCN_LANG_PYTHON),

/**
* Option to select statistics for source code files written in
* the C++ programming language.
*/
RCN_OPT_LANG_CPP = RECKON_MK_FRMT_OPT(RCN_LANG_CPP),

/**
* Option to select statistics for source code files written in
* the JavaScript programming language.
Expand Down
7 changes: 7 additions & 0 deletions src/lib/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ add_test_suite(
TEST_SUITE_LINK ${RECKON_TARGET_LIB_OBJ}
)

add_test_suite(
TEST_SUITE_NAME CppLanguageUnitTest
TEST_SUITE_TARGET test_lang_cpp
TEST_SUITE_SOURCE unit/c/test_lang_cpp.c
TEST_SUITE_LINK ${RECKON_TARGET_LIB_OBJ}
)

add_test_suite(
TEST_SUITE_NAME JavaScriptLanguageUnitTest
TEST_SUITE_TARGET test_lang_javascript
Expand Down
Loading
Loading