use std::{
fs::File as StdFile, io::Write,
path::PathBuf,
process::Command,
};
use log::{info, warn};
use rem_utils::resolve_aeneas_path;
use crate::error::AENEASError;
pub fn coq_conversion(
original_llbc: &PathBuf,
refactored_llbc: &PathBuf,
out_dir: &Option<PathBuf>,
aeneas_path: &Option<PathBuf>,
) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>> {
info!("Converting LLBC files to CoQ files");
let aeneas_path: PathBuf = resolve_aeneas_path(aeneas_path)?;
let primitives_save_path: PathBuf = out_dir.clone().unwrap_or_else(|| {
original_llbc
.parent() .map(|p| p.to_path_buf())
.unwrap_or_else(|| original_llbc.clone())
});
create_primitives_file(primitives_save_path)?;
let original_coq_path: PathBuf = convert_llbc_to_coq(&aeneas_path, &original_llbc, out_dir.clone())?;
let refactored_coq_path: PathBuf = convert_llbc_to_coq(&aeneas_path, &refactored_llbc, out_dir.clone())?;
Ok((original_coq_path, refactored_coq_path))
}
fn convert_llbc_to_coq(
aeneas_path: &PathBuf,
llbc_path: &PathBuf,
out_dir: Option<PathBuf>,
) -> Result<PathBuf, Box<dyn std::error::Error>> {
let output_dir: PathBuf = out_dir.unwrap_or_else(|| {
llbc_path
.parent()
.map(|p| p.to_path_buf())
.unwrap_or_else(|| llbc_path.clone())
});
info!("Output directory: {:?}", output_dir);
let output = Command::new(aeneas_path)
.arg("-backend")
.arg("coq")
.arg(llbc_path)
.arg("-dest")
.arg(output_dir.clone())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
warn!("AENEAS failed to convert the LLBC file to CoQ");
warn!("Stderr: {}", stderr);
warn!("Stdout: {}", stdout);
return Err(Box::new(AENEASError::RuntimeError));
}
let file_stem = llbc_path
.file_stem()
.ok_or(AENEASError::InvalidPathConversion)?
.to_str()
.unwrap();
let coq_file_name = convert_llbc_to_coq_filename(file_stem);
let mut returned_path = output_dir.join(coq_file_name);
returned_path.set_extension("v");
Ok(returned_path)
}
fn create_primitives_file(
out_dir: PathBuf, ) -> Result<(), Box<dyn std::error::Error>> {
let dest_file: PathBuf = out_dir.join("Primitives.v");
if !dest_file.exists(){
let mut file: StdFile = StdFile::create(dest_file)?;
file.write_all(include_str!("Primitives.v").as_bytes())?;
} else {
info!("Skipping creation of `Primitives.v` as it already exists")
}
Ok(())
}
fn convert_llbc_to_coq_filename(file_stem: &str) -> String {
file_stem
.split('_')
.map(|s| {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect::<String>()
}