use winnow::combinator::{alt, delimited, opt, preceded, separated};
use winnow::prelude::*;
use crate::ast::{
BinaryConnective, FormulaRole, GeneralData, GeneralTerm, Quantifier, UnaryConnective,
};
use crate::lexer::{
PResult, atomic_word, distinct_object, dollar_word, lower_word, number, upper_word, ws,
};
pub fn binary_connective(input: &mut &str) -> PResult<BinaryConnective> {
alt((
"<=>".value(BinaryConnective::Iff),
"=>".value(BinaryConnective::Impl),
"<=".value(BinaryConnective::RevImpl),
"<~>".value(BinaryConnective::Xor),
"~|".value(BinaryConnective::Nor),
"~&".value(BinaryConnective::Nand),
"|".value(BinaryConnective::Or),
"&".value(BinaryConnective::And),
))
.parse_next(input)
}
pub fn nonassoc_connective(input: &mut &str) -> PResult<BinaryConnective> {
alt((
"<=>".value(BinaryConnective::Iff),
"=>".value(BinaryConnective::Impl),
"<=".value(BinaryConnective::RevImpl),
"<~>".value(BinaryConnective::Xor),
"~|".value(BinaryConnective::Nor),
"~&".value(BinaryConnective::Nand),
))
.parse_next(input)
}
pub fn quantifier(input: &mut &str) -> PResult<Quantifier> {
alt(('!'.value(Quantifier::Forall), '?'.value(Quantifier::Exists))).parse_next(input)
}
pub fn unary_connective(input: &mut &str) -> PResult<UnaryConnective> {
'~'.value(UnaryConnective::Not).parse_next(input)
}
pub fn formula_role(input: &mut &str) -> PResult<FormulaRole> {
let start = lower_word.parse_next(input)?;
let checkpoint = *input;
let hyphen_result: PResult<&str> = "-".parse_next(input);
if hyphen_result.is_ok() {
if let Ok(rest) = lower_word.parse_next(input) {
let end_pos = rest.as_ptr() as usize + rest.len();
let start_pos = start.as_ptr() as usize;
let full_len = end_pos - start_pos;
let full_role = unsafe {
std::str::from_utf8_unchecked(std::slice::from_raw_parts(start.as_ptr(), full_len))
};
if let Some(role) = FormulaRole::parse(full_role) {
return Ok(role);
}
}
*input = checkpoint;
}
FormulaRole::parse(start)
.ok_or_else(|| winnow::error::ErrMode::Backtrack(winnow::error::ContextError::new()))
}
pub fn general_term<'a>(input: &mut &'a str) -> PResult<GeneralTerm<'a>> {
alt((
general_term_colon_pair,
general_term_simple,
))
.parse_next(input)
}
fn general_term_simple<'a>(input: &mut &'a str) -> PResult<GeneralTerm<'a>> {
alt((
general_list.map(GeneralTerm::List),
general_data,
general_function,
distinct_object.map(GeneralTerm::DistinctObject),
number.map(GeneralTerm::Number),
upper_word.map(GeneralTerm::Variable),
atomic_word.map(GeneralTerm::Word),
))
.parse_next(input)
}
fn general_term_colon_pair<'a>(input: &mut &'a str) -> PResult<GeneralTerm<'a>> {
let left = general_term_simple.parse_next(input)?;
ws.parse_next(input)?;
':'.parse_next(input)?;
ws.parse_next(input)?;
let right = general_term.parse_next(input)?;
Ok(GeneralTerm::ColonPair(Box::new(left), Box::new(right)))
}
fn general_function<'a>(input: &mut &'a str) -> PResult<GeneralTerm<'a>> {
let name = atomic_word.parse_next(input)?;
ws.parse_next(input)?;
if input.starts_with('(') {
'('.parse_next(input)?;
ws.parse_next(input)?;
let args = separated(
1..,
|i: &mut &'a str| {
let t = general_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
)
.parse_next(input)?;
ws.parse_next(input)?;
')'.parse_next(input)?;
Ok(GeneralTerm::Function(name, args))
} else {
Ok(GeneralTerm::Word(name))
}
}
fn general_list<'a>(input: &mut &'a str) -> PResult<Vec<GeneralTerm<'a>>> {
delimited(
('[', ws),
separated(
0..,
|i: &mut &'a str| {
let t = general_term(i)?;
ws.parse_next(i)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ']'),
)
.parse_next(input)
}
fn general_data<'a>(input: &mut &'a str) -> PResult<GeneralTerm<'a>> {
let keyword = dollar_word.parse_next(input)?;
ws.parse_next(input)?;
'('.parse_next(input)?;
ws.parse_next(input)?;
let data = match keyword {
"thf" => {
let formula = crate::parser::thf::thf_formula.parse_next(input)?;
GeneralData::THF(Box::new(formula))
}
"tff" => {
let formula = crate::parser::tff::tff_formula.parse_next(input)?;
GeneralData::TFF(Box::new(formula))
}
"fof" => {
let formula = crate::parser::fof::fof_formula.parse_next(input)?;
GeneralData::FOF(Box::new(formula))
}
"cnf" => {
let formula = crate::parser::cnf::cnf_formula.parse_next(input)?;
GeneralData::CNF(Box::new(formula))
}
"fot" => {
let term = crate::parser::fof::fof_term.parse_next(input)?;
GeneralData::FOT(Box::new(term))
}
_ => {
return Err(winnow::error::ErrMode::Backtrack(
winnow::error::ContextError::new(),
));
}
};
ws.parse_next(input)?;
')'.parse_next(input)?;
Ok(GeneralTerm::Formula(Box::new(data)))
}
pub fn annotations<'a>(input: &mut &'a str) -> PResult<Option<crate::ast::Annotations<'a>>> {
let result = opt(preceded((',', ws), |i: &mut &'a str| {
let source = general_term(i)?;
ws.parse_next(i)?;
let useful_info = opt(preceded(
(',', ws),
delimited(
('[', ws),
separated(
0..,
|i2: &mut &'a str| {
let t = general_term(i2)?;
ws.parse_next(i2)?;
Ok(t)
},
(ws, ',', ws),
),
(ws, ']'),
),
))
.parse_next(i)?;
Ok(crate::ast::Annotations {
source,
useful_info,
})
}))
.parse_next(input)?;
Ok(result)
}
pub fn paren_list<'a, O, P>(
parser: P,
) -> impl Parser<&'a str, Vec<O>, winnow::error::ErrMode<winnow::error::ContextError>>
where
P: Parser<&'a str, O, winnow::error::ErrMode<winnow::error::ContextError>>,
{
delimited(('(', ws), separated(1.., parser, (ws, ',', ws)), (ws, ')'))
}
pub fn bracket_list<'a, O, P>(
parser: P,
) -> impl Parser<&'a str, Vec<O>, winnow::error::ErrMode<winnow::error::ContextError>>
where
P: Parser<&'a str, O, winnow::error::ErrMode<winnow::error::ContextError>>,
{
delimited(('[', ws), separated(1.., parser, (ws, ',', ws)), (ws, ']'))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_general_term_function() {
let mut input = "file('source.p', ax1)";
let result = general_term.parse_next(&mut input);
assert!(
result.is_ok(),
"general_term failed on function: {:?}",
result.err()
);
}
#[test]
fn test_general_function_simple() {
let mut input = "file(a, b)";
let result = general_function(&mut input);
assert!(
result.is_ok(),
"general_function failed: {:?}",
result.err()
);
}
#[test]
fn test_annotations_parse() {
let mut input = ", file('source.p', ax1)";
let result = annotations(&mut input);
assert!(result.is_ok(), "annotations failed: {:?}", result.err());
}
}