use std::io::Write as _;
use std::path::PathBuf;
use clap::Parser;
use miette::{IntoDiagnostic, WrapErr};
#[derive(Debug, Parser)]
pub(crate) struct Args {
input: PathBuf,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(long, conflicts_with = "output")]
stdout: bool,
}
pub(crate) fn exec(args: Args) -> miette::Result<()> {
let source = std::fs::read_to_string(&args.input)
.into_diagnostic()
.wrap_err_with(|| format!("failed to read '{}'", args.input.display()))?;
let name = args.input.display().to_string();
let lines = xqasm::parse(&source, &name)?;
let program = xqasm::assemble(&lines, &source, &name)?;
let encoded = program.encode();
if args.stdout {
std::io::stdout()
.write_all(&encoded)
.into_diagnostic()
.wrap_err("failed to write bytecode to stdout")?;
} else {
let out_path = args.output.unwrap_or_else(|| {
let mut p = args.input.clone();
let _ = p.set_extension("xqb");
p
});
std::fs::write(&out_path, &encoded)
.into_diagnostic()
.wrap_err_with(|| format!("failed to write '{}'", out_path.display()))?;
eprintln!(
"assembled {} instructions ({} bytes) -> {}",
instruction_count(program.code()),
encoded.len(),
out_path.display(),
);
}
Ok(())
}
fn instruction_count(buf: &[u8]) -> usize {
xqvm::InstructionStream::new(buf)
.filter_map(Result::ok)
.count()
}