use std::{path::PathBuf, time::Instant};
use anyhow::{bail, Result};
use ariadne::Source;
use clap::Parser;
use edlang_codegen_llvm::linker::{link_binary, link_shared_lib};
use edlang_lowering::lower_modules;
use edlang_session::{DebugInfo, OptLevel, Session};
#[derive(Parser, Debug)]
#[command(author, version, about = "edlang compiler driver", long_about = None, bin_name = "edlangc")]
pub struct CompilerArgs {
input: PathBuf,
#[arg(short, long, default_value_t = false)]
release: bool,
#[arg(short = 'O', long)]
optlevel: Option<u8>,
#[arg(long)]
debug_info: Option<bool>,
#[arg(short, long, default_value_t = false)]
library: bool,
#[arg(long, default_value_t = false)]
ast: bool,
#[arg(long, default_value_t = false)]
ir: bool,
#[arg(long, default_value_t = false)]
llvm: bool,
#[arg(long, default_value_t = false)]
asm: bool,
}
pub fn main() -> Result<()> {
tracing_subscriber::fmt::init();
let args = CompilerArgs::parse();
compile_single_file(args)?;
Ok(())
}
pub fn compile_single_file(args: CompilerArgs) -> Result<()> {
if !args.input.is_file() {
bail!("Input is not a file");
}
let start_time = Instant::now();
tracing_subscriber::fmt::init();
let path = args.input.display().to_string();
let source = std::fs::read_to_string(&args.input)?;
let modules = edlang_parser::parse_ast(&source);
let modules = match modules {
Ok(modules) => modules,
Err(error) => {
let report = edlang_parser::error_to_report(&path, &error)?;
edlang_parser::print_report(&path, &source, report)?;
std::process::exit(1)
}
};
let cwd = std::env::current_dir()?;
let target_dir = cwd.join("target_ed/");
if !target_dir.exists() {
std::fs::create_dir_all(&target_dir)?;
}
let output_file = target_dir.join(PathBuf::from(args.input.file_name().unwrap()));
let output_file = if args.library {
output_file.with_extension(Session::get_platform_library_ext())
} else if cfg!(target_os = "windows") {
output_file.with_extension("exe")
} else {
output_file.with_extension("")
};
let session = Session {
file_path: args.input,
debug_info: if let Some(debug_info) = args.debug_info {
if debug_info {
DebugInfo::Full
} else {
DebugInfo::None
}
} else if args.release {
DebugInfo::None
} else {
DebugInfo::Full
},
optlevel: if let Some(optlevel) = args.optlevel {
match optlevel {
0 => OptLevel::None,
1 => OptLevel::Less,
2 => OptLevel::Default,
_ => OptLevel::Aggressive,
}
} else if args.release {
OptLevel::Aggressive
} else {
OptLevel::None
},
source: Source::from(source),
library: args.library,
target_dir,
output_file,
output_asm: args.asm,
output_llvm: args.llvm,
};
tracing::debug!("Input file: {:#?}", session.file_path);
tracing::debug!("Target dir: {:#?}", session.target_dir);
tracing::debug!("Output file: {:#?}", session.output_file);
tracing::debug!("Is library: {:#?}", session.library);
tracing::debug!("Optlevel: {:#?}", session.optlevel);
tracing::debug!("Debug Info: {:#?}", session.debug_info);
if args.ast {
println!("{:#?}", modules);
return Ok(());
}
let program_ir = match lower_modules(&modules) {
Ok(ir) => ir,
Err(error) => {
let report = edlang_check::lowering_error_to_report(error, &session);
let path = session.file_path.display().to_string();
report.eprint((path, session.source.clone()))?;
std::process::exit(1);
}
};
if args.ir {
println!("{:#?}", program_ir);
return Ok(());
}
let object_path = edlang_codegen_llvm::compile(&session, &program_ir).unwrap();
if session.library {
link_shared_lib(&object_path, &session.output_file)?;
} else {
link_binary(&object_path, &session.output_file)?;
}
let elapsed = start_time.elapsed();
tracing::debug!("Done in {:?}", elapsed);
Ok(())
}