lua2hcb_compiler/
main.rs

1mod asm;
2mod compile;
3mod ir;
4mod lua;
5mod meta;
6
7use anyhow::{anyhow, Context, Result};
8use clap::Parser;
9use std::fs;
10use std::path::PathBuf;
11
12#[derive(Parser, Debug)]
13#[command(name = "lua2hcb")]
14#[command(about = "Compile a constrained Lua 5.3 subset (decompiler IR style) back to HCB.")]
15struct Cli {
16    #[arg(long)]
17    meta: PathBuf,
18
19    #[arg(long)]
20    lua: PathBuf,
21
22    #[arg(short, long)]
23    out: PathBuf,
24}
25
26fn main() -> Result<()> {
27    let cli = Cli::parse();
28
29    let meta = meta::load_meta(&cli.meta)
30        .with_context(|| format!("loading meta: {}", cli.meta.display()))?;
31
32    let program = lua::parse_lua(&cli.lua).with_context(|| "parsing lua source")?;
33    if program.functions.is_empty() {
34        return Err(anyhow!("no functions found in lua input"));
35    }
36
37    let (items, layout) = compile::compile_program(&meta, &program).with_context(|| "compiling")?;
38
39    let mut emit_meta = meta.clone();
40    emit_meta.non_volatile_global_count = layout.non_volatile_count;
41    emit_meta.volatile_global_count = layout.volatile_count;
42
43    let (code, labels) = asm::assemble(&emit_meta, &items).with_context(|| "assembling")?;
44
45    let entry_point = labels
46        .get("fn:main")
47        .copied()
48        .ok_or_else(|| anyhow!("missing entry function: main"))?;
49
50    let sysdesc = asm::build_sysdesc(&emit_meta, entry_point).with_context(|| "building sysdesc")?;
51
52    let sys_desc_offset: u32 =
53        4 + u32::try_from(code.len()).map_err(|_| anyhow!("code too large"))?;
54
55    let mut out = Vec::new();
56    out.extend_from_slice(&sys_desc_offset.to_le_bytes());
57    out.extend_from_slice(&code);
58    out.extend_from_slice(&sysdesc);
59
60    fs::write(&cli.out, out).with_context(|| format!("writing output: {}", cli.out.display()))?;
61
62    Ok(())
63}