use winnow::combinator::{alt, delimited, opt, separated};
use winnow::error::StrContext;
use winnow::prelude::*;
use crate::ast::cnf::{CNFAtomicFormula, CNFFormula, CNFLiteral, CNFStatement};
use crate::ast::fof::FOFTerm;
use crate::lexer::{
PResult, atomic_word, defined_word, distinct_object, number, system_word, upper_word, ws,
};
use crate::parser::fof::fof_term;
pub fn cnf_statement<'a>(input: &mut &'a str) -> PResult<CNFStatement<'a>> {
cnf_formula
.map(CNFStatement::Logical)
.context(StrContext::Label("cnf_statement"))
.parse_next(input)
}
pub fn cnf_formula<'a>(input: &mut &'a str) -> PResult<CNFFormula<'a>> {
alt((
delimited(
('(', ws),
cnf_formula.map(|f| CNFFormula::Parens(Box::new(f))),
(ws, ')'),
),
cnf_disjunction.map(CNFFormula::Disjunction),
))
.parse_next(input)
}
fn cnf_disjunction<'a>(input: &mut &'a str) -> PResult<Vec<CNFLiteral<'a>>> {
separated(1.., cnf_literal, (ws, '|', ws)).parse_next(input)
}
pub fn cnf_literal<'a>(input: &mut &'a str) -> PResult<CNFLiteral<'a>> {
if input.starts_with('~') {
'~'.parse_next(input)?;
ws.parse_next(input)?;
let atom = cnf_atomic_formula.parse_next(input)?;
return Ok(CNFLiteral::Negative(atom));
}
cnf_infix_or_positive(input)
}
fn cnf_infix_or_positive<'a>(input: &mut &'a str) -> PResult<CNFLiteral<'a>> {
let first = *input
.as_bytes()
.first()
.ok_or_else(|| winnow::error::ErrMode::Backtrack(winnow::error::ContextError::new()))?;
match first {
b'a'..=b'z' | b'\'' => {
let pred = atomic_word.parse_next(input)?;
ws.parse_next(input)?;
let args: Vec<FOFTerm<'a>> = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?
.unwrap_or_default();
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(FOFTerm::Function(pred, args), right))
} else if input.starts_with('=') {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(FOFTerm::Function(pred, args), right))
} else {
Ok(CNFLiteral::Positive(CNFAtomicFormula::Plain(pred, args)))
}
}
b'A'..=b'Z' => {
let left = FOFTerm::Variable(upper_word.parse_next(input)?);
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(left, right))
} else if input.starts_with('=') {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(left, right))
} else {
Err(winnow::error::ErrMode::Backtrack(
winnow::error::ContextError::new(),
))
}
}
b'$' => {
if input.starts_with("$true")
&& !input[5..].starts_with(|c: char| c.is_ascii_alphanumeric() || c == '_')
{
*input = &input[5..];
return Ok(CNFLiteral::Positive(CNFAtomicFormula::True));
}
if input.starts_with("$false")
&& !input[6..].starts_with(|c: char| c.is_ascii_alphanumeric() || c == '_')
{
*input = &input[6..];
return Ok(CNFLiteral::Positive(CNFAtomicFormula::False));
}
if input.starts_with("$$") {
let pred = system_word.parse_next(input)?;
ws.parse_next(input)?;
let args: Vec<FOFTerm<'a>> = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?
.unwrap_or_default();
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(
FOFTerm::SystemFunction(pred, args),
right,
))
} else if input.starts_with('=') {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(
FOFTerm::SystemFunction(pred, args),
right,
))
} else {
Ok(CNFLiteral::Positive(CNFAtomicFormula::System(pred, args)))
}
} else {
let pred = defined_word.parse_next(input)?;
ws.parse_next(input)?;
let args: Vec<FOFTerm<'a>> = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?
.unwrap_or_default();
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(
FOFTerm::DefinedFunction(pred, args),
right,
))
} else if input.starts_with('=') {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(
FOFTerm::DefinedFunction(pred, args),
right,
))
} else {
Ok(CNFLiteral::Positive(CNFAtomicFormula::Defined(pred, args)))
}
}
}
b'0'..=b'9' | b'+' | b'-' => {
let left = number.map(FOFTerm::Number).parse_next(input)?;
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(left, right))
} else {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(left, right))
}
}
b'"' => {
let left = distinct_object
.map(FOFTerm::DistinctObject)
.parse_next(input)?;
ws.parse_next(input)?;
if input.starts_with("!=") {
"!=".parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Inequality(left, right))
} else {
'='.parse_next(input)?;
ws.parse_next(input)?;
let right = fof_term.parse_next(input)?;
Ok(CNFLiteral::Equality(left, right))
}
}
_ => Err(winnow::error::ErrMode::Backtrack(
winnow::error::ContextError::new(),
)),
}
}
pub fn cnf_atomic_formula<'a>(input: &mut &'a str) -> PResult<CNFAtomicFormula<'a>> {
alt((
"$true".value(CNFAtomicFormula::True),
"$false".value(CNFAtomicFormula::False),
cnf_system_atomic,
cnf_defined_atomic,
cnf_plain_atomic,
))
.parse_next(input)
}
fn cnf_plain_atomic<'a>(input: &mut &'a str) -> PResult<CNFAtomicFormula<'a>> {
let pred = atomic_word.parse_next(input)?;
ws.parse_next(input)?;
let args = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?;
Ok(CNFAtomicFormula::Plain(pred, args.unwrap_or_default()))
}
fn cnf_defined_atomic<'a>(input: &mut &'a str) -> PResult<CNFAtomicFormula<'a>> {
let pred = defined_word.parse_next(input)?;
ws.parse_next(input)?;
let args = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?;
Ok(CNFAtomicFormula::Defined(pred, args.unwrap_or_default()))
}
fn cnf_system_atomic<'a>(input: &mut &'a str) -> PResult<CNFAtomicFormula<'a>> {
let pred = system_word.parse_next(input)?;
ws.parse_next(input)?;
let args = opt(delimited(
('(', ws),
separated(
1..,
|i: &mut &'a str| {
let t = fof_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?;
Ok(CNFAtomicFormula::System(pred, args.unwrap_or_default()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cnf_literal() {
assert!(cnf_literal.parse_peek("p").is_ok());
assert!(cnf_literal.parse_peek("~p").is_ok());
assert!(cnf_literal.parse_peek("p(X)").is_ok());
assert!(cnf_literal.parse_peek("~p(X, Y)").is_ok());
}
#[test]
fn test_cnf_formula() {
assert!(cnf_formula.parse_peek("p | q").is_ok());
assert!(cnf_formula.parse_peek("p | ~q | r(X)").is_ok());
assert!(cnf_formula.parse_peek("(p | q)").is_ok());
}
#[test]
fn test_cnf_equality() {
let result = cnf_formula.parse_peek("X = Y | ~p(X)");
assert!(result.is_ok());
}
}