use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::ffi::OsStr;
use clap::{Parser, Subcommand};
mod compile;
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Compile {
#[arg(value_parser = valid_spec_file)]
spec: PathBuf,
#[arg(value_parser = valid_map_file)]
map: PathBuf,
#[arg(short,long,default_value="rust")]
impl_str: String,
#[arg(short,long, value_name = "PATH", value_parser=valid_location)]
output: Option<PathBuf>,
#[arg(short,long, value_name = "PATH", value_parser=valid_location)]
bounds: Option<PathBuf>,
#[arg(long,default_value_t=false)]
disable_booleanizer: bool,
#[arg(long,default_value_t=false)]
disable_aux: bool,
#[arg(long,default_value_t=false)]
disable_rewrite: bool,
#[arg(long,default_value_t=false)]
disable_cse: bool,
#[arg(long,default_value_t=false)]
enable_sat: bool,
#[arg(long)]
timeout_sat: Option<i32>,
},
Run {
#[arg(value_parser = valid_spec_input_file)]
spec: PathBuf,
#[arg(value_parser = valid_trace_file)]
trace: PathBuf,
#[arg(short, long, value_name = "PATH", value_parser=valid_location)]
output: Option<PathBuf>,
#[arg(long,default_value_t=false)]
disable_contracts: bool,
#[arg(long,default_value_t=false)]
enable_aux: bool,
},
}
fn valid_spec_input_file(s: &str) -> Result<PathBuf, String> {
let file : PathBuf = s
.parse()
.map_err(|_| format!("`{s}` isn't a file"))?;
if !file.exists() {
return Err(format!(
"{} is not valid file",file.to_string_lossy()
))
}
if file.extension().and_then(OsStr::to_str) == Some("c2po") ||
file.extension().and_then(OsStr::to_str) == Some("mltl") ||
file.extension().and_then(OsStr::to_str) == Some("bin"){
Ok(file as PathBuf)
} else {
Err(format!(
"{} is not a .c2po, .mltl, or spec.bin file",file.extension().and_then(OsStr::to_str).unwrap()
))
}
}
fn valid_spec_file(s: &str) -> Result<PathBuf, String> {
let file : PathBuf = s
.parse()
.map_err(|_| format!("`{s}` isn't a file"))?;
if !file.exists() {
return Err(format!(
"{} is not valid file",file.to_string_lossy()
))
}
if file.extension().and_then(OsStr::to_str) == Some("c2po") ||
file.extension().and_then(OsStr::to_str) == Some("mltl") {
Ok(file as PathBuf)
} else {
Err(format!(
"{s} is not a .c2po or .mltl file"
))
}
}
fn valid_trace_file(s: &str) -> Result<PathBuf, String> {
let file : PathBuf = s
.parse()
.map_err(|_| format!("`{s}` isn't a file"))?;
if !file.exists() {
return Err(format!(
"{} is not valid file",file.to_string_lossy()
))
}
if file.extension().and_then(OsStr::to_str) == Some("csv"){
Ok(file as PathBuf)
} else {
Err(format!(
"{} is not a .csv file", file.extension().and_then(OsStr::to_str).unwrap()
))
}
}
fn valid_map_file(s: &str) -> Result<PathBuf, String> {
let file : PathBuf = s
.parse()
.map_err(|_| format!("`{s}` isn't a file"))?;
if !file.exists() {
return Err(format!(
"{} is not valid file",file.to_string_lossy()
))
}
if file.extension().and_then(OsStr::to_str) == Some("csv") ||
file.extension().and_then(OsStr::to_str) == Some("map"){
Ok(file as PathBuf)
} else {
Err(format!(
"{s} is not a .csv or .map file"
))
}
}
fn valid_location(s: &str) -> Result<PathBuf, String> {
let path : PathBuf = s
.parse()
.map_err(|_| format!("`{s}` isn't a path"))?;
if path.exists() {
Ok(path as PathBuf)
} else {
Err(format!(
"{} is not valid location",path.to_string_lossy()
))
}
}
fn main() {
let cli = Cli::parse();
match &cli.command {
Some(Commands::Run { spec, trace, output, disable_contracts, enable_aux}) => {
let spec_file: Vec<u8>;
if spec.extension().and_then(OsStr::to_str) == Some("c2po") ||
spec.extension().and_then(OsStr::to_str) == Some("mltl") {
let random_file = srfng::Generator::new().generate().as_str().to_owned();
compile::c2po_compile(spec.to_str().unwrap(),
trace.to_str().unwrap(),
"",
"rust",
&random_file,
"",
true,
*enable_aux || !disable_contracts,
true,
true,
false,
3600,
);
let new_spec = PathBuf::from("./".to_owned() + &random_file);
spec_file = fs::read(new_spec).expect("Error opening specification file");
let _ = fs::remove_file("./".to_owned() + &random_file);
} else {
spec_file = fs::read(spec).expect("Error opening specification file");
}
let mut monitor = r2u2_core::get_monitor(&spec_file);
let signal_file: fs::File = fs::File::open(trace).expect("Error opening signal CSV file");
let mut reader = csv::ReaderBuilder::new().trim(csv::Trim::All).has_headers(false).comment(Some(b'#')).from_reader(signal_file);
if output.is_some(){
let mut out_location = output.clone().unwrap();
out_location.push("r2u2_out.log");
let mut output_file: fs::File = fs::File::create(out_location).expect("Error creating output file");
for result in reader.records() {
let record = &result.expect("Error reading signal values");
let first_element = record.get(0).expect("Error reading signal values");
if first_element.starts_with('@') {
let end_idx = first_element.find(" ").unwrap_or(1);
match first_element[1..end_idx].parse::<u32>() {
Ok(n) => { monitor.time_stamp = n; }
Err(_e) => {}
}
r2u2_core::load_string_signal(&mut monitor, 0, &first_element[end_idx+1..first_element.len()]);
} else {
r2u2_core::load_string_signal(&mut monitor, 0, record.get(0).expect("Error reading signal values"));
}
for n in 1..record.len(){
r2u2_core::load_string_signal(&mut monitor, n, record.get(n).expect("Error reading signal values"));
}
if r2u2_core::monitor_step(&mut monitor) {
if *enable_aux {
for out in r2u2_core::get_output_buffer(&monitor) {
let _ = output_file.write_fmt(format_args!("{} ({}):{},{}\n", out.spec_str, out.spec_num, out.verdict.time, if out.verdict.truth {"T"} else {"F"}));
}
} else {
for out in r2u2_core::get_output_buffer(&monitor) {
let _ = output_file.write_fmt(format_args!("{}:{},{}\n", out.spec_num, out.verdict.time, if out.verdict.truth {"T"} else {"F"}));
}
}
if !disable_contracts {
for out in r2u2_core::get_contract_buffer(&monitor) {
let _ = output_file.write_fmt(format_args!("Contract {} {} at {}\n", out.spec_str, if out.status == r2u2_core::AGC_VERIFIED {"verified"} else if out.status == r2u2_core::AGC_INVALID {"invalid"} else {"inactive"}, out.time));
}
}
} else {
let _ = output_file.write_fmt(format_args!("Overflow occurred!!!!\n"));
}
}
println!("Output written to {}/r2u2_out.log", output.clone().unwrap().to_string_lossy());
} else{
for result in reader.records() {
let record = &result.expect("Error reading signal values");
let first_element = record.get(0).expect("Error reading signal values");
if first_element.starts_with('@') {
let end_idx = first_element.find(" ").unwrap_or(1);
match first_element[1..end_idx].parse::<u32>() {
Ok(n) => { monitor.time_stamp = n; }
Err(_e) => {}
}
r2u2_core::load_string_signal(&mut monitor, 0, &first_element[end_idx+1..first_element.len()]);
} else {
r2u2_core::load_string_signal(&mut monitor, 0, record.get(0).expect("Error reading signal values"));
}
for n in 1..record.len(){
r2u2_core::load_string_signal(&mut monitor, n, record.get(n).expect("Error reading signal values"));
}
if r2u2_core::monitor_step(&mut monitor) {
if *enable_aux {
for out in r2u2_core::get_output_buffer(&monitor) {
println!("{} ({}):{},{}", out.spec_str, out.spec_num, out.verdict.time, if out.verdict.truth {"T"} else {"F"} );
}
} else {
for out in r2u2_core::get_output_buffer(&monitor) {
println!("{}:{},{}", out.spec_num, out.verdict.time, if out.verdict.truth {"T"} else {"F"} );
}
}
if !disable_contracts {
for out in r2u2_core::get_contract_buffer(&monitor) {
println!("Contract {} {} at {}", out.spec_str, if out.status == r2u2_core::AGC_VERIFIED {"verified"} else if out.status == r2u2_core::AGC_INVALID {"invalid"} else {"inactive"}, out.time);
}
}
} else {
println!("Overflow occurred!!!!")
}
}
}
},
Some(Commands::Compile { spec, map, impl_str, output, bounds, disable_booleanizer,
disable_aux, disable_rewrite, disable_cse, enable_sat, timeout_sat}) => {
let mut out_location: String;
if output.is_some(){
out_location = output.clone().unwrap_or_else(PathBuf::new).to_str().unwrap_or(".").to_owned();
out_location.push_str("/spec.bin");
} else{
out_location = "spec.bin".to_owned();
}
compile::c2po_compile(spec.to_str().unwrap(),
if map.extension().and_then(OsStr::to_str) == Some("csv") { map.to_str().unwrap() } else {""},
if map.extension().and_then(OsStr::to_str) == Some("map") { map.to_str().unwrap() } else {""},
impl_str,
&out_location,
bounds.clone().unwrap_or_else(PathBuf::new).to_str().unwrap_or(""),
!disable_booleanizer,
!disable_aux,
!disable_rewrite,
!disable_cse,
enable_sat.to_owned(),
if timeout_sat.is_some() {timeout_sat.unwrap()} else {3600},
);
}
_ => {}
}
}