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