| 241 | } |
| 242 | |
| 243 | bool cbm_ast_profile_compute(TSNode func_body, const char *source, const char **param_names, |
| 244 | int param_count, cbm_ast_profile_t *out) { |
| 245 | if (ts_node_is_null(func_body)) { |
| 246 | return false; |
| 247 | } |
| 248 | |
| 249 | memset(out, 0, sizeof(*out)); |
| 250 | out->param_count = (uint16_t)param_count; |
| 251 | |
| 252 | uint32_t op_set[HALSTEAD_SET_SIZE]; |
| 253 | uint32_t operand_set[HALSTEAD_SET_SIZE]; |
| 254 | memset(op_set, 0, sizeof(op_set)); |
| 255 | memset(operand_set, 0, sizeof(operand_set)); |
| 256 | |
| 257 | int total_depth = 0; |
| 258 | int node_count = 0; |
| 259 | bool in_return = false; |
| 260 | bool in_condition = false; |
| 261 | |
| 262 | profile_frame_t stack[WALK_STACK_CAP]; |
| 263 | int top = 0; |
| 264 | stack[top++] = (profile_frame_t){func_body, 0}; |
| 265 | |
| 266 | while (top > 0) { |
| 267 | profile_frame_t frame = stack[--top]; |
| 268 | TSNode node = frame.node; |
| 269 | int depth = frame.depth; |
| 270 | uint32_t child_count = ts_node_child_count(node); |
| 271 | const char *kind = ts_node_type(node); |
| 272 | |
| 273 | if (!ts_node_is_named(node) && child_count == 0) { |
| 274 | /* Anonymous leaf (punctuation, keywords) — skip. */ |
| 275 | goto push_children; |
| 276 | } |
| 277 | |
| 278 | node_count++; |
| 279 | total_depth += depth; |
| 280 | |
| 281 | if ((uint16_t)depth > out->max_nesting_depth) { |
| 282 | out->max_nesting_depth = (uint16_t)depth; |
| 283 | } |
| 284 | |
| 285 | accumulate_control_flow(kind, out, &in_return); |
| 286 | accumulate_expressions(kind, out); |
| 287 | accumulate_halstead(kind, child_count, op_set, operand_set, out); |
| 288 | accumulate_data_flow(node, kind, child_count, source, param_names, param_count, in_return, |
| 289 | in_condition, out); |
| 290 | |
| 291 | /* Track context for data flow: are we inside a condition? */ |
| 292 | if (is_control_if(kind) || is_control_while(kind)) { |
| 293 | in_condition = true; |
| 294 | } |
| 295 | |
| 296 | push_children: |
| 297 | /* Reset context flags when leaving return/condition scope */ |
| 298 | if (is_return(kind)) { |
| 299 | in_return = false; |
| 300 | } |
no test coverage detected