brokk_bifrost_cpp/
clones.rs1use brokk_bifrost_core::analyzer::tree_walk::push_children_reversed;
8use tree_sitter::{Node, Parser};
9
10const CPP_CLONE_AST_IDENTIFIER_TYPES: &[&str] = &[
11 "identifier",
12 "field_identifier",
13 "namespace_identifier",
14 "type_identifier",
15];
16const CPP_CLONE_AST_STRING_TYPES: &[&str] = &["string_literal", "raw_string_literal"];
17const CPP_CLONE_AST_NUMBER_TYPES: &[&str] = &["number_literal"];
18
19pub fn cpp_clone_parser() -> Parser {
20 let mut parser = Parser::new();
21 parser
22 .set_language(&tree_sitter_cpp::LANGUAGE.into())
23 .expect("failed to load cpp parser");
24 parser
25}
26
27fn normalize_cpp_clone_leaf_token(node: Node<'_>, source: &str) -> String {
28 let kind = node.kind();
29 let token = source
30 .get(node.start_byte()..node.end_byte())
31 .unwrap_or("")
32 .trim();
33 if token.is_empty() || kind == "comment" {
34 return String::new();
35 }
36 if CPP_CLONE_AST_IDENTIFIER_TYPES.contains(&kind) {
37 return "ID".to_string();
38 }
39 if CPP_CLONE_AST_STRING_TYPES.contains(&kind) {
40 return "STR".to_string();
41 }
42 if CPP_CLONE_AST_NUMBER_TYPES.contains(&kind) {
43 return "NUM".to_string();
44 }
45 if matches!(token, "true" | "false") {
46 return "BOOL".to_string();
47 }
48 if token.chars().count() == 1 && token.chars().all(|ch| !ch.is_alphanumeric()) {
49 return format!("OP:{token}");
50 }
51 format!("T:{kind}")
52}
53
54pub fn cpp_clone_profile(parser: &mut Parser, source: &str) -> (Vec<String>, String) {
57 let Some(tree) = parser.parse(source, None) else {
58 return (Vec::new(), String::new());
59 };
60 let mut normalized_tokens = Vec::new();
61 let mut ast_labels = Vec::new();
62 let mut stack = vec![tree.root_node()];
63 while let Some(node) = stack.pop() {
64 if cpp_is_ignorable_clone_logging_node(node, source) {
65 continue;
66 }
67 ast_labels.push(normalize_cpp_clone_ast_label(node, source));
68 if node.named_child_count() == 0 {
69 let token = normalize_cpp_clone_leaf_token(node, source);
70 if !token.is_empty() {
71 normalized_tokens.push(token);
72 }
73 }
74 push_children_reversed(node, &mut stack);
75 }
76 (normalized_tokens, ast_labels.join("|"))
77}
78
79fn normalize_cpp_clone_ast_label(node: Node<'_>, source: &str) -> String {
80 let kind = node.kind();
81 let text = source
82 .get(node.start_byte()..node.end_byte())
83 .unwrap_or("")
84 .trim();
85 if CPP_CLONE_AST_IDENTIFIER_TYPES.contains(&kind) {
86 return "ID".to_string();
87 }
88 if CPP_CLONE_AST_STRING_TYPES.contains(&kind) {
89 return "STR".to_string();
90 }
91 if CPP_CLONE_AST_NUMBER_TYPES.contains(&kind) {
92 return "NUM".to_string();
93 }
94 if matches!(text, "true" | "false") {
95 return "BOOL".to_string();
96 }
97 format!("N:{kind}")
98}
99
100fn cpp_is_ignorable_clone_logging_node(node: Node<'_>, source: &str) -> bool {
101 if node.kind() != "expression_statement" {
102 return false;
103 }
104 let text = source
105 .get(node.start_byte()..node.end_byte())
106 .unwrap_or("")
107 .trim();
108 text.contains("std::cout")
109 || text.contains("std::cerr")
110 || text.contains("std::clog")
111 || text.starts_with("printf(")
112}