mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-19 09:29:36 +01:00
* Add NoGood and NoGoodStore (#65) * Update Flake to nix 22.05 * Add nogood-algorithm to the ADF * Add public api for the nogood-learner * Add direnv to gitignore * Avoid a Box, support custom heuristics functions * Add ng option with heu to binary * Introduce a new flag to handle big instances (modelcount vs adhoccount) Note that adhoccount without modelcount will not produce correct modelcounts if memoization is used * Add new heuristic * Add crossbeam-channel to represent an output-stream of stable models Uses a crossbeam-channel to fill a Queue, which can be used safely from outside the function. This rework is done to also allow ad-hoc output of results in a potentially multi-threaded setup. * Added documentation on this new feature on the module-page * Fix broken links in rust-doc * Update Readme for lib to reflect the new NoGood API * Add metadata to bin/Cargo.toml, add features * added a benchmark feature, to easily compile benchmark-releases * Fix facet count tests * Add multithread-safe functionality for the dictionary/ordering * Streamline a couple of API calls * Expose more structs and methods to the public API * Breaking some API (though nothing which is currently used in the binary) * Simple version of gh pages * Added more links and information to the landing page * Fix badges in the app-doc * Add two valued interpretation Parameterised the stable-nogood algorithm to allow a variable stability check function. * Refactor nogood-algorithm name * Update README.md and documentation (`docu` folder) `README.md` on the `/` level is now presenting the same information which is provided in `docs/index.md` * Update main - Update main functionality - Update naming * Fix cli-test * Update Version to 0.3.0 Due to braking API changes and reaching a milestone, the version is incremented to 0.3.0 (beta) * Update Documentation navigation (#81) * flake.lock: Update Flake lock file updates: • Updated input 'flake-utils': 'github:numtide/flake-utils/1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1' (2022-05-30) → 'github:numtide/flake-utils/7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249' (2022-07-04) • Updated input 'gitignoresrc': 'github:hercules-ci/gitignore.nix/bff2832ec341cf30acb3a4d3e2e7f1f7b590116a' (2022-03-05) → 'github:hercules-ci/gitignore.nix/f2ea0f8ff1bce948ccb6b893d15d5ea3efaf1364' (2022-07-21) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/8b538fcb329a7bc3d153962f17c509ee49166973' (2022-06-15) → 'github:NixOS/nixpkgs/e43cf1748462c81202a32b26294e9f8eefcc3462' (2022-08-01) • Updated input 'nixpkgs-unstable': 'github:NixOS/nixpkgs/b1957596ff1c7aa8c55c4512b7ad1c9672502e8e' (2022-06-15) → 'github:NixOS/nixpkgs/7b9be38c7250b22d829ab6effdee90d5e40c6e5c' (2022-07-30) • Updated input 'rust-overlay': 'github:oxalica/rust-overlay/9eea93067eff400846c36f57b7499df9ef428ba0' (2022-06-17) → 'github:oxalica/rust-overlay/9055cb4f33f062c0dd33aa7e3c89140da8f70057' (2022-08-02) * Add type alias for NoGood Add a type alias `Interpretation` for NoGood to reflect the duality where an Interpretation might become a NoGood. * Add documentation information about later revisions on VarContainer Co-authored-by: Maximilian Marx <mmarx@wh2.tu-dresden.de>
581 lines
20 KiB
Rust
581 lines
20 KiB
Rust
//! Parser for ADFs with all needed helper-methods.
|
|
//! It utilises the [nom-crate](https://crates.io/crates/nom).
|
|
use lexical_sort::{natural_lexical_cmp, StringSort};
|
|
use nom::{
|
|
branch::alt,
|
|
bytes::complete::{tag, take_until},
|
|
character::complete::{alphanumeric1, multispace0},
|
|
combinator::{all_consuming, value},
|
|
multi::many1,
|
|
sequence::{delimited, preceded, separated_pair, terminated},
|
|
IResult,
|
|
};
|
|
use std::collections::HashMap;
|
|
use std::{
|
|
cell::RefCell,
|
|
sync::{Arc, RwLock},
|
|
};
|
|
|
|
use crate::datatypes::adf::VarContainer;
|
|
|
|
/// A representation of a formula, still using the strings from the input.
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub enum Formula<'a> {
|
|
/// `c(f)` in the input format.
|
|
Bot,
|
|
/// `c(v)` in the input format.
|
|
Top,
|
|
/// Some atomic variable in the input format.
|
|
Atom(&'a str),
|
|
/// Negation of a subformula.
|
|
Not(Box<Formula<'a>>),
|
|
/// Conjunction of two subformulae.
|
|
And(Box<Formula<'a>>, Box<Formula<'a>>),
|
|
/// Disjunction of two subformulae.
|
|
Or(Box<Formula<'a>>, Box<Formula<'a>>),
|
|
/// Implication of two subformulae.
|
|
Imp(Box<Formula<'a>>, Box<Formula<'a>>),
|
|
/// Exclusive-Or of two subformulae.
|
|
Xor(Box<Formula<'a>>, Box<Formula<'a>>),
|
|
/// If and only if connective between two formulae.
|
|
Iff(Box<Formula<'a>>, Box<Formula<'a>>),
|
|
}
|
|
|
|
impl Formula<'_> {
|
|
pub(crate) fn to_boolean_expr(
|
|
&self,
|
|
) -> biodivine_lib_bdd::boolean_expression::BooleanExpression {
|
|
match self {
|
|
Formula::Top => biodivine_lib_bdd::boolean_expression::BooleanExpression::Const(true),
|
|
Formula::Bot => biodivine_lib_bdd::boolean_expression::BooleanExpression::Const(false),
|
|
Formula::Atom(name) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Variable(name.to_string())
|
|
}
|
|
Formula::Not(subformula) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Not(Box::new(
|
|
subformula.to_boolean_expr(),
|
|
))
|
|
}
|
|
Formula::And(sub_a, sub_b) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::And(
|
|
Box::new(sub_a.to_boolean_expr()),
|
|
Box::new(sub_b.to_boolean_expr()),
|
|
)
|
|
}
|
|
Formula::Or(sub_a, sub_b) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Or(
|
|
Box::new(sub_a.to_boolean_expr()),
|
|
Box::new(sub_b.to_boolean_expr()),
|
|
)
|
|
}
|
|
Formula::Iff(sub_a, sub_b) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Iff(
|
|
Box::new(sub_a.to_boolean_expr()),
|
|
Box::new(sub_b.to_boolean_expr()),
|
|
)
|
|
}
|
|
Formula::Imp(sub_a, sub_b) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Imp(
|
|
Box::new(sub_a.to_boolean_expr()),
|
|
Box::new(sub_b.to_boolean_expr()),
|
|
)
|
|
}
|
|
Formula::Xor(sub_a, sub_b) => {
|
|
biodivine_lib_bdd::boolean_expression::BooleanExpression::Xor(
|
|
Box::new(sub_a.to_boolean_expr()),
|
|
Box::new(sub_b.to_boolean_expr()),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for Formula<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Formula::Atom(a) => {
|
|
write!(f, "{}", a)?;
|
|
}
|
|
Formula::Not(n) => {
|
|
write!(f, "not({:?})", n)?;
|
|
}
|
|
Formula::And(f1, f2) => {
|
|
write!(f, "and({:?},{:?})", f1, f2)?;
|
|
}
|
|
Formula::Or(f1, f2) => {
|
|
write!(f, "or({:?},{:?})", f1, f2)?;
|
|
}
|
|
Formula::Imp(f1, f2) => {
|
|
write!(f, "imp({:?},{:?})", f1, f2)?;
|
|
}
|
|
Formula::Xor(f1, f2) => {
|
|
write!(f, "xor({:?},{:?})", f1, f2)?;
|
|
}
|
|
Formula::Iff(f1, f2) => {
|
|
write!(f, "iff({:?},{:?})", f1, f2)?;
|
|
}
|
|
Formula::Bot => {
|
|
write!(f, "Const(B)")?;
|
|
}
|
|
Formula::Top => {
|
|
write!(f, "Const(T)")?;
|
|
}
|
|
}
|
|
write!(f, "")
|
|
}
|
|
}
|
|
|
|
/// A parse structure to hold all the information given by the input file in one place.
|
|
///
|
|
/// Due to an internal representation with [RefCell][std::cell::RefCell] and [Rc][std::rc::Rc] the values can be
|
|
/// handed over to other structures without further memory needs.
|
|
///
|
|
/// Note that the parser can be utilised by an [ADF][`crate::adf::Adf`] to initialise it with minimal overhead.
|
|
#[derive(Debug)]
|
|
pub struct AdfParser<'a> {
|
|
namelist: Arc<RwLock<Vec<String>>>,
|
|
dict: Arc<RwLock<HashMap<String, usize>>>,
|
|
formulae: RefCell<Vec<Formula<'a>>>,
|
|
formulaname: RefCell<Vec<String>>,
|
|
}
|
|
|
|
impl Default for AdfParser<'_> {
|
|
fn default() -> Self {
|
|
AdfParser {
|
|
namelist: Arc::new(RwLock::new(Vec::new())),
|
|
dict: Arc::new(RwLock::new(HashMap::new())),
|
|
formulae: RefCell::new(Vec::new()),
|
|
formulaname: RefCell::new(Vec::new()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> AdfParser<'b>
|
|
where
|
|
'a: 'b,
|
|
{
|
|
#[allow(dead_code)]
|
|
fn parse_statements(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
|
move |input| {
|
|
let (rem, _) = many1(self.parse_statement())(input)?;
|
|
Ok((rem, ()))
|
|
}
|
|
}
|
|
|
|
/// Parses a full input file and creates internal structures.
|
|
/// Note that this method returns a closure (see the following example for the correct usage).
|
|
/// # Example
|
|
/// ```
|
|
/// let parser = adf_bdd::parser::AdfParser::default();
|
|
/// parser.parse()("s(a).ac(a,c(v)).s(b).ac(b,a).s(c).ac(c,neg(b)).");
|
|
/// let adf = adf_bdd::adf::Adf::from_parser(&parser);
|
|
/// ```
|
|
pub fn parse(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
|
log::info!("[Start] parsing");
|
|
|input| {
|
|
value(
|
|
(),
|
|
all_consuming(many1(alt((self.parse_statement(), self.parse_ac())))),
|
|
)(input)
|
|
}
|
|
}
|
|
|
|
fn parse_statement(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
|
|input| {
|
|
let mut dict = self
|
|
.dict
|
|
.write()
|
|
.expect("RwLock of dict could not get write access");
|
|
let mut namelist = self
|
|
.namelist
|
|
.write()
|
|
.expect("RwLock of namelist could not get write access");
|
|
let (remain, statement) =
|
|
terminated(AdfParser::statement, terminated(tag("."), multispace0))(input)?;
|
|
if !dict.contains_key(statement) {
|
|
let pos = namelist.len();
|
|
namelist.push(String::from(statement));
|
|
dict.insert(namelist[pos].clone(), pos);
|
|
}
|
|
Ok((remain, ()))
|
|
}
|
|
}
|
|
|
|
fn parse_ac(&'a self) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
|
|
|input| {
|
|
let (remain, (name, formula)) =
|
|
terminated(AdfParser::ac, terminated(tag("."), multispace0))(input)?;
|
|
self.formulae.borrow_mut().push(formula);
|
|
self.formulaname.borrow_mut().push(String::from(name));
|
|
Ok((remain, ()))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> AdfParser<'a> {
|
|
/// Creates a new parser, utilising the already existing [VarContainer]
|
|
pub fn with_var_container(var_container: VarContainer) -> AdfParser<'a> {
|
|
AdfParser {
|
|
namelist: var_container.names(),
|
|
dict: var_container.mappings(),
|
|
formulae: RefCell::new(Vec::new()),
|
|
formulaname: RefCell::new(Vec::new()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AdfParser<'_> {
|
|
/// after an update to the namelist, all indizes are updated
|
|
fn regenerate_indizes(&self) {
|
|
self.namelist
|
|
.read()
|
|
.expect("ReadLock on namelist failed")
|
|
.iter()
|
|
.enumerate()
|
|
.for_each(|(i, elem)| {
|
|
self.dict
|
|
.write()
|
|
.expect("WriteLock on dict failed")
|
|
.insert(elem.clone(), i);
|
|
});
|
|
}
|
|
|
|
/// Sort the variables in lexicographical order.
|
|
/// Results, which got used before might become corrupted.
|
|
/// Ensure that all used data is physically copied.
|
|
pub fn varsort_lexi(&self) -> &Self {
|
|
self.namelist
|
|
.write()
|
|
.expect("WriteLock on namelist failed")
|
|
.sort_unstable();
|
|
self.regenerate_indizes();
|
|
self
|
|
}
|
|
|
|
/// Sort the variables in alphanumerical order.
|
|
/// Results, which got used before might become corrupted.
|
|
/// Ensure that all used data is physically copied.
|
|
pub fn varsort_alphanum(&self) -> &Self {
|
|
self.namelist
|
|
.write()
|
|
.expect("WriteLock on namelist failed")
|
|
.string_sort_unstable(natural_lexical_cmp);
|
|
self.regenerate_indizes();
|
|
self
|
|
}
|
|
|
|
fn statement(input: &str) -> IResult<&str, &str> {
|
|
preceded(tag("s"), delimited(tag("("), AdfParser::atomic, tag(")")))(input)
|
|
}
|
|
|
|
fn ac(input: &str) -> IResult<&str, (&str, Formula)> {
|
|
preceded(
|
|
tag("ac"),
|
|
delimited(
|
|
tag("("),
|
|
separated_pair(
|
|
AdfParser::atomic,
|
|
delimited(multispace0, tag(","), multispace0),
|
|
AdfParser::formula,
|
|
),
|
|
tag(")"),
|
|
),
|
|
)(input)
|
|
}
|
|
|
|
fn atomic_term(input: &str) -> IResult<&str, Formula> {
|
|
AdfParser::atomic(input).map(|(input, result)| (input, Formula::Atom(result)))
|
|
}
|
|
|
|
fn formula(input: &str) -> IResult<&str, Formula> {
|
|
alt((
|
|
AdfParser::constant,
|
|
AdfParser::binary_op,
|
|
AdfParser::unary_op,
|
|
AdfParser::atomic_term,
|
|
))(input)
|
|
}
|
|
|
|
fn unary_op(input: &str) -> IResult<&str, Formula> {
|
|
preceded(
|
|
tag("neg"),
|
|
delimited(tag("("), AdfParser::formula, tag(")")),
|
|
)(input)
|
|
.map(|(input, result)| (input, Formula::Not(Box::new(result))))
|
|
}
|
|
|
|
fn constant(input: &str) -> IResult<&str, Formula> {
|
|
alt((
|
|
preceded(tag("c"), delimited(tag("("), tag("v"), tag(")"))),
|
|
preceded(tag("c"), delimited(tag("("), tag("f"), tag(")"))),
|
|
))(input)
|
|
.map(|(input, result)| {
|
|
(
|
|
input,
|
|
match result {
|
|
"v" => Formula::Top,
|
|
"f" => Formula::Bot,
|
|
_ => unreachable!(),
|
|
},
|
|
)
|
|
})
|
|
}
|
|
|
|
fn formula_pair(input: &str) -> IResult<&str, (Formula, Formula)> {
|
|
separated_pair(
|
|
preceded(tag("("), AdfParser::formula),
|
|
delimited(multispace0, tag(","), multispace0),
|
|
terminated(AdfParser::formula, tag(")")),
|
|
)(input)
|
|
}
|
|
|
|
fn and(input: &str) -> IResult<&str, Formula> {
|
|
preceded(tag("and"), AdfParser::formula_pair)(input)
|
|
.map(|(input, (f1, f2))| (input, Formula::And(Box::new(f1), Box::new(f2))))
|
|
}
|
|
|
|
fn or(input: &str) -> IResult<&str, Formula> {
|
|
preceded(tag("or"), AdfParser::formula_pair)(input)
|
|
.map(|(input, (f1, f2))| (input, Formula::Or(Box::new(f1), Box::new(f2))))
|
|
}
|
|
fn imp(input: &str) -> IResult<&str, Formula> {
|
|
preceded(tag("imp"), AdfParser::formula_pair)(input)
|
|
.map(|(input, (f1, f2))| (input, Formula::Imp(Box::new(f1), Box::new(f2))))
|
|
}
|
|
|
|
fn xor(input: &str) -> IResult<&str, Formula> {
|
|
preceded(tag("xor"), AdfParser::formula_pair)(input)
|
|
.map(|(input, (f1, f2))| (input, Formula::Xor(Box::new(f1), Box::new(f2))))
|
|
}
|
|
|
|
fn iff(input: &str) -> IResult<&str, Formula> {
|
|
preceded(tag("iff"), AdfParser::formula_pair)(input)
|
|
.map(|(input, (f1, f2))| (input, Formula::Iff(Box::new(f1), Box::new(f2))))
|
|
}
|
|
|
|
fn binary_op(input: &str) -> IResult<&str, Formula> {
|
|
alt((
|
|
AdfParser::and,
|
|
AdfParser::or,
|
|
AdfParser::imp,
|
|
AdfParser::xor,
|
|
AdfParser::iff,
|
|
))(input)
|
|
}
|
|
|
|
fn atomic(input: &str) -> IResult<&str, &str> {
|
|
alt((
|
|
delimited(tag("\""), take_until("\""), tag("\"")),
|
|
alphanumeric1,
|
|
))(input)
|
|
}
|
|
|
|
/// Allows insight of the number of parsed statements.
|
|
pub fn dict_size(&self) -> usize {
|
|
//self.dict.borrow().len()
|
|
self.dict.read().expect("ReadLock on dict failed").len()
|
|
}
|
|
|
|
/// Returns the number-representation and position of a given statement in string-representation.
|
|
///
|
|
/// Will return [None] if the string does no occur in the dictionary.
|
|
pub fn dict_value(&self, value: &str) -> Option<usize> {
|
|
self.dict
|
|
.read()
|
|
.expect("ReadLock on dict failed")
|
|
.get(value)
|
|
.copied()
|
|
}
|
|
|
|
/// Returns the acceptance condition of a statement at the given position.
|
|
///
|
|
/// Will return [None] if the position does not map to a formula.
|
|
pub fn ac_at(&self, idx: usize) -> Option<Formula> {
|
|
self.formulae.borrow().get(idx).cloned()
|
|
}
|
|
|
|
pub(crate) fn dict(&self) -> Arc<RwLock<HashMap<String, usize>>> {
|
|
Arc::clone(&self.dict)
|
|
}
|
|
|
|
pub(crate) fn namelist(&self) -> Arc<RwLock<Vec<String>>> {
|
|
Arc::clone(&self.namelist)
|
|
}
|
|
|
|
/// Returns a [`VarContainer`][crate::datatypes::adf::VarContainer] which allows to access the variable information gathered by the parser
|
|
pub fn var_container(&self) -> VarContainer {
|
|
VarContainer::from_parser(self.namelist(), self.dict())
|
|
}
|
|
|
|
pub(crate) fn formula_count(&self) -> usize {
|
|
self.formulae.borrow().len()
|
|
}
|
|
|
|
pub(crate) fn formula_order(&self) -> Vec<usize> {
|
|
self.formulaname
|
|
.borrow()
|
|
.iter()
|
|
.map(|name| {
|
|
*self
|
|
.dict
|
|
.read()
|
|
.expect("ReadLock on dict failed")
|
|
.get(name)
|
|
.expect("Dictionary should contain all the used formulanames")
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn atomic_parse() {
|
|
assert_eq!(
|
|
AdfParser::atomic("\" 123 21 ())) ((( {}||| asfjklj fsajfj039409u902 jfi a \""),
|
|
Ok((
|
|
"",
|
|
" 123 21 ())) ((( {}||| asfjklj fsajfj039409u902 jfi a "
|
|
))
|
|
);
|
|
assert_eq!(AdfParser::atomic("foo"), Ok(("", "foo")));
|
|
assert_eq!(AdfParser::atomic("foo()"), Ok(("()", "foo")));
|
|
assert_eq!(
|
|
AdfParser::atomic("()foo"),
|
|
Err(nom::Err::Error(nom::error::Error::new(
|
|
"()foo",
|
|
nom::error::ErrorKind::AlphaNumeric
|
|
)))
|
|
);
|
|
assert!(AdfParser::atomic(" adf").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn statement_parse() {
|
|
assert_eq!(AdfParser::statement("s(ab)"), Ok(("", "ab")));
|
|
assert_eq!(AdfParser::statement("s(\"a b\")"), Ok(("", "a b")));
|
|
assert!(AdfParser::statement("s(a_b)").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn parse_statement() {
|
|
let parser: AdfParser = AdfParser::default();
|
|
|
|
let input = "s(a). s(b). s(c).s(d).s(b).s(c).";
|
|
// many1(parser.parse_statement())(input).unwrap();
|
|
let (_remain, _) = parser.parse_statement()(input).unwrap();
|
|
assert_eq!(parser.dict_size(), 1);
|
|
assert_eq!(parser.dict_value("c"), None);
|
|
|
|
let (_remain, _) = parser.parse_statements()(input).unwrap();
|
|
assert_eq!(parser.dict_size(), 4);
|
|
assert_eq!(parser.dict_value("c"), Some(2usize));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_formula() {
|
|
let input = "and(or(neg(a),iff(\" iff left \",b)),xor(imp(c,d),e))";
|
|
let (_remain, result) = AdfParser::formula(input).unwrap();
|
|
|
|
assert_eq!(
|
|
format!("{:?}", result),
|
|
"and(or(not(a),iff( iff left ,b)),xor(imp(c,d),e))"
|
|
);
|
|
|
|
assert_eq!(
|
|
AdfParser::formula("and(c(v),c(f))").unwrap(),
|
|
(
|
|
"",
|
|
Formula::And(Box::new(Formula::Top), Box::new(Formula::Bot))
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse() {
|
|
let parser = AdfParser::default();
|
|
let input = "s(a).s(c).ac(a,b).ac(b,neg(a)).s(b).ac(c,and(c(v),or(c(f),a))).";
|
|
|
|
let (remain, _) = parser.parse()(input).unwrap();
|
|
assert_eq!(remain, "");
|
|
assert_eq!(parser.dict_size(), 3);
|
|
assert_eq!(parser.dict_value("b"), Some(2usize));
|
|
assert_eq!(
|
|
format!("{:?}", parser.ac_at(1).unwrap()),
|
|
format!("{:?}", Formula::Not(Box::new(Formula::Atom("a"))))
|
|
);
|
|
assert_eq!(parser.formula_count(), 3);
|
|
assert_eq!(parser.formula_order(), vec![0, 2, 1]);
|
|
}
|
|
|
|
#[test]
|
|
fn non_consuming_parse() {
|
|
let parser = AdfParser::default();
|
|
let input = "s(a).s(c).ac(a,b).ac(b,neg(a)).s(b).ac(c,and(c(v),or(c(f),a))). wee";
|
|
|
|
let x = parser.parse()(input);
|
|
assert!(x.is_err());
|
|
assert_eq!(
|
|
x.err().unwrap(),
|
|
nom::Err::Error(nom::error::Error::new("wee", nom::error::ErrorKind::Eof))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn constant() {
|
|
assert_eq!(AdfParser::constant("c(v)").unwrap().1, Formula::Top);
|
|
assert_eq!(AdfParser::constant("c(f)").unwrap().1, Formula::Bot);
|
|
assert_eq!(format!("{:?}", Formula::Top), "Const(T)");
|
|
assert_eq!(format!("{:?}", Formula::Bot), "Const(B)");
|
|
}
|
|
|
|
#[test]
|
|
fn sort_updates() {
|
|
let parser = AdfParser::default();
|
|
let input = "s(a).s(c).ac(a,b).ac(b,neg(a)).s(b).ac(c,and(c(v),or(c(f),a))).";
|
|
|
|
parser.parse()(input).unwrap();
|
|
assert_eq!(parser.dict_value("a"), Some(0));
|
|
assert_eq!(parser.dict_value("b"), Some(2));
|
|
assert_eq!(parser.dict_value("c"), Some(1));
|
|
|
|
parser.varsort_lexi();
|
|
|
|
assert_eq!(parser.dict_value("a"), Some(0));
|
|
assert_eq!(parser.dict_value("b"), Some(1));
|
|
assert_eq!(parser.dict_value("c"), Some(2));
|
|
|
|
let parser = AdfParser::default();
|
|
let input = "s(a2).s(0).s(1).s(2).s(10).s(11).s(20).ac(0,c(v)).ac(1,c(v)).ac(2,c(v)).ac(10,c(v)).ac(20,c(v)).ac(11,c(v)).ac(a2,c(f)).";
|
|
|
|
parser.parse()(input).unwrap();
|
|
assert_eq!(parser.dict_value("a2"), Some(0));
|
|
assert_eq!(parser.dict_value("0"), Some(1));
|
|
assert_eq!(parser.dict_value("1"), Some(2));
|
|
assert_eq!(parser.dict_value("2"), Some(3));
|
|
assert_eq!(parser.dict_value("10"), Some(4));
|
|
assert_eq!(parser.dict_value("11"), Some(5));
|
|
assert_eq!(parser.dict_value("20"), Some(6));
|
|
|
|
parser.varsort_lexi();
|
|
assert_eq!(parser.dict_value("0"), Some(0));
|
|
assert_eq!(parser.dict_value("1"), Some(1));
|
|
assert_eq!(parser.dict_value("2"), Some(4));
|
|
assert_eq!(parser.dict_value("10"), Some(2));
|
|
assert_eq!(parser.dict_value("11"), Some(3));
|
|
assert_eq!(parser.dict_value("20"), Some(5));
|
|
assert_eq!(parser.dict_value("a2"), Some(6));
|
|
|
|
parser.varsort_alphanum();
|
|
assert_eq!(parser.dict_value("0"), Some(0));
|
|
assert_eq!(parser.dict_value("1"), Some(1));
|
|
assert_eq!(parser.dict_value("2"), Some(2));
|
|
assert_eq!(parser.dict_value("10"), Some(3));
|
|
assert_eq!(parser.dict_value("11"), Some(4));
|
|
assert_eq!(parser.dict_value("20"), Some(5));
|
|
assert_eq!(parser.dict_value("a2"), Some(6));
|
|
}
|
|
}
|