use nom:: { IResult, bytes::complete::{take_while, take_while1, take_while_m_n, is_not}, character::is_alphanumeric, character::complete::{digit1, multispace0}, sequence::{preceded, delimited, pair}, combinator::{map, map_res}, multi::{many0, separated_list}, branch::alt, bytes::complete::tag, }; #[derive(PartialEq)] pub struct PredicateDeclaration { name: String, arity: usize, } #[derive(PartialEq)] pub struct Predicate { declaration: PredicateDeclaration, arguments: Vec, } #[derive(PartialEq)] pub struct Exists { parameters: Vec, argument: Box, } #[derive(PartialEq)] pub struct ForAll { parameters: Vec, argument: Box, } #[derive(PartialEq)] pub enum Formula { Exists(Exists), ForAll(ForAll), Not(Box), And(Vec>), Or(Vec>), Implies(Box, Box), Biconditional(Box, Box), Less(Term, Term), LessOrEqual(Term, Term), Greater(Term, Term), GreaterOrEqual(Term, Term), Equal(Term, Term), NotEqual(Term, Term), Boolean(bool), Predicate(Predicate), } #[derive(PartialEq)] pub enum Domain { Program, Integer, } #[derive(PartialEq)] pub struct VariableDeclaration { name: String, domain: Domain, } #[derive(PartialEq)] pub enum Term { Infimum, Supremum, Integer(i64), Symbolic(String), String(String), Variable(VariableDeclaration), Add(Box, Box), Subtract(Box, Box), Multiply(Box, Box), Negative(Box), } pub enum TermOperator { Add, Subtract, Multiply, } impl std::fmt::Debug for VariableDeclaration { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.domain { Domain::Program => write!(format, "X")?, Domain::Integer => write!(format, "N")?, }; write!(format, "{}", &self.name) } } impl std::fmt::Display for VariableDeclaration { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.domain { Domain::Program => write!(format, "X")?, Domain::Integer => write!(format, "N")?, }; write!(format, "{}", &self.name) } } fn term_precedence(term: &Term) -> u64 { match term { Term::Infimum | Term::Supremum | Term::Integer(_) | Term::Symbolic(_) | Term::String(_) | Term::Variable(_) => 0, Term::Negative(_) => 1, Term::Multiply(_, _) => 2, Term::Add(_, _) | Term::Subtract(_, _) => 3, } } fn formula_precedence(formula: &Formula) -> u64 { match formula { Formula::Predicate(_) | Formula::Boolean(_) | Formula::Less(_, _) | Formula::LessOrEqual(_, _) | Formula::Greater(_, _) | Formula::GreaterOrEqual(_, _) | Formula::Equal(_, _) | Formula::NotEqual(_, _) => 0, Formula::Not(_) => 1, Formula::And(_) => 2, Formula::Or(_) => 3, Formula::Implies(_, _) => 4, Formula::Biconditional(_, _) => 5, Formula::Exists(_) | Formula::ForAll(_) => 6, } } impl std::fmt::Debug for Term { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Term::Infimum => write!(format, "#inf"), Term::Supremum => write!(format, "#sup"), Term::Integer(value) => write!(format, "{}", value), Term::Symbolic(ref value) => write!(format, "{}", value), Term::String(ref value) => write!(format, "\"{}\"", value), Term::Variable(ref declaration) => write!(format, "{:?}", declaration), Term::Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right), Term::Subtract(ref left, ref right) => write!(format, "({:?} - {:?})", left, right), Term::Multiply(ref left, ref right) => write!(format, "({:?} * {:?})", left, right), Term::Negative(ref argument) => write!(format, "-({:?})", argument), } } } impl std::fmt::Display for Term { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Term::Infimum => write!(format, "#inf"), Term::Supremum => write!(format, "#sup"), Term::Integer(value) => write!(format, "{}", value), Term::Symbolic(ref value) => write!(format, "{}", value), Term::String(ref value) => write!(format, "\"{}\"", value), Term::Variable(ref declaration) => write!(format, "{}", declaration), Term::Add(ref left, ref right) => write!(format, "({} + {})", left, right), Term::Subtract(ref left, ref right) => write!(format, "({} - {})", left, right), Term::Multiply(ref left, ref right) => write!(format, "({} * {})", left, right), Term::Negative(ref argument) => write!(format, "-({})", argument), } } } impl std::fmt::Debug for Formula { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Formula::Exists(ref exists) => { write!(format, "exists")?; let mut separator = " "; for parameter in &exists.parameters { write!(format, "{}{:?}", separator, parameter)?; separator = ", " } write!(format, " ({:?})", exists.argument) }, Formula::ForAll(ref for_all) => { write!(format, "forall")?; let mut separator = " "; for parameter in &for_all.parameters { write!(format, "{}{:?}", separator, parameter)?; separator = ", " } write!(format, " ({:?})", for_all.argument) }, Formula::Not(ref argument) => write!(format, "not {:?}", argument), Formula::And(ref arguments) => { write!(format, "(")?; let mut separator = ""; for argument in arguments { write!(format, "{}{:?}", separator, argument)?; separator = " and " } write!(format, ")") }, Formula::Or(ref arguments) => { write!(format, "(")?; let mut separator = ""; for argument in arguments { write!(format, "{}{:?}", separator, argument)?; separator = " or " } write!(format, ")") }, Formula::Implies(ref left, ref right) => write!(format, "({:?} -> {:?})", left, right), Formula::Biconditional(ref left, ref right) => write!(format, "({:?} <-> {:?})", left, right), Formula::Less(ref left, ref right) => write!(format, "({:?} < {:?})", left, right), Formula::LessOrEqual(ref left, ref right) => write!(format, "({:?} <= {:?})", left, right), Formula::Greater(ref left, ref right) => write!(format, "({:?} > {:?})", left, right), Formula::GreaterOrEqual(ref left, ref right) => write!(format, "({:?} >= {:?})", left, right), Formula::Equal(ref left, ref right) => write!(format, "({:?} = {:?})", left, right), Formula::NotEqual(ref left, ref right) => write!(format, "({:?} != {:?})", left, right), Formula::Boolean(value) => match value { true => write!(format, "#true"), false => write!(format, "#false"), }, Formula::Predicate(ref predicate) => { write!(format, "{}", predicate.declaration.name)?; if !predicate.arguments.is_empty() { write!(format, "(")?; let mut separator = ""; for argument in &predicate.arguments { write!(format, "{}{:?}", separator, argument)?; separator = ", " } write!(format, ")")?; } Ok(()) }, } } } impl std::fmt::Display for Formula { fn fmt(&self, format: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { Formula::Exists(ref exists) => { write!(format, "exists")?; let mut separator = " "; for parameter in &exists.parameters { write!(format, "{}{}", separator, parameter)?; separator = ", " } write!(format, " ({})", exists.argument) }, Formula::ForAll(ref for_all) => { write!(format, "forall")?; let mut separator = " "; for parameter in &for_all.parameters { write!(format, "{}{}", separator, parameter)?; separator = ", " } write!(format, " ({})", for_all.argument) }, Formula::Not(ref argument) => write!(format, "not {}", argument), Formula::And(ref arguments) => { write!(format, "(")?; let mut separator = ""; for argument in arguments { write!(format, "{}{}", separator, argument)?; separator = " and " } write!(format, ")") }, Formula::Or(ref arguments) => { write!(format, "(")?; let mut separator = ""; for argument in arguments { write!(format, "{}{}", separator, argument)?; separator = " or " } write!(format, ")") }, Formula::Implies(ref left, ref right) => write!(format, "({} -> {})", left, right), Formula::Biconditional(ref left, ref right) => write!(format, "({} <-> {})", left, right), Formula::Less(ref left, ref right) => write!(format, "({} < {})", left, right), Formula::LessOrEqual(ref left, ref right) => write!(format, "({} <= {})", left, right), Formula::Greater(ref left, ref right) => write!(format, "({} > {})", left, right), Formula::GreaterOrEqual(ref left, ref right) => write!(format, "({} >= {})", left, right), Formula::Equal(ref left, ref right) => write!(format, "({} = {})", left, right), Formula::NotEqual(ref left, ref right) => write!(format, "({} != {})", left, right), Formula::Boolean(value) => match value { true => write!(format, "#true"), false => write!(format, "#false"), }, Formula::Predicate(ref predicate) => { write!(format, "{}", predicate.declaration.name)?; if !predicate.arguments.is_empty() { write!(format, "(")?; let mut separator = ""; for argument in &predicate.arguments { write!(format, "{}{}", separator, argument)?; separator = ", " } write!(format, ")")?; } Ok(()) }, } } } fn infimum(i: &str) -> IResult<&str, Term> { map ( delimited(multispace0, tag("#inf"), multispace0), |_| Term::Infimum )(i) } fn supremum(i: &str) -> IResult<&str, Term> { map ( delimited(multispace0, tag("#sup"), multispace0), |_| Term::Supremum )(i) } fn integer(i: &str) -> IResult<&str, Term> { map ( map_res ( delimited(multispace0, digit1, multispace0), std::str::FromStr::from_str ), Term::Integer )(i) } fn is_lowercase_alphanumeric(c: char) -> bool { c.is_alphanumeric() && c.is_lowercase() } fn symbolic_identifier(i: &str) -> IResult<&str, String> { map ( pair ( take_while_m_n(1, 1, is_lowercase_alphanumeric), take_while(char::is_alphanumeric) ), |(s0, s1)| format!("{}{}", s0, s1) )(i) } fn symbolic(i: &str) -> IResult<&str, Term> { map ( delimited(multispace0, symbolic_identifier, multispace0), Term::Symbolic )(i) } fn string(i: &str) -> IResult<&str, Term> { map ( delimited ( multispace0, delimited ( tag("\""), is_not("\""), tag("\""), ), multispace0 ), |s: &str| Term::String(s.to_string()) )(i) } fn program_variable_identifier(i: &str) -> IResult<&str, String> { map ( delimited ( multispace0, preceded ( tag("X"), take_while1(char::is_alphanumeric) ), multispace0 ), |s: &str| s.to_string() )(i) } fn integer_variable_identifier(i: &str) -> IResult<&str, String> { map ( delimited ( multispace0, preceded ( tag("N"), take_while1(char::is_alphanumeric) ), multispace0 ), |s: &str| s.to_string() )(i) } fn program_variable_declaration(i: &str) -> IResult<&str, VariableDeclaration> { map ( program_variable_identifier, |s| VariableDeclaration{name: s, domain: Domain::Program} )(i) } fn integer_variable_declaration(i: &str) -> IResult<&str, VariableDeclaration> { map ( integer_variable_identifier, |s| VariableDeclaration{name: s, domain: Domain::Integer} )(i) } fn variable_declaration(i: &str) -> IResult<&str, VariableDeclaration> { alt (( program_variable_declaration, integer_variable_declaration ))(i) } fn program_variable(i: &str) -> IResult<&str, Term> { map ( program_variable_identifier, |s| Term::Variable(VariableDeclaration{name: s, domain: Domain::Program}) )(i) } fn integer_variable(i: &str) -> IResult<&str, Term> { map ( integer_variable_identifier, |s| Term::Variable(VariableDeclaration{name: s, domain: Domain::Integer}) )(i) } fn variable(i: &str) -> IResult<&str, Term> { alt (( program_variable, integer_variable ))(i) } fn predicate_0_ary(i: &str) -> IResult<&str, Formula> { map ( delimited(multispace0, symbolic_identifier, multispace0), |name| Formula::Predicate( Predicate { declaration: PredicateDeclaration { name: name, arity: 0, }, arguments: vec![], }) )(i) } fn predicate_n_ary(i: &str) -> IResult<&str, Formula> { map ( pair ( delimited(multispace0, symbolic_identifier, multispace0), delimited ( tag("("), separated_list(tag(","), term), tag(")") ) ), |(name, arguments)| Formula::Predicate( Predicate { declaration: PredicateDeclaration { name: name, arity: arguments.len(), }, arguments: arguments, }) )(i) } fn predicate(i: &str) -> IResult<&str, Formula> { alt (( predicate_n_ary, predicate_0_ary ))(i) } fn boolean(i: &str) -> IResult<&str, Formula> { map ( delimited ( multispace0, alt (( map(tag("#true"), |_| true), map(tag("#false"), |_| false) )), multispace0 ), |value| Formula::Boolean(value) )(i) } fn term_parenthesized(i: &str) -> IResult<&str, Term> { delimited ( multispace0, delimited ( tag("("), term, tag(")") ), multispace0 )(i) } fn fold_terms(initial: Term, remainder: Vec<(TermOperator, Term)>) -> Term { remainder.into_iter().fold(initial, |accumulator, pair| { let (term_operator, term) = pair; match term_operator { TermOperator::Add => Term::Add(Box::new(accumulator), Box::new(term)), TermOperator::Subtract => Term::Subtract(Box::new(accumulator), Box::new(term)), TermOperator::Multiply => Term::Multiply(Box::new(accumulator), Box::new(term)), } }) } fn term_precedence_0(i: &str) -> IResult<&str, Term> { alt (( infimum, supremum, integer, symbolic, string, variable, term_parenthesized ))(i) } fn term_precedence_1(i: &str) -> IResult<&str, Term> { alt (( map ( delimited ( multispace0, preceded(tag("-"), term_precedence_0), multispace0 ), |term| Term::Negative(Box::new(term)) ), term_precedence_0 ))(i) } fn term_precedence_2(i: &str) -> IResult<&str, Term> { let (i, initial) = term_precedence_1(i)?; let (i, remainder) = many0 ( |i| { let (i, term) = preceded(tag("*"), term_precedence_1)(i)?; Ok((i, (TermOperator::Multiply, term))) } )(i)?; Ok((i, fold_terms(initial, remainder))) } fn term_precedence_3(i: &str) -> IResult<&str, Term> { let (i, initial) = term_precedence_2(i)?; let (i, remainder) = many0 ( alt (( |i| { let (i, term) = preceded(tag("+"), term_precedence_2)(i)?; Ok((i, (TermOperator::Add, term))) }, |i| { let (i, term) = preceded(tag("-"), term_precedence_2)(i)?; Ok((i, (TermOperator::Subtract, term))) } )) )(i)?; Ok((i, fold_terms(initial, remainder))) } fn term(i: &str) -> IResult<&str, Term> { term_precedence_3(i) } #[cfg(test)] mod tests { #[test] fn parse_integer() { assert_eq!(crate::integer("12345"), Ok(("", crate::Term::Integer(12345)))); } #[test] fn parse_variable_declaration() { assert_eq!(crate::variable_declaration("X5"), Ok(("", crate::VariableDeclaration{domain: crate::Domain::Program, name: "5".to_string()}))); assert_eq!(crate::variable_declaration("NX3"), Ok(("", crate::VariableDeclaration{domain: crate::Domain::Integer, name: "X3".to_string()}))); } #[test] fn parse_variable() { assert_eq!(crate::variable("X5"), Ok(("", crate::Term::Variable(crate::VariableDeclaration{domain: crate::Domain::Program, name: "5".to_string()})))); assert_eq!(crate::variable("NX3"), Ok(("", crate::Term::Variable(crate::VariableDeclaration{domain: crate::Domain::Integer, name: "X3".to_string()})))); } #[test] fn parse_string() { assert_eq!(crate::string(" \"foobar\" "), Ok(("", crate::Term::String("foobar".to_string())))); } #[test] fn parse_boolean() { assert_eq!(crate::boolean(" #true "), Ok(("", crate::Formula::Boolean(true)))); assert_eq!(crate::boolean(" #false "), Ok(("", crate::Formula::Boolean(false)))); } #[test] fn parse_term() { assert_eq!(crate::term(" 5 + 3"), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Integer(3)), )))); assert_eq!(crate::term(" -5"), Ok(("", crate::Term::Negative ( Box::new(crate::Term::Integer(5)) ) ))); assert_eq!(crate::term(" 5+3 * -(9+ 1) + 2 "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Multiply ( Box::new(crate::Term::Integer(3)), Box::new(crate::Term::Negative ( Box::new(crate::Term::Add ( Box::new(crate::Term::Integer(9)), Box::new(crate::Term::Integer(1)), )) )), )), )), Box::new(crate::Term::Integer(2)), )))); assert_eq!(crate::term(" 5 + a "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Symbolic("a".to_string())), )))); assert_eq!(crate::term(" 5 + #sup "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Supremum), )))); assert_eq!(crate::term(" 5 + #inf "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Infimum), )))); assert_eq!(crate::term(" 5 + \" text \" "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::String(" text ".to_string())), )))); assert_eq!(crate::term(" 5 + X1 "), Ok(("", crate::Term::Add ( Box::new(crate::Term::Integer(5)), Box::new(crate::Term::Variable(crate::VariableDeclaration{domain: crate::Domain::Program, name: "1".to_string()})), )))); } #[test] fn parse_predicate() { assert_eq!(crate::predicate("p"), Ok(("", crate::Formula::Predicate(crate::Predicate{declaration: crate::PredicateDeclaration{name: "p".to_string(), arity: 0}, arguments: vec![]})))); assert_eq!(crate::predicate_n_ary("p(5, 6, 7)"), Ok(("", crate::Formula::Predicate ( crate::Predicate { declaration: crate::PredicateDeclaration { name: "p".to_string(), arity: 3, }, arguments: vec! [ crate::Term::Integer(5), crate::Term::Integer(6), crate::Term::Integer(7), ] } )))); assert_eq!(crate::predicate("p(1, 3+4*5+6, \"test\")"), Ok(("", crate::Formula::Predicate ( crate::Predicate { declaration: crate::PredicateDeclaration { name: "p".to_string(), arity: 3, }, arguments: vec! [ crate::Term::Integer(1), crate::Term::Add ( Box::new(crate::Term::Add ( Box::new(crate::Term::Integer(3)), Box::new(crate::Term::Multiply ( Box::new(crate::Term::Integer(4)), Box::new(crate::Term::Integer(5)), )), )), Box::new(crate::Term::Integer(6)), ), crate::Term::String("test".to_string()), ] } )))); } }