use std::path::PathBuf;
use colored::*;
use log::{debug, error, info};
use paw;
use std::convert::TryFrom;
use std::fs;
use std::io::{self, prelude::*};
use libprop_sat_solver::formula::PropositionalFormula;
use libprop_sat_solver::tableaux_solver::{is_satisfiable, is_valid};
pub mod logger;
pub mod parser;
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, PartialEq, structopt::StructOpt)]
pub struct Args {
#[structopt(short = "d", long)]
debug: bool,
#[structopt(short = "c", long = "formula")]
single_formula: Option<String>,
#[structopt(short = "m", long)]
mode: Option<char>,
#[structopt(short = "i", long = "input")]
input_file: Option<PathBuf>,
#[structopt(short = "o", long = "output")]
output_file: Option<PathBuf>,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq)]
pub enum CliOutputMode {
Satisfiability,
Validity,
}
impl TryFrom<char> for CliOutputMode {
type Error = ();
fn try_from(c: char) -> Result<Self, Self::Error> {
match c.to_ascii_lowercase() {
's' => Ok(Self::Satisfiability),
'v' => Ok(Self::Validity),
_ => Err(()),
}
}
}
impl TryFrom<String> for CliOutputMode {
type Error = ();
fn try_from(s: String) -> Result<Self, Self::Error> {
match s.to_ascii_lowercase().as_ref() {
"sat" | "satisfiability" => Ok(Self::Satisfiability),
"val" | "validity" => Ok(Self::Validity),
_ => Err(()),
}
}
}
#[paw::main]
pub fn main(args: Args) -> io::Result<()> {
logger::setup(args.debug);
info!(
"{}: v{}",
"Propositional Tableau Solver".cyan().bold().underline(),
VERSION.unwrap_or("unknown version").yellow()
);
info!("arguments provided\n {:#?}", &args);
let mut inputs: Vec<String> = Vec::new();
if let Some(input) = &args.single_formula {
inputs.push(input.to_string());
} else if let Some(input_path) = &args.input_file {
let file = fs::File::open(input_path)?;
let reader = io::BufReader::new(&file);
for line in reader.lines() {
if let Ok(line) = line {
inputs.push(line);
} else {
error!(
"I/O error encountered when trying to read from {:#?}",
&file
);
std::process::exit(5);
}
}
} else {
let stdin = io::stdin();
let stdin = stdin.lock();
for line in stdin.lines() {
if let Ok(line) = line {
inputs.push(line);
} else {
error!("I/O error encountered when trying to read from STDIN");
std::process::exit(5);
}
}
}
debug!("raw inputs:\n{:#?}", &inputs);
let formulas: Vec<PropositionalFormula> = inputs
.iter()
.map(|f| match parser::parse(f) {
Ok(f) => f,
Err(_) => {
error!("ill-formed formula: {:#?}", &f);
std::process::exit(22);
}
})
.collect();
debug!("parsed formulas:\n{:#?}", &formulas);
let results: Vec<bool>;
let mode = args.mode.map(|c| CliOutputMode::try_from(c).ok()).flatten();
match mode {
Some(CliOutputMode::Validity) => {
info!("using validity mode");
results = formulas.iter().map(is_valid).collect();
}
_ => {
info!("using satisfiability mode");
results = formulas.iter().map(is_satisfiable).collect();
}
}
let stdout = io::stdout();
let mut stdout = stdout.lock();
for result in results {
stdout.write_fmt(format_args!("{:?}\n", result))?;
}
Ok(())
}