use winnow::combinator::{alt, delimited, opt, preceded, separated};
use winnow::error::StrContext;
use winnow::prelude::*;
use crate::ast::tcf::{
TCFAtomicFormula, TCFClause, TCFFormula, TCFLiteral, TCFStatement, TCFTyping,
};
use crate::lexer::{PResult, atomic_word, ws};
use crate::parser::tff::{tff_term, tff_top_level_type, tff_variable};
pub fn tcf_statement<'a>(input: &mut &'a str) -> PResult<TCFStatement<'a>> {
alt((
tcf_typing.map(TCFStatement::Typing),
tcf_formula.map(TCFStatement::Logical),
))
.context(StrContext::Label("tcf_statement"))
.parse_next(input)
}
fn tcf_typing<'a>(input: &mut &'a str) -> PResult<TCFTyping<'a>> {
let symbol = atomic_word.parse_next(input)?;
ws.parse_next(input)?;
':'.parse_next(input)?;
ws.parse_next(input)?;
let typ = tff_top_level_type.parse_next(input)?;
Ok(TCFTyping { symbol, typ })
}
pub fn tcf_formula<'a>(input: &mut &'a str) -> PResult<TCFFormula<'a>> {
alt((
tcf_quantified_formula,
tcf_clause.map(TCFFormula::Clause),
))
.parse_next(input)
}
fn tcf_quantified_formula<'a>(input: &mut &'a str) -> PResult<TCFFormula<'a>> {
'!'.parse_next(input)?;
ws.parse_next(input)?;
'['.parse_next(input)?;
ws.parse_next(input)?;
let vars = separated(1.., tff_variable, (ws, ',', ws)).parse_next(input)?;
ws.parse_next(input)?;
']'.parse_next(input)?;
ws.parse_next(input)?;
':'.parse_next(input)?;
ws.parse_next(input)?;
let clause = tcf_clause.parse_next(input)?;
Ok(TCFFormula::Quantified {
variables: vars,
clause: Box::new(clause),
})
}
pub fn tcf_clause<'a>(input: &mut &'a str) -> PResult<TCFClause<'a>> {
alt((
tcf_disjunction.map(TCFClause::Disjunction),
delimited(
('(', ws),
tcf_clause.map(|c| TCFClause::Parens(Box::new(c))),
(ws, ')'),
),
))
.parse_next(input)
}
fn tcf_disjunction<'a>(input: &mut &'a str) -> PResult<Vec<TCFLiteral<'a>>> {
separated(1.., tcf_literal, (ws, '|', ws)).parse_next(input)
}
pub fn tcf_literal<'a>(input: &mut &'a str) -> PResult<TCFLiteral<'a>> {
alt((
delimited(
('(', ws),
tcf_literal.map(|l| TCFLiteral::Parens(Box::new(l))),
(ws, ')'),
),
preceded(('~', ws), tcf_atomic_formula).map(TCFLiteral::Negative),
tcf_infix,
tcf_atomic_formula.map(TCFLiteral::Positive),
))
.parse_next(input)
}
fn tcf_infix<'a>(input: &mut &'a str) -> PResult<TCFLiteral<'a>> {
let left = tff_term.parse_next(input)?;
ws.parse_next(input)?;
let is_neg = alt(("!=".value(true), "=".value(false))).parse_next(input)?;
ws.parse_next(input)?;
let right = tff_term.parse_next(input)?;
Ok(if is_neg {
TCFLiteral::Inequality(left, right)
} else {
TCFLiteral::Equality(left, right)
})
}
pub fn tcf_atomic_formula<'a>(input: &mut &'a str) -> PResult<TCFAtomicFormula<'a>> {
alt((
"$true".value(TCFAtomicFormula::True),
"$false".value(TCFAtomicFormula::False),
tcf_plain_atomic,
))
.parse_next(input)
}
fn tcf_plain_atomic<'a>(input: &mut &'a str) -> PResult<TCFAtomicFormula<'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 = tff_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ')'),
))
.parse_next(input)?;
Ok(TCFAtomicFormula::Plain(pred, args.unwrap_or_default()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcf_literal() {
assert!(tcf_literal.parse_peek("p").is_ok());
assert!(tcf_literal.parse_peek("~p").is_ok());
assert!(tcf_literal.parse_peek("p(X)").is_ok());
}
#[test]
fn test_tcf_clause() {
assert!(tcf_clause.parse_peek("p | q").is_ok());
assert!(tcf_clause.parse_peek("p | ~q | r").is_ok());
}
#[test]
fn test_tcf_formula() {
assert!(tcf_formula.parse_peek("p | q").is_ok());
}
}