use super::escape::{escape_shell_string, escape_variable_name};
use super::posix::{classify_if_structure, get_indent, unwrap_single_if};
use crate::ir::shell_ir::Command;
use crate::ir::{ShellIR, ShellValue};
use crate::models::Result;
use std::fmt::Write;
impl super::posix::PosixEmitter {
pub(crate) fn emit_ir(&self, output: &mut String, ir: &ShellIR, indent: usize) -> Result<()> {
let ir_label = match ir {
ShellIR::Let { .. } => "Let",
ShellIR::Exec { .. } => "Exec",
ShellIR::If { .. } => "If",
ShellIR::Exit { .. } => "Exit",
ShellIR::Sequence(_) => "Sequence",
ShellIR::Noop => "Noop",
ShellIR::Function { .. } => "Function",
ShellIR::Echo { .. } => "Echo",
ShellIR::For { .. } => "For",
ShellIR::While { .. } => "While",
ShellIR::Case { .. } => "Case",
ShellIR::ForIn { .. } => "ForIn",
ShellIR::Break => "Break",
ShellIR::Continue => "Continue",
ShellIR::Return { .. } => "Return",
};
self.record_decision("ir_dispatch", ir_label, ir_label);
match ir {
ShellIR::Let { name, value, .. } => {
self.emit_let_statement(output, name, value, indent)
}
ShellIR::Exec { cmd, .. } => self.emit_exec_statement(output, cmd, indent),
ShellIR::If {
test,
then_branch,
else_branch,
} => self.emit_if_statement(output, test, then_branch, else_branch.as_deref(), indent),
ShellIR::Exit { code, message } => {
self.emit_exit_statement(output, (*code).into(), message.as_ref(), indent)
}
ShellIR::Sequence(items) => self.emit_sequence(output, items, indent),
ShellIR::Noop => self.emit_noop(output, indent),
ShellIR::Function { name, params, body } => {
self.emit_function(output, name, params, body, indent)
}
ShellIR::Echo { value } => self.emit_echo_statement(output, value, indent),
ShellIR::For {
var,
start,
end,
body,
} => self.emit_for_statement(output, var, start, end, body, indent),
ShellIR::While { condition, body } => {
self.emit_while_statement(output, condition, body, indent)
}
ShellIR::Case { scrutinee, arms } => {
self.emit_case_statement(output, scrutinee, arms, indent)
}
ShellIR::ForIn { var, items, body } => {
self.emit_for_in_statement(output, var, items, body, indent)
}
ShellIR::Break => {
let indent_str = get_indent(indent + 1);
writeln!(output, "{indent_str}break")?;
Ok(())
}
ShellIR::Continue => {
let indent_str = get_indent(indent + 1);
writeln!(output, "{indent_str}continue")?;
Ok(())
}
ShellIR::Return { value } => {
let indent_str = get_indent(indent + 1);
if let Some(val) = value {
let value_str = self.emit_shell_value(val)?;
writeln!(output, "{indent_str}echo {value_str}")?;
}
writeln!(output, "{indent_str}return")?;
Ok(())
}
}
}
pub(crate) fn emit_let_statement(
&self,
output: &mut String,
name: &str,
value: &ShellValue,
indent: usize,
) -> Result<()> {
let choice = match value {
ShellValue::String(_) => "single_quote",
ShellValue::Variable(_) => "variable_ref",
ShellValue::Concat(_) => "concat",
ShellValue::CommandSubst(_) => "cmd_subst",
ShellValue::Arithmetic { .. } => "arithmetic",
ShellValue::Bool(_) => "bool_literal",
_ => "other",
};
self.record_decision("assignment_value", choice, "Let");
let indent_str = get_indent(indent + 1);
let var_name = escape_variable_name(name);
let var_value = self.emit_assignment_value(value)?;
writeln!(output, "{indent_str}{var_name}={var_value}")?;
Ok(())
}
pub(crate) fn emit_assignment_value(&self, value: &ShellValue) -> Result<String> {
match value {
ShellValue::String(s) => {
if s.is_empty() {
Ok("''".to_string())
} else if !s.contains('\'') {
Ok(format!("'{s}'"))
} else {
Ok(escape_shell_string(s))
}
}
_ => self.emit_shell_value(value),
}
}
pub(crate) fn emit_exec_statement(
&self,
output: &mut String,
cmd: &Command,
indent: usize,
) -> Result<()> {
let choice = if cmd.program == "echo" || cmd.program == "printf" {
"builtin_echo"
} else if cmd.program.starts_with("rash_") {
"runtime_call"
} else {
"external_cmd"
};
self.record_decision("exec_command", choice, "Exec");
let indent_str = get_indent(indent + 1);
let command_str = self.emit_command(cmd)?;
writeln!(output, "{indent_str}{command_str}")?;
Ok(())
}
pub(crate) fn emit_if_statement(
&self,
output: &mut String,
test: &ShellValue,
then_branch: &ShellIR,
else_branch: Option<&ShellIR>,
indent: usize,
) -> Result<()> {
self.record_decision("if_structure", classify_if_structure(else_branch), "If");
let indent_str = get_indent(indent + 1);
let test_expr = self.emit_test_expression(test)?;
writeln!(output, "{indent_str}if {test_expr}; then")?;
self.emit_ir(output, then_branch, indent + 1)?;
if let Some(else_ir) = else_branch {
self.emit_else_branch(output, else_ir, indent)?;
}
writeln!(output, "{indent_str}fi")?;
Ok(())
}
pub(crate) fn emit_else_branch(
&self,
output: &mut String,
else_ir: &ShellIR,
indent: usize,
) -> Result<()> {
let indent_str = get_indent(indent + 1);
let unwrapped = unwrap_single_if(else_ir);
if let ShellIR::If {
test: elif_test,
then_branch: elif_then,
else_branch: elif_else,
} = unwrapped
{
let elif_expr = self.emit_test_expression(elif_test)?;
writeln!(output, "{indent_str}elif {elif_expr}; then")?;
self.emit_ir(output, elif_then, indent + 1)?;
if let Some(final_else) = elif_else {
self.emit_elif_chain(output, final_else, indent)?;
}
} else {
writeln!(output, "{indent_str}else")?;
self.emit_ir(output, else_ir, indent + 1)?;
}
Ok(())
}
pub(crate) fn emit_elif_chain(
&self,
output: &mut String,
else_ir: &ShellIR,
indent: usize,
) -> Result<()> {
let indent_str = get_indent(indent + 1);
let unwrapped = unwrap_single_if(else_ir);
if let ShellIR::If {
test: elif_test,
then_branch: elif_then,
else_branch: elif_else,
} = unwrapped
{
let elif_expr = self.emit_test_expression(elif_test)?;
writeln!(output, "{indent_str}elif {elif_expr}; then")?;
self.emit_ir(output, elif_then, indent + 1)?;
if let Some(final_else) = elif_else {
self.emit_elif_chain(output, final_else, indent)?;
}
} else {
writeln!(output, "{indent_str}else")?;
self.emit_ir(output, else_ir, indent + 1)?;
}
Ok(())
}
pub(crate) fn emit_exit_statement(
&self,
output: &mut String,
code: i32,
message: Option<&String>,
indent: usize,
) -> Result<()> {
let indent_str = get_indent(indent + 1);
if let Some(msg) = message {
let escaped_msg = escape_shell_string(msg);
writeln!(output, "{indent_str}echo {escaped_msg} >&2")?;
}
writeln!(output, "{indent_str}exit {code}")?;
Ok(())
}
pub(crate) fn emit_sequence(
&self,
output: &mut String,
items: &[ShellIR],
indent: usize,
) -> Result<()> {
if items.is_empty() {
self.emit_noop(output, indent)?;
} else {
for item in items {
self.emit_ir(output, item, indent)?;
}
}
Ok(())
}
pub(crate) fn emit_noop(&self, output: &mut String, indent: usize) -> Result<()> {
let indent_str = get_indent(indent + 1);
writeln!(output, "{indent_str}:")?;
Ok(())
}
pub(crate) fn emit_echo_statement(
&self,
output: &mut String,
value: &ShellValue,
indent: usize,
) -> Result<()> {
let indent_str = get_indent(indent + 1);
let value_str = self.emit_shell_value(value)?;
writeln!(output, "{indent_str}echo {value_str}")?;
Ok(())
}
pub(crate) fn emit_for_statement(
&self,
output: &mut String,
var: &str,
start: &ShellValue,
end: &ShellValue,
body: &ShellIR,
indent: usize,
) -> Result<()> {
self.record_decision("for_construct", "seq_range", "For");
let indent_str = get_indent(indent + 1);
let var_name = escape_variable_name(var);
let start_str = self.emit_shell_value(start)?;
let end_str = self.emit_shell_value(end)?;
writeln!(
output,
"{indent_str}for {var_name} in $(seq {start_str} {end_str}); do"
)?;
self.emit_ir(output, body, indent + 1)?;
writeln!(output, "{indent_str}done")?;
Ok(())
}
pub(crate) fn emit_for_in_statement(
&self,
output: &mut String,
var: &str,
items: &[ShellValue],
body: &ShellIR,
indent: usize,
) -> Result<()> {
self.record_decision("for_construct", "for_in_list", "ForIn");
let indent_str = get_indent(indent + 1);
let var_name = escape_variable_name(var);
let items_str: Vec<String> = items
.iter()
.map(|item| self.emit_shell_value(item))
.collect::<Result<Vec<_>>>()?;
let items_joined = items_str.join(" ");
writeln!(output, "{indent_str}for {var_name} in {items_joined}; do")?;
self.emit_ir(output, body, indent + 1)?;
writeln!(output, "{indent_str}done")?;
Ok(())
}
}
include!("posix_emit_ir_methods.rs");