#![allow(
clippy::enum_glob_use,
clippy::too_many_lines,
clippy::wildcard_imports
)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use super::{Abc, Stats};
use crate::macros::tcl_bool_terminal_kinds;
use crate::*;
const TCL_ASSIGNMENT_COMMANDS: &[&[u8]] = &[b"incr", b"append", b"lappend"];
fn tcl_inspect_container(container_node: &Node, conditions: &mut f64) {
let mut node = *container_node;
let mut node_kind = node.kind_id().into();
let Some(parent) = node.parent() else { return };
let has_boolean_content = matches!(parent.kind_id().into(), Tcl::BinopExpr);
loop {
let is_not = matches!(node_kind, Tcl::UnaryExpr)
&& node
.child(0)
.is_some_and(|c| c.kind_id() == Tcl::BANG as u16);
if !is_not {
break;
}
let Some(child) = node.child(1) else { break };
node = child;
node_kind = node.kind_id().into();
if matches!(node_kind, tcl_bool_terminal_kinds!()) {
if has_boolean_content {
*conditions += 1.;
}
break;
}
}
}
fn tcl_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
let list_kind = list_node.kind_id().into();
let mut cursor = list_node.cursor();
if cursor.goto_first_child() {
loop {
let node = cursor.node();
let node_kind = node.kind_id().into();
if matches!(node_kind, tcl_bool_terminal_kinds!())
&& matches!(list_kind, Tcl::BinopExpr)
{
*conditions += 1.;
} else if node.is_named() {
tcl_inspect_container(&node, conditions);
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
impl Abc for TclCode {
fn compute<'a>(node: &Node<'a>, code: &'a [u8], stats: &mut Stats) {
match node.kind_id().into() {
Tcl::Set => {
stats.assignments += 1.;
}
Tcl::Command => {
if tcl_command_is_assignment(node, code) {
stats.assignments += 1.;
} else {
stats.branches += 1.;
}
}
Tcl::EQEQ
| Tcl::BANGEQ
| Tcl::LT
| Tcl::GT
| Tcl::LTEQ
| Tcl::GTEQ
| Tcl::Eq
| Tcl::Ne
| Tcl::In
| Tcl::Ni
| Tcl::TernaryExpr
| Tcl::Elseif
| Tcl::Else => {
stats.conditions += 1.;
}
Tcl::AMPAMP | Tcl::PIPEPIPE => {
if let Some(parent) = node.parent() {
tcl_count_unary_conditions(&parent, &mut stats.conditions);
}
}
_ => {}
}
}
}
fn tcl_command_is_assignment(node: &Node, code: &[u8]) -> bool {
let Some(first) = node.child(0) else {
return false;
};
let start = first.start_byte();
let end = first.end_byte();
if end > code.len() || start >= end {
return false;
}
let word = &code[start..end];
TCL_ASSIGNMENT_COMMANDS.contains(&word)
}