use std::{fs, path::Path};
use clap::Parser;
use ezio::prelude::*;
use serde::{Deserialize, Serialize};
use shadow_rs::shadow;
shadow!(build);
#[derive(Serialize, Deserialize, Debug)]
enum Target {
#[serde(alias = "riscv")]
RISCV,
#[serde(alias = "wasm")]
WASM,
#[serde(alias = "shuorv")]
SHUORV,
}
#[derive(Serialize, Deserialize, Debug)]
struct Config {
#[serde(default)]
optimization: Vec<String>,
#[serde(default)]
emit_ir: bool,
#[serde(default)]
emit_asm: bool,
target: Target,
}
#[derive(clap::Parser, Debug)]
#[command(version, long_version = build::CLAP_LONG_VERSION, about, long_about = None)]
struct Args {
#[command(subcommand)]
action: Action,
}
#[derive(clap::Subcommand, Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
enum Action {
Build,
New { name: String },
}
fn main() {
let args = Args::parse();
let current_dir = std::env::current_dir().unwrap();
match args.action {
Action::Build => {
let config_path = current_dir.join("road.toml");
let target_dir = current_dir.join("target");
let asm_path = current_dir
.join("target")
.join(current_dir.file_name().unwrap());
let result_path = current_dir.join(current_dir.file_name().unwrap());
let config = file::read(config_path);
let config: Config = toml::from_str(&config).unwrap();
match config.target {
Target::RISCV => {
compile_to_asm(target_dir, ¤t_dir, &asm_path, config);
std::process::Command::new("shuasm")
.arg("-i")
.arg(format!("{}.asm", asm_path.display()))
.arg("-o")
.arg(current_dir.join(format!("{}.clef", asm_path.to_str().unwrap())))
.output()
.expect("failed to execute assembler");
std::process::Command::new("linker")
.arg("-i")
.arg(current_dir.join(format!("{}.clef", asm_path.to_str().unwrap())))
.arg("-o")
.arg(current_dir.join(format!("{}.clef", result_path.to_str().unwrap())))
.output()
.expect("failed to execute linker");
}
Target::WASM => {
unimplemented!()
}
Target::SHUORV => {
unimplemented!()
}
}
}
Action::New { name } => {
let project_dir = current_dir.join(name);
fs::create_dir_all(&project_dir).unwrap();
let config = Config {
optimization: vec![
"RemoveOnlyOnceStore".to_string(),
"RemoveLoadDirectlyAfterStore".to_string(),
"RemoveUnusedRegister".to_string(),
"MemoryToRegister".to_string(),
"RemoveUnusedRegister".to_string(),
],
emit_ir: false,
target: Target::RISCV,
emit_asm: true,
};
let config = toml::to_string(&config).unwrap();
file::write(project_dir.join("road.toml"), &config);
file::write(project_dir.join("main.come"), "fn main() -> () {}");
}
}
}
fn compile_to_asm(
target_dir: impl AsRef<Path>,
current_dir: impl AsRef<Path>,
target_filename: impl AsRef<Path>,
config: Config,
) {
let target_dir = target_dir.as_ref();
let current_dir = current_dir.as_ref();
fs::create_dir_all(target_dir).unwrap();
let mut compiler_cmd = std::process::Command::new("come");
compiler_cmd
.arg("-i")
.arg(current_dir.join("main.come").display().to_string())
.arg("-o")
.arg(format!(
"{}.asm",
target_dir.join(&target_filename).display()
));
if config.emit_ir {
compiler_cmd
.arg("--emit-ir")
.arg(format!("{}.ir", target_dir.join(target_filename).display()));
}
if !config.optimization.is_empty() {
let optimization = config.optimization.join(",");
compiler_cmd.arg("-O").arg(&optimization);
}
compiler_cmd.output().expect("failed to execute compiler");
}