use nom:: { IResult, sequence::{delimited, pair, preceded, terminated, tuple}, combinator::{map, recognize, opt}, character::complete::digit1, branch::alt, bytes::complete::tag, multi::separated_list, }; use foliage::parse::whitespace0; pub fn recognize_and_keep>, O, E: nom::error::ParseError, F>(parser: F) -> impl Fn(I) -> IResult where F: Fn(I) -> IResult, { move |input: I| { let i = input.clone(); match parser(i) { Ok((i, result)) => { let index = input.offset(&i); Ok((i, (input.slice(..index), result))) }, Err(e) => Err(e), } } } fn formula_statement_kind(i: &str) -> IResult<&str, crate::project::FormulaStatementKind> { delimited ( whitespace0, alt (( map ( tag("axiom:"), |_| crate::project::FormulaStatementKind::Axiom, ), map ( tag("completion(constraint):"), |_| crate::project::FormulaStatementKind::Completion(crate::project::CompletionTarget::Constraint), ), map ( preceded ( tag("completion"), delimited ( tag("("), pair ( terminated ( foliage::parse::symbolic_identifier, tag("/"), ), digit1, ), tag("):"), ), ), |(name, arity)| match arity.parse::() { Ok(arity) => crate::project::FormulaStatementKind::Completion( crate::project::CompletionTarget::Predicate(foliage::PredicateDeclaration{name, arity})), Err(error) => panic!("invalid arity “{}”: {}", arity, error), } ), map ( tag("assumption:"), |_| crate::project::FormulaStatementKind::Assumption, ), map ( tag("lemma(forward):"), |_| crate::project::FormulaStatementKind::Lemma(Some(crate::project::ProofDirection::Forward)), ), map ( tag("lemma(backward):"), |_| crate::project::FormulaStatementKind::Lemma(Some(crate::project::ProofDirection::Backward)), ), map ( tag("lemma:"), |_| crate::project::FormulaStatementKind::Lemma(None), ), map ( tag("assertion:"), |_| crate::project::FormulaStatementKind::Assertion, ), )), whitespace0, )(i) } fn formula_statement(i: &str) -> IResult<&str, (crate::project::FormulaStatementKind, foliage::Formula)> { terminated ( pair ( formula_statement_kind, foliage::formula, ), preceded ( whitespace0, tag("."), ), )(i) } fn input_statement(i: &str) -> IResult<&str, Vec> { delimited ( whitespace0, preceded ( pair ( tag("input:"), whitespace0, ), separated_list ( tag(","), delimited ( whitespace0, alt (( map ( pair ( terminated ( foliage::parse::symbolic_identifier, tag("/"), ), digit1, ), |(name, arity)| crate::project::InputSymbol::Predicate( foliage::PredicateDeclaration { name: name.to_string(), arity: arity.parse::().expect("invalid arity"), }) ), map ( pair ( foliage::parse::symbolic_identifier, opt ( preceded ( delimited ( whitespace0, tag("->"), whitespace0, ), tag("integer"), ), ), ), |(name, domain)| match domain { None => crate::project::InputSymbol::Constant( crate::project::InputConstantDeclaration { name: name.to_string(), domain: foliage::Domain::Program, }), Some("integer") => crate::project::InputSymbol::Constant( crate::project::InputConstantDeclaration { name: name.to_string(), domain: foliage::Domain::Integer, }), Some(ref unknown_sort) => panic!("unrecognized sort “{}”", unknown_sort), } ), )), whitespace0, ) ) ), preceded ( whitespace0, tag("."), ), )(i) } pub enum Statement { Formula(crate::project::FormulaStatementKind, foliage::Formula), Input(Vec), } fn statement_enclosed_by_whitespace(i: &str) -> IResult<&str, (&str, (&str, Statement), &str)> { tuple (( recognize(whitespace0), recognize_and_keep ( alt (( map(formula_statement, |(formula_statement_kind, formula)| Statement::Formula(formula_statement_kind, formula)), map(input_statement, |input_symbols| Statement::Input(input_symbols)), )) ), recognize(whitespace0), ))(i) } pub fn project(i: &str) -> IResult<&str, crate::Project> { let mut statement_input = i.clone(); let mut blocks = Vec::new(); let mut input_constants = std::collections::HashSet::new(); let mut input_predicates = std::collections::HashSet::new(); loop { let i_ = statement_input.clone(); match statement_enclosed_by_whitespace(i_) { Ok((i, (whitespace_before, (statement_original_text, statement), whitespace_after))) => { // Iteration must always consume input (to prevent infinite loops) if i == statement_input { return Err(nom::Err::Error(nom::error::ParseError::from_error_kind(statement_input, nom::error::ErrorKind::Many0))); } if !whitespace_before.is_empty() { blocks.push(crate::project::Block::Whitespace(whitespace_before.to_string())); } match statement { Statement::Formula(formula_statement_kind, formula) => { let formula_statement = crate::project::FormulaStatement { kind: formula_statement_kind, original_text: statement_original_text.to_string(), formula, proven: false, }; blocks.push(crate::project::Block::FormulaStatement(formula_statement)); }, Statement::Input(input_symbols) => { for input_symbol in input_symbols { match input_symbol { crate::project::InputSymbol::Constant(name) => { input_constants.insert(name); }, crate::project::InputSymbol::Predicate(declaration) => { input_predicates.insert(declaration); }, } } let input_statement = crate::project::InputStatement { original_text: statement_original_text.to_string(), }; blocks.push(crate::project::Block::InputStatement(input_statement)); }, } if !whitespace_after.is_empty() { blocks.push(crate::project::Block::Whitespace(whitespace_after.to_string())); } statement_input = i; }, Err(nom::Err::Error(_)) => break, Err(e) => return Err(e), } } let i = statement_input; // Verify that the whole file has been parsed if i != "" { eprintln!("parsing error at:\n{}", i); return Err(nom::Err::Error(nom::error::ParseError::from_error_kind(statement_input, nom::error::ErrorKind::Many0))); } let project = crate::Project { blocks, input_constants, input_predicates, }; Ok((i, project)) }