foliage-rs/src/lib.rs

930 lines
19 KiB
Rust

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<Term>,
}
#[derive(PartialEq)]
pub struct Exists
{
parameters: Vec<VariableDeclaration>,
argument: Box<Formula>,
}
#[derive(PartialEq)]
pub struct ForAll
{
parameters: Vec<VariableDeclaration>,
argument: Box<Formula>,
}
#[derive(PartialEq)]
pub enum Formula
{
Exists(Exists),
ForAll(ForAll),
Not(Box<Formula>),
And(Vec<Box<Formula>>),
Or(Vec<Box<Formula>>),
Implies(Box<Formula>, Box<Formula>),
Biconditional(Box<Formula>, Box<Formula>),
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<Term>, Box<Term>),
Subtract(Box<Term>, Box<Term>),
Multiply(Box<Term>, Box<Term>),
Negative(Box<Term>),
}
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()),
]
}
))));
}
}