Skip to content

Commit

Permalink
Add universal and existential quantifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
nwagner84 committed Dec 17, 2023
1 parent e32aaf1 commit 36fbd7b
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 84 deletions.
18 changes: 18 additions & 0 deletions crates/pica-matcher/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@ pub(crate) fn parse_relational_op_usize(
.parse_next(i)
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum Quantifier {
All,
#[default]
Any,
}

#[inline]
pub(crate) fn parse_quantifier(i: &mut &[u8]) -> PResult<Quantifier> {
alt((
"ALL".value(Quantifier::All),
"∀".value(Quantifier::All),
"ANY".value(Quantifier::Any),
"∃".value(Quantifier::Any),
))
.parse_next(i)
}

#[derive(Debug, Copy, Clone)]
enum Quotes {
Single,
Expand Down
34 changes: 24 additions & 10 deletions crates/pica-matcher/src/field_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ use winnow::error::ParserError;
use winnow::prelude::*;

use crate::common::{
parse_relational_op_usize, ws, BooleanOp, RelationalOp,
parse_quantifier, parse_relational_op_usize, ws, BooleanOp,
RelationalOp,
};
use crate::occurrence_matcher::parse_occurrence_matcher;
use crate::subfield_matcher::{
self, parse_subfield_matcher, parse_subfield_singleton_matcher,
};
use crate::tag_matcher::parse_tag_matcher;
use crate::{
MatcherOptions, OccurrenceMatcher, ParseMatcherError,
MatcherOptions, OccurrenceMatcher, ParseMatcherError, Quantifier,
SubfieldMatcher, TagMatcher,
};

Expand Down Expand Up @@ -113,6 +114,7 @@ impl FromStr for ExistsMatcher {
/// criterion.
#[derive(Debug, Clone)]
pub struct SubfieldsMatcher {
quantifier: Quantifier,
tag_matcher: TagMatcher,
occurrence_matcher: OccurrenceMatcher,
subfield_matcher: SubfieldMatcher,
Expand Down Expand Up @@ -157,25 +159,33 @@ impl SubfieldsMatcher {
fields: impl IntoIterator<Item = &'a FieldRef<'a>>,
options: &MatcherOptions,
) -> bool {
fields.into_iter().any(|field| {
let mut fields = fields.into_iter().filter(|field| {
self.tag_matcher == field.tag()
&& self.occurrence_matcher == field.occurrence()
&& self
.subfield_matcher
.is_match(field.subfields(), options)
})
});

let check_fn = |field: &FieldRef| -> bool {
self.subfield_matcher.is_match(field.subfields(), options)
};

match self.quantifier {
Quantifier::Any => fields.any(check_fn),
Quantifier::All => fields.all(check_fn),
}
}
}

fn parse_subfields_matcher_dot(
i: &mut &[u8],
) -> PResult<SubfieldsMatcher> {
(
opt(ws(parse_quantifier)).map(Option::unwrap_or_default),
parse_tag_matcher,
parse_occurrence_matcher,
preceded('.', parse_subfield_singleton_matcher),
)
.map(|(t, o, s)| SubfieldsMatcher {
.map(|(q, t, o, s)| SubfieldsMatcher {
quantifier: q,
tag_matcher: t,
occurrence_matcher: o,
subfield_matcher: s,
Expand All @@ -187,11 +197,13 @@ fn parse_subfields_matcher_bracket(
i: &mut &[u8],
) -> PResult<SubfieldsMatcher> {
(
opt(ws(parse_quantifier)).map(Option::unwrap_or_default),
parse_tag_matcher,
parse_occurrence_matcher,
delimited(ws('{'), parse_subfield_matcher, ws('}')),
)
.map(|(t, o, s)| SubfieldsMatcher {
.map(|(q, t, o, s)| SubfieldsMatcher {
quantifier: q,
tag_matcher: t,
occurrence_matcher: o,
subfield_matcher: s,
Expand Down Expand Up @@ -523,13 +535,15 @@ fn parse_field_matcher_exists(i: &mut &[u8]) -> PResult<FieldMatcher> {
FieldMatcher::Singleton(SingletonMatcher::Exists(matcher))
}),
(
opt(parse_quantifier).map(Option::unwrap_or_default),
parse_tag_matcher,
parse_occurrence_matcher,
preceded(ws('.'), subfield_matcher::parse_exists_matcher),
)
.map(|(t, o, s)| {
.map(|(q, t, o, s)| {
FieldMatcher::Singleton(SingletonMatcher::Subfields(
SubfieldsMatcher {
quantifier: q,
tag_matcher: t,
occurrence_matcher: o,
subfield_matcher: SubfieldMatcher::Singleton(
Expand Down
2 changes: 1 addition & 1 deletion crates/pica-matcher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod record_matcher;
pub mod subfield_matcher;
mod tag_matcher;

pub use common::RelationalOp;
pub use common::{Quantifier, RelationalOp};
pub use error::ParseMatcherError;
pub use field_matcher::FieldMatcher;
pub use matcher_builder::MatcherBuilder;
Expand Down
Loading

0 comments on commit 36fbd7b

Please sign in to comment.