use super::expressions::csharpize_expression;
use super::syntax::split_top_level_args;
pub(super) fn match_big_integer_literal(s: &str) -> Option<(String, usize)> {
let bytes = s.as_bytes();
if bytes.is_empty() || !bytes[0].is_ascii_digit() {
return None;
}
let mut j = 0;
while j < bytes.len() && bytes[j].is_ascii_digit() {
j += 1;
}
if j < bytes.len() {
let after = bytes[j];
if after == b'x'
|| after == b'X'
|| after.is_ascii_alphabetic()
|| after == b'_'
|| after == b'.'
{
return None;
}
}
let digits = &s[..j];
if !decimal_exceeds_u64(digits) {
return None;
}
Some((format!("BigInteger.Parse(\"{digits}\")"), j))
}
pub(super) fn match_map_literal(rest: &str) -> Option<(String, usize)> {
let inner = rest.strip_prefix("Map(")?;
let close = matching_paren(inner)?;
let body = inner[..close].trim();
if body.is_empty() || body.contains("/*") {
return None;
}
let mut entries = Vec::new();
for entry in split_top_level_args(body) {
let (key, value) = split_top_level_colon(entry)?;
entries.push(format!(
"[{}] = {}",
csharpize_expression(key.trim()),
csharpize_expression(value.trim())
));
}
let consumed = "Map(".len() + close + 1;
Some((
format!("new Map<object, object> {{ {} }}", entries.join(", ")),
consumed,
))
}
fn matching_paren(s: &str) -> Option<usize> {
let bytes = s.as_bytes();
let mut depth = 0i32;
let mut in_string: Option<u8> = None;
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if let Some(quote) = in_string {
if b == b'\\' && i + 1 < bytes.len() {
i += 2;
continue;
}
if b == quote {
in_string = None;
}
i += 1;
continue;
}
match b {
b'"' | b'\'' => in_string = Some(b),
b')' if depth == 0 => return Some(i),
b'(' | b'[' | b'{' => depth += 1,
b')' | b']' | b'}' => depth -= 1,
_ => {}
}
i += 1;
}
None
}
fn split_top_level_colon(entry: &str) -> Option<(&str, &str)> {
let bytes = entry.as_bytes();
let mut depth = 0i32;
let mut in_string: Option<u8> = None;
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if let Some(quote) = in_string {
if b == b'\\' && i + 1 < bytes.len() {
i += 2;
continue;
}
if b == quote {
in_string = None;
}
i += 1;
continue;
}
match b {
b'"' | b'\'' => in_string = Some(b),
b'(' | b'[' | b'{' => depth += 1,
b')' | b']' | b'}' => depth -= 1,
b':' if depth == 0 => return Some((&entry[..i], &entry[i + 1..])),
_ => {}
}
i += 1;
}
None
}
pub(super) fn match_big_byte_literal(s: &str) -> Option<(String, usize)> {
let bytes = s.as_bytes();
if bytes.len() < 2 || bytes[0] != b'0' || !(bytes[1] == b'x' || bytes[1] == b'X') {
return None;
}
let mut j = 2;
while j < bytes.len() && bytes[j].is_ascii_hexdigit() {
j += 1;
}
let hex = &s[2..j];
if hex.len() <= 16 || hex.len() % 2 != 0 {
return None;
}
if j < bytes.len() && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_') {
return None;
}
let mut rendered = String::from("new byte[] { ");
for (idx, pair) in hex.as_bytes().chunks_exact(2).enumerate() {
if idx > 0 {
rendered.push_str(", ");
}
rendered.push_str("0x");
rendered.push(pair[0] as char);
rendered.push(pair[1] as char);
}
rendered.push_str(" }");
Some((rendered, j))
}
fn decimal_exceeds_u64(digits: &str) -> bool {
const U64_MAX: &str = "18446744073709551615";
let trimmed = digits.trim_start_matches('0');
let significant = if trimmed.is_empty() { "0" } else { trimmed };
match significant.len().cmp(&U64_MAX.len()) {
std::cmp::Ordering::Greater => true,
std::cmp::Ordering::Less => false,
std::cmp::Ordering::Equal => significant > U64_MAX,
}
}