pub(crate) fn syntax_heuristic_check(content: &[u8]) -> Option<String> {
let text = std::str::from_utf8(content).ok()?;
let stripped = strip_comments(text);
let stripped = strip_string_literals(&stripped);
let mut braces = 0i32;
let mut parens = 0i32;
let mut brackets = 0i32;
for c in stripped.chars() {
match c {
'{' => braces += 1,
'}' => braces -= 1,
'(' => parens += 1,
')' => parens -= 1,
'[' => brackets += 1,
']' => brackets -= 1,
_ => {}
}
}
if braces != 0 {
return Some(format!(
"unbalanced braces: {} more {} than {}",
braces.abs(),
if braces > 0 { "open" } else { "close" },
if braces > 0 { "close" } else { "open" }
));
}
if parens != 0 {
return Some(format!(
"unbalanced parentheses: {} more {} than {}",
parens.abs(),
if parens > 0 { "open" } else { "close" },
if parens > 0 { "close" } else { "open" }
));
}
if brackets != 0 {
return Some(format!(
"unbalanced brackets: {} more {} than {}",
brackets.abs(),
if brackets > 0 { "open" } else { "close" },
if brackets > 0 { "close" } else { "open" }
));
}
None
}
pub(crate) fn strip_comments(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut chars = text.chars().peekable();
while let Some(c) = chars.next() {
if c == '/' {
match chars.peek() {
Some('/') => {
chars.next();
for nc in chars.by_ref() {
if nc == '\n' {
out.push('\n');
break;
}
}
}
Some('*') => {
chars.next();
let mut prev = '\0';
for nc in chars.by_ref() {
if prev == '*' && nc == '/' {
break;
}
prev = nc;
}
}
_ => out.push(c),
}
} else if c == '"' {
out.push(c);
while let Some(nc) = chars.next() {
out.push(nc);
if nc == '\\' {
if let Some(escaped) = chars.next() {
out.push(escaped);
}
} else if nc == '"' {
break;
}
}
} else if c == '\'' {
out.push(c);
while let Some(nc) = chars.next() {
out.push(nc);
if nc == '\\' {
if let Some(escaped) = chars.next() {
out.push(escaped);
}
} else if nc == '\'' {
break;
}
}
} else {
out.push(c);
}
}
out
}
pub(crate) fn strip_string_literals(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut in_string = false;
let mut prev = '\0';
for c in text.chars() {
if c == '"' && prev != '\\' {
in_string = !in_string;
}
if !in_string {
out.push(c);
}
prev = c;
}
out
}