use super::super::types::BinOp;
pub(super) fn node_text<'a>(node: tree_sitter::Node, source: &'a [u8]) -> &'a str {
let range = node.byte_range();
std::str::from_utf8(&source[range.start..range.end]).unwrap_or("")
}
pub(super) fn unquote(s: &str) -> String {
let s = s.trim();
let stripped = strip_string_prefix(s);
if stripped.starts_with("\"\"\"") && stripped.ends_with("\"\"\"") && stripped.len() >= 6 {
return stripped[3..stripped.len() - 3].to_string();
}
if stripped.starts_with("'''") && stripped.ends_with("'''") && stripped.len() >= 6 {
return stripped[3..stripped.len() - 3].to_string();
}
if ((stripped.starts_with('"') && stripped.ends_with('"'))
|| (stripped.starts_with('\'') && stripped.ends_with('\'')))
&& stripped.len() >= 2
{
return stripped[1..stripped.len() - 1].to_string();
}
s.to_string()
}
fn strip_string_prefix(s: &str) -> &str {
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
match bytes[i] {
b'f' | b'F' | b'b' | b'B' | b'r' | b'R' | b'u' | b'U' => i += 1,
_ => break,
}
}
&s[i..]
}
pub(super) fn parse_integer(s: &str) -> Option<i64> {
let s = s.replace('_', "");
if s.starts_with("0x") || s.starts_with("0X") {
i64::from_str_radix(&s[2..], 16).ok()
} else if s.starts_with("0o") || s.starts_with("0O") {
i64::from_str_radix(&s[2..], 8).ok()
} else if s.starts_with("0b") || s.starts_with("0B") {
i64::from_str_radix(&s[2..], 2).ok()
} else {
s.parse::<i64>().ok()
}
}
pub(super) fn parse_float(s: &str) -> Option<f64> {
let s = s.replace('_', "");
s.parse::<f64>().ok()
}
pub(super) fn strip_numeric_suffix(s: &str) -> &str {
let bytes = s.as_bytes();
if bytes.is_empty() {
return s;
}
if s.starts_with("0x") || s.starts_with("0X") {
let mut end = bytes.len();
while end > 2 && matches!(bytes[end - 1], b'u' | b'U' | b'l' | b'L') {
end -= 1;
}
if end > 2 && end < bytes.len() {
return &s[..end];
}
return s;
}
for suffix in &[
"i128", "i64", "i32", "i16", "i8", "isize", "u128", "u64", "u32", "u16", "u8", "usize",
"f64", "f32",
] {
if s.ends_with(suffix) {
let end = s.len() - suffix.len();
if end > 0 {
return &s[..end];
}
}
}
let mut end = bytes.len();
while end > 0
&& matches!(
bytes[end - 1],
b'u' | b'U' | b'l' | b'L' | b'f' | b'F' | b'd' | b'D'
)
{
end -= 1;
}
if end > 0 && end < bytes.len() {
return &s[..end];
}
s
}
pub(super) fn text_to_binop(op: &str) -> BinOp {
match op.trim() {
"+" => BinOp::Add,
"-" => BinOp::Sub,
"*" => BinOp::Mul,
"/" => BinOp::Div,
"%" | "//" => BinOp::Mod,
"==" => BinOp::Eq,
"!=" => BinOp::NotEq,
"<" => BinOp::Lt,
">" => BinOp::Gt,
"<=" => BinOp::LtEq,
">=" => BinOp::GtEq,
"and" => BinOp::And,
"or" => BinOp::Or,
"&&" => BinOp::And,
"||" => BinOp::Or,
_ => BinOp::Add, }
}
pub(super) fn extract_binary_op(node: tree_sitter::Node, source: &[u8]) -> BinOp {
if let Some(op_node) = node.child_by_field_name("operator") {
return text_to_binop(node_text(op_node, source));
}
let child_count = node.child_count();
if child_count >= 3 {
for i in 0..child_count {
if let Some(child) = node.child(i) {
if !child.is_named() {
let text = node_text(child, source);
if matches!(
text,
"+" | "-"
| "*"
| "/"
| "%"
| "//"
| "=="
| "!="
| "<"
| ">"
| "<="
| ">="
| "and"
| "or"
| "&&"
| "||"
) {
return text_to_binop(text);
}
}
}
}
}
for i in 0..child_count {
if let Some(child) = node.child(i) {
let text = node_text(child, source);
if text == "and" || text == "or" {
return text_to_binop(text);
}
}
}
BinOp::Add }