alpha-shell 0.3.0

A transpiler for the AlphaShell language
use super::{
  error::{Error, TranspileResult},
  transpiler::{BlockType, Transpiler},
  value,
};
use crate::parse::{
  function_call::{FunctionCall, Next},
  node::Node,
};

fn transpile_next(t: &mut Transpiler, next: &Next, node: &Node) -> TranspileResult<String> {
  match next {
    Next::Call(call) => transpile_inner(t, call, node),
    Next::File(file) => Ok(format!(">{file}")),
  }
}

pub fn transpile_inner(
  t: &mut Transpiler,
  call: &FunctionCall,
  node: &Node,
) -> TranspileResult<String> {
  let FunctionCall {
    name,
    args,
    is_daemon,
    next,
  } = call;

  if name == "$" {
    t.push_block(BlockType::Arithmetics);

    let mut transpiled_args = Vec::new();

    for arg in args {
      transpiled_args.push(value::transpile_inner(t, arg, node)?);
    }

    t.pop_block();

    let args = transpiled_args.join(" ");

    if matches!(t.get_block(), Some(BlockType::Generic)) {
      return Ok(t.use_indent(&format!("(( {args} ))")));
    }

    return Ok(format!("$(( {} ))", args));
  }

  let basic_call = if args.is_empty() {
    name.clone()
  } else {
    let mut transpiled_args = vec![];
    for arg in args {
      transpiled_args.push(value::transpile_inner(t, arg, node)?);
    }
    let args = transpiled_args.join(" ");

    format!("{name} {args}")
  };

  let call = if let Some(next) = next {
    t.push_block(BlockType::FunctionCall);
    let next = transpile_next(t, next, node)?;
    t.pop_block();
    format!("{basic_call} | {next}")
  } else if *is_daemon {
    format!("{basic_call} &")
  } else {
    basic_call
  };

  let call = match t.get_block() {
    Some(BlockType::FunctionCall) => call,
    Some(BlockType::Expression) => format!(r#""$({})""#, call),
    _ => t.use_indent(&call),
  };

  Ok(call)
}

pub fn transpile(t: &mut Transpiler, node: &Node) -> TranspileResult<String> {
  match node {
    Node::FunctionCall(call) => transpile_inner(t, call, node),
    _ => Err(Error::new("Invalid node type", node)),
  }
}