use super::operators::{
and_operator, biimplication_operator, implication_operator, negation_operator, or_operator,
};
use super::variable::variable;
use super::ParseResult;
use libprop_sat_solver::formula::{BinaryOperator, PropositionalFormula};
use nom::branch::alt;
use nom::bytes::complete::take_while;
use nom::character::complete::char;
use nom::sequence::{preceded, separated_pair, terminated};
#[inline]
pub fn propositional_variable(input: &str) -> ParseResult<&str, PropositionalFormula> {
let (remaining_input, variable) = variable(input)?;
let formula = PropositionalFormula::variable(variable);
Ok((remaining_input, formula))
}
#[inline]
pub fn space(input: &str) -> ParseResult<&str, &str> {
let space_chars = " \t";
take_while(move |c| space_chars.contains(c))(input)
}
#[inline]
pub fn paired_parentheses<'a, R, P>(inner_parser: P) -> impl Fn(&'a str) -> ParseResult<&'a str, R>
where
P: Fn(&'a str) -> ParseResult<&'a str, R>,
{
preceded(
char('('),
terminated(preceded(space, inner_parser), preceded(space, char(')'))),
)
}
#[inline]
pub fn negated_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
let (remaining_input, sub_formula) =
paired_parentheses(preceded(negation_operator, propositional_formula))(input)?;
Ok((
remaining_input,
PropositionalFormula::negated(Box::new(sub_formula)),
))
}
#[inline]
pub fn parse_binary_formula(
main_connective_parser: fn(&str) -> ParseResult<&str, BinaryOperator>,
value_constructor_fn: fn(
Box<PropositionalFormula>,
Box<PropositionalFormula>,
) -> PropositionalFormula,
) -> impl Fn(&str) -> ParseResult<&str, PropositionalFormula> {
move |input| {
let (remaining_input, (left_sub_formula, right_sub_formula)) =
paired_parentheses(separated_pair(
preceded(space, propositional_formula),
preceded(space, main_connective_parser),
preceded(space, propositional_formula),
))(input)?;
Ok((
remaining_input,
value_constructor_fn(Box::new(left_sub_formula), Box::new(right_sub_formula)),
))
}
}
#[inline]
pub fn conjunction_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
parse_binary_formula(and_operator, PropositionalFormula::conjunction)(input)
}
#[inline]
pub fn disjunction_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
parse_binary_formula(or_operator, PropositionalFormula::disjunction)(input)
}
#[inline]
pub fn implication_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
parse_binary_formula(implication_operator, PropositionalFormula::implication)(input)
}
#[inline]
pub fn biimplication_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
parse_binary_formula(biimplication_operator, PropositionalFormula::biimplication)(input)
}
#[inline]
pub fn propositional_formula(input: &str) -> ParseResult<&str, PropositionalFormula> {
alt((
propositional_variable,
negated_formula,
conjunction_formula,
disjunction_formula,
implication_formula,
biimplication_formula,
))(input)
}
#[cfg(test)]
mod tests {
use super::*;
use assert2::check;
use libprop_sat_solver::formula::Variable;
#[test]
fn test_space() {
check!(("", " \t") == space(" \t").unwrap());
}
#[test]
fn simple_propositional_variable_formula() {
let expected_formula = PropositionalFormula::variable(Variable::new("a"));
check!(("", expected_formula) == propositional_variable("a").unwrap());
}
#[test]
fn negated_formula() {
let expected_formula = PropositionalFormula::negated(Box::new(
PropositionalFormula::variable(Variable::new("a")),
));
check!(("", expected_formula) == propositional_formula("(-a)").unwrap());
}
#[test]
fn conjunction_formula() {
let left_sub_formula = PropositionalFormula::variable(Variable::new("a"));
let right_sub_formula = PropositionalFormula::variable(Variable::new("b"));
let expected_formula = PropositionalFormula::conjunction(
Box::new(left_sub_formula),
Box::new(right_sub_formula),
);
check!(("", expected_formula) == propositional_formula("(a^b)").unwrap());
}
#[test]
fn disjunction_formula() {
let left_sub_formula = PropositionalFormula::variable(Variable::new("a"));
let right_sub_formula = PropositionalFormula::variable(Variable::new("b"));
let expected_formula = PropositionalFormula::disjunction(
Box::new(left_sub_formula),
Box::new(right_sub_formula),
);
check!(("", expected_formula) == propositional_formula("(a|b)").unwrap());
}
#[test]
fn implication_formula() {
let left_sub_formula = PropositionalFormula::variable(Variable::new("a"));
let right_sub_formula = PropositionalFormula::variable(Variable::new("b"));
let expected_formula = PropositionalFormula::implication(
Box::new(left_sub_formula),
Box::new(right_sub_formula),
);
check!(("", expected_formula) == propositional_formula("(a->b)").unwrap());
}
#[test]
fn bimplication_formula() {
let left_sub_formula = PropositionalFormula::variable(Variable::new("a"));
let right_sub_formula = PropositionalFormula::variable(Variable::new("b"));
let expected_formula = PropositionalFormula::biimplication(
Box::new(left_sub_formula),
Box::new(right_sub_formula),
);
check!(("", expected_formula) == propositional_formula("(a<->b)").unwrap());
}
#[test]
fn nested_formula() {
let sub_formula_a = PropositionalFormula::variable(Variable::new("a"));
let sub_formula_b = PropositionalFormula::variable(Variable::new("b"));
let sub_formula_c = PropositionalFormula::variable(Variable::new("c"));
let left_sub_formula =
PropositionalFormula::biimplication(Box::new(sub_formula_a), Box::new(sub_formula_b));
let expected_formula =
PropositionalFormula::disjunction(Box::new(left_sub_formula), Box::new(sub_formula_c));
check!(("", expected_formula) == propositional_formula("((a<->b)|c)").unwrap());
}
}