use super::super::super::HighLevelEmitter;
use super::super::util::{is_else_open, is_if_open};
use super::parse::{is_temp, parse_label_line};
pub(super) fn extract_block_body(
statements: &[String],
header_index: usize,
) -> Option<(Vec<String>, usize)> {
let end = HighLevelEmitter::find_block_end(statements, header_index)?;
let body = statements
.get(header_index + 1..end)
.unwrap_or_default()
.to_vec();
Some((body, end))
}
pub(super) fn collect_trivia(statements: &[String], mut index: usize) -> (Vec<String>, usize) {
let mut trivia = Vec::new();
while index < statements.len() {
let trimmed = statements[index].trim();
if trimmed.is_empty() || trimmed.starts_with("//") {
trivia.push(statements[index].clone());
index += 1;
continue;
}
break;
}
(trivia, index)
}
pub(super) fn find_first_if_in_range(
statements: &[String],
start: usize,
end: usize,
) -> Option<usize> {
let mut index = start;
while index < end {
let trimmed = statements[index].trim();
if trimmed.is_empty() || trimmed.starts_with("//") {
index += 1;
continue;
}
if is_if_open(trimmed) {
return Some(index);
}
index += 1;
}
None
}
pub(super) fn find_next_if_after_case_prelude(
statements: &[String],
start: usize,
) -> Option<usize> {
let mut index = start;
while index < statements.len() {
let trimmed = statements[index].trim();
if trimmed.is_empty() || trimmed.starts_with("//") {
index += 1;
continue;
}
if is_if_open(trimmed) {
return Some(index);
}
if let Some(assign) = HighLevelEmitter::parse_assignment(statements[index].as_str()) {
if is_temp(&assign.lhs) && temp_consumed_by_next_code(statements, index, &assign.lhs) {
index += 1;
continue;
}
}
return None;
}
None
}
pub(super) fn temp_consumed_by_next_code(statements: &[String], index: usize, temp: &str) -> bool {
statements
.iter()
.skip(index + 1)
.find(|stmt| {
let trimmed = stmt.trim();
!trimmed.is_empty() && !trimmed.starts_with("//")
})
.is_some_and(|stmt| HighLevelEmitter::contains_identifier(stmt, temp))
}
pub(super) fn case_body_is_switch_safe(body: &[String], scrutinee: &str) -> bool {
if body_ends_with_terminator(body) {
return true;
}
!body.iter().any(|line| statement_reassigns(line, scrutinee))
}
pub(super) fn find_label_after(statements: &[String], start: usize, label: &str) -> Option<usize> {
let needle = format!("{label}:");
let mut index = start;
while index < statements.len() {
if statements[index].trim() == needle {
return Some(index);
}
index += 1;
}
None
}
pub(super) fn find_label_in_range(
statements: &[String],
start: usize,
end: usize,
label: &str,
) -> Option<usize> {
let needle = format!("{label}:");
let mut index = start;
while index < end {
if statements[index].trim() == needle {
return Some(index);
}
index += 1;
}
None
}
pub(super) fn find_else_block_after(statements: &[String], start: usize) -> Option<(usize, usize)> {
let (_, header) = collect_trivia(statements, start);
if header >= statements.len() || !is_else_open(statements[header].trim()) {
return None;
}
let end = HighLevelEmitter::find_block_end(statements, header)?;
Some((header, end))
}
pub(super) fn find_label_body_end(statements: &[String], start: usize) -> usize {
let mut index = start;
while index < statements.len() {
if index > start && parse_label_line(statements[index].as_str()).is_some() {
break;
}
index += 1;
}
index.saturating_sub(1)
}
pub(super) fn end_of_if_chain(statements: &[String], start: usize) -> Option<usize> {
let if_end = HighLevelEmitter::find_block_end(statements, start)?;
let (_, next) = collect_trivia(statements, if_end + 1);
if next < statements.len() && is_else_open(statements[next].trim()) {
return HighLevelEmitter::find_block_end(statements, next);
}
Some(if_end)
}
fn body_ends_with_terminator(body: &[String]) -> bool {
for line in body.iter().rev() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with("//") || trimmed == "{" || trimmed == "}" {
continue;
}
return is_terminator_statement(trimmed);
}
false
}
fn is_terminator_statement(line: &str) -> bool {
let trimmed = line.trim();
trimmed == "return;"
|| trimmed.starts_with("return ")
|| trimmed.starts_with("throw")
|| trimmed.starts_with("abort")
|| trimmed.starts_with("goto ")
|| trimmed == "break;"
|| trimmed == "continue;"
}
fn statement_reassigns(line: &str, scrutinee: &str) -> bool {
HighLevelEmitter::parse_assignment(line).is_some_and(|assignment| assignment.lhs == scrutinee)
}