Refactor field filters to share same base parse node

This commit is contained in:
NGnius (Graham) 2023-07-09 15:30:22 -04:00
parent 00812bb3a3
commit 7dbd896348
9 changed files with 247 additions and 151 deletions

View file

@ -6,21 +6,25 @@ pub(crate) fn standard_vocab(vocabulary: &mut LanguageDictionary) {
// filters
.add(crate::lang::vocabulary::filters::empty_filter())
.add(crate::lang::vocabulary::filters::unique_filter()) // accepts .(unique)
.add(crate::lang::vocabulary::filters::field_filter()) // accepts any .(something)
.add(crate::lang::vocabulary::filters::field_filter_maybe())
.add(crate::lang::vocabulary::filters::index_filter())
.add(crate::lang::vocabulary::filters::range_filter())
.add(crate::lang::vocabulary::filters::field_like_filter())
.add(crate::lang::vocabulary::filters::field_re_filter())
.add( // accepts any .(.something)
crate::lang::vocabulary::filters::field::FieldFilterBlockFactory::new()
.push(crate::lang::vocabulary::filters::field::FieldFilterComparisonFactory)
.push(crate::lang::vocabulary::filters::field::FieldFilterMaybeFactory)
.push(crate::lang::vocabulary::filters::field::FieldLikeFilterFactory)
.push(crate::lang::vocabulary::filters::field::FieldRegexFilterFactory)
.to_statement_factory()
)
.add(crate::lang::vocabulary::filters::index_filter())
.add(crate::lang::vocabulary::filters::unique_field_filter())
.add(crate::lang::vocabulary::filters::nonempty_filter())
// sorters
.add(crate::lang::vocabulary::sorters::empty_sort())
.add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(shuffle)
.add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(~shuffle)
.add(crate::lang::vocabulary::sorters::bliss_sort())
.add(crate::lang::vocabulary::sorters::bliss_next_sort())
.add(crate::lang::vocabulary::sorters::radio_sort())
.add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(name)
.add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(.name)
// iter blocks
.add(
crate::lang::ItemBlockFactory::new()

View file

@ -1,11 +1,11 @@
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use super::utility::{assert_comparison_operator, comparison_op};
use crate::lang::utility::{assert_token, assert_type, check_is_type, assert_token_raw};
use super::super::utility::{assert_comparison_operator, comparison_op};
use super::{FieldFilterFactory, FieldFilterPredicate};
use crate::lang::utility::{assert_token, assert_type, check_is_type};
use crate::lang::LanguageDictionary;
use crate::lang::TypePrimitive;
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::processing::general::Type;
use crate::tokens::Token;
@ -46,7 +46,7 @@ impl Display for FieldFilter {
}
}
impl FilterPredicate for FieldFilter {
impl FieldFilterPredicate for FieldFilter {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
@ -93,41 +93,33 @@ impl FilterPredicate for FieldFilter {
fn reset(&mut self) -> Result<(), RuntimeMsg> {
Ok(())
}
fn box_clone(&self) -> Box<dyn FieldFilterPredicate + 'static> {
Box::new(self.clone())
}
}
pub struct FieldFilterFactory;
pub struct FieldFilterComparisonFactory;
impl FilterFactory<FieldFilter> for FieldFilterFactory {
fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool {
impl FieldFilterFactory<FieldFilter> for FieldFilterComparisonFactory {
fn is_filter(&self, tokens: &[Token]) -> bool {
let tokens_len = tokens.len();
(tokens_len >= 3
(tokens_len >= 1
// .field > variable OR .field < variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_open_angle_bracket() || tokens[2].is_close_angle_bracket()))
|| (tokens_len >= 4 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_open_angle_bracket() || tokens[2].is_close_angle_bracket() || tokens[2].is_equals() || tokens[2].is_exclamation())
&& tokens[3].is_equals()
&& !(tokens_len > 4 && tokens[4].is_equals())
&& (tokens[0].is_open_angle_bracket() || tokens[0].is_close_angle_bracket()))
|| (tokens_len >= 2 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable
&& (tokens[0].is_open_angle_bracket() || tokens[0].is_close_angle_bracket() || tokens[0].is_equals() || tokens[0].is_exclamation())
&& tokens[1].is_equals()
&& !(tokens_len > 2 && tokens[2].is_equals())
)
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
_dict: &LanguageDictionary,
) -> Result<FieldFilter, SyntaxError> {
assert_token_raw(Token::Dot, tokens)?;
let field = assert_token(
|t| match t {
Token::Name(n) => Some(n),
_ => None,
},
Token::Name("field_name".into()),
tokens,
)?;
let compare_operator = assert_comparison_operator(tokens)?;
if check_is_type(&tokens[0]) {
let value = VariableOrValue::Value(assert_type(tokens)?);
@ -159,10 +151,3 @@ impl FilterFactory<FieldFilter> for FieldFilterFactory {
}
}
}
pub type FieldFilterStatementFactory = FilterStatementFactory<FieldFilter, FieldFilterFactory>;
#[inline(always)]
pub fn field_filter() -> FieldFilterStatementFactory {
FieldFilterStatementFactory::new(FieldFilterFactory)
}

View file

@ -0,0 +1,160 @@
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use crate::tokens::Token;
use crate::lang::utility::{assert_token, assert_token_raw};
use crate::lang::LanguageDictionary;
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::Context;
use crate::Item;
pub trait FieldFilterFactory<T: FieldFilterPredicate + 'static>: Send + Sync {
fn is_filter(&self, tokens: &[Token]) -> bool;
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
dict: &LanguageDictionary,
) -> Result<T, SyntaxError>;
}
pub trait FieldFilterPredicate: Send + Sync + Debug + Display {
fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg>;
fn is_complete(&self) -> bool;
fn reset(&mut self) -> Result<(), RuntimeMsg>;
fn box_clone(&self) -> Box<dyn FieldFilterPredicate + 'static>;
}
pub struct FieldFilterFactoryBoxer<T: FieldFilterPredicate + 'static> {
inner: Box<dyn FieldFilterFactory<T>>
}
#[derive(Debug)]
pub struct BoxedFilterPredicate {
inner: Box<dyn FieldFilterPredicate + 'static>
}
impl Clone for BoxedFilterPredicate {
fn clone(&self) -> Self {
Self {
inner: self.inner.box_clone()
}
}
}
impl Display for BoxedFilterPredicate {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
(&self.inner as &dyn Display).fmt(f)
}
}
impl FilterPredicate for BoxedFilterPredicate {
fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
self.inner.matches(item, ctx)
}
fn is_complete(&self) -> bool {
self.inner.is_complete()
}
fn reset(&mut self) -> Result<(), RuntimeMsg> {
self.inner.reset()
}
}
impl FieldFilterPredicate for BoxedFilterPredicate {
fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
self.inner.matches(item, ctx)
}
fn is_complete(&self) -> bool {
self.inner.is_complete()
}
fn reset(&mut self) -> Result<(), RuntimeMsg> {
self.inner.reset()
}
fn box_clone(&self) -> Box<dyn FieldFilterPredicate + 'static> {
self.inner.box_clone()
}
}
impl <T: FieldFilterPredicate + 'static> FieldFilterFactory<BoxedFilterPredicate> for FieldFilterFactoryBoxer<T> {
fn is_filter(&self, tokens: &[Token]) -> bool {
self.inner.is_filter(tokens)
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
dict: &LanguageDictionary,
) -> Result<BoxedFilterPredicate, SyntaxError> {
self.inner.build_filter(tokens, field, dict).map(|x| BoxedFilterPredicate { inner: Box::new(x) })
}
}
pub struct FieldFilterBlockFactory {
field_filters: Vec<Box<dyn FieldFilterFactory<BoxedFilterPredicate>>>,
}
impl FieldFilterBlockFactory {
pub fn new() -> Self {
Self {
field_filters: Vec::new(),
}
}
pub fn push<T: FieldFilterPredicate + 'static, F: FieldFilterFactory<T> + 'static>(mut self, factory: F) -> Self {
self.field_filters.push(
Box::new(FieldFilterFactoryBoxer { inner: Box::new(factory) })
);
self
}
#[inline(always)]
pub fn to_statement_factory(self) -> FieldFilterStatementFactory {
FieldFilterStatementFactory::new(self)
}
}
impl FilterFactory<BoxedFilterPredicate> for FieldFilterBlockFactory {
fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool {
tokens.len() > 1 && tokens[0].is_dot()
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
dict: &LanguageDictionary,
) -> Result<BoxedFilterPredicate, SyntaxError> {
assert_token_raw(Token::Dot, tokens)?;
let field = assert_token(
|t| match t {
Token::Name(n) => Some(n),
_ => None,
},
Token::Name("field_name".into()),
tokens,
)?;
for filter in &self.field_filters {
if filter.is_filter(tokens.make_contiguous()) {
return filter.build_filter(tokens, field, dict);
}
}
Err(SyntaxError {
got: tokens.front().cloned(),
token: Token::Name("<comparison op>".into()),
line: 0,
})
}
}
pub type FieldFilterStatementFactory =
FilterStatementFactory<BoxedFilterPredicate, FieldFilterBlockFactory>;

View file

@ -1,45 +1,34 @@
use std::collections::VecDeque;
use super::utility::assert_comparison_operator;
use super::super::utility::assert_comparison_operator;
use super::{field_filter::VariableOrValue, FieldFilter, FieldFilterErrorHandling};
use crate::lang::utility::{assert_token, assert_token_raw, assert_type, check_is_type};
use crate::lang::LanguageDictionary;
use crate::lang::SyntaxError;
use crate::lang::{FilterFactory, FilterStatementFactory};
//use crate::lang::{FilterFactory, FilterStatementFactory};
use super::FieldFilterFactory;
use crate::tokens::Token;
pub struct FieldFilterMaybeFactory;
impl FilterFactory<FieldFilter> for FieldFilterMaybeFactory {
fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool {
impl FieldFilterFactory<FieldFilter> for FieldFilterMaybeFactory {
fn is_filter(&self, tokens: &[Token]) -> bool {
let tokens_len = tokens.len();
(tokens_len >= 4 // .field > variable OR .field < variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_interrogation() || tokens[2].is_exclamation())
&& (tokens[3].is_open_angle_bracket() || tokens[3].is_close_angle_bracket()))
|| (tokens_len >= 5 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_interrogation() || tokens[2].is_exclamation())
&& (tokens[3].is_open_angle_bracket() || tokens[3].is_close_angle_bracket() || tokens[3].is_equals() || tokens[3].is_exclamation())
&& tokens[4].is_equals())
(tokens_len >= 2 // .field > variable OR .field < variable
&& (tokens[0].is_interrogation() || tokens[0].is_exclamation())
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket()))
|| (tokens_len >= 3 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable
&& (tokens[0].is_interrogation() || tokens[0].is_exclamation())
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket() || tokens[1].is_equals() || tokens[1].is_exclamation())
&& tokens[2].is_equals())
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
_dict: &LanguageDictionary,
) -> Result<FieldFilter, SyntaxError> {
assert_token_raw(Token::Dot, tokens)?;
let field = assert_token(
|t| match t {
Token::Name(n) => Some(n),
_ => None,
},
Token::Name("field_name".into()),
tokens,
)?;
let error_f;
let error_c;
if tokens[0].is_interrogation() {
@ -82,11 +71,3 @@ impl FilterFactory<FieldFilter> for FieldFilterMaybeFactory {
}
}
}
pub type FieldFilterMaybeStatementFactory =
FilterStatementFactory<FieldFilter, FieldFilterMaybeFactory>;
#[inline(always)]
pub fn field_filter_maybe() -> FieldFilterMaybeStatementFactory {
FieldFilterMaybeStatementFactory::new(FieldFilterMaybeFactory)
}

View file

@ -5,7 +5,7 @@ use super::field_filter::{FieldFilterErrorHandling, VariableOrValue};
use crate::lang::utility::{assert_token, assert_token_raw, check_name};
use crate::lang::LanguageDictionary;
use crate::lang::TypePrimitive;
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use super::{FieldFilterFactory, FieldFilterPredicate};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::processing::general::Type;
use crate::tokens::Token;
@ -22,7 +22,7 @@ pub struct FieldLikeFilter {
impl FieldLikeFilter {
fn sanitise_string(s: &str) -> String {
super::utility::sanitise_string(s)
super::super::utility::sanitise_string(s)
}
}
@ -35,7 +35,7 @@ impl Display for FieldLikeFilter {
}
}
impl FilterPredicate for FieldLikeFilter {
impl FieldFilterPredicate for FieldLikeFilter {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
@ -74,38 +74,30 @@ impl FilterPredicate for FieldLikeFilter {
fn reset(&mut self) -> Result<(), RuntimeMsg> {
Ok(())
}
fn box_clone(&self) -> Box<dyn FieldFilterPredicate + 'static> {
Box::new(self.clone())
}
}
pub struct FieldLikeFilterFactory;
impl FilterFactory<FieldLikeFilter> for FieldLikeFilterFactory {
fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool {
impl FieldFilterFactory<FieldLikeFilter> for FieldLikeFilterFactory {
fn is_filter(&self, tokens: &[Token]) -> bool {
let tokens_len = tokens.len();
(tokens_len >= 3 // field like variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (check_name("like", tokens[2]) || check_name("unlike", tokens[2])))
|| (tokens_len >= 4 // field? like variable OR field! like variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_interrogation() || tokens[2].is_exclamation())
&& (check_name("like", tokens[3]) || check_name("unlike", tokens[3])))
(tokens_len >= 1 // field like variable
&& (check_name("like", &tokens[0]) || check_name("unlike", &tokens[0])))
|| (tokens_len >= 2 // field? like variable OR field! like variable
&& (tokens[0].is_interrogation() || tokens[0].is_exclamation())
&& (check_name("like", &tokens[1]) || check_name("unlike", &tokens[1])))
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
_dict: &LanguageDictionary,
) -> Result<FieldLikeFilter, SyntaxError> {
assert_token_raw(Token::Dot, tokens)?;
let field = assert_token(
|t| match t {
Token::Name(n) => Some(n),
_ => None,
},
Token::Name("field_name".into()),
tokens,
)?;
let error_handling = if tokens[0].is_interrogation() {
assert_token_raw(Token::Interrogation, tokens)?;
FieldFilterErrorHandling::Ignore
@ -164,11 +156,3 @@ impl FilterFactory<FieldLikeFilter> for FieldLikeFilterFactory {
}
}
}
pub type FieldLikeFilterStatementFactory =
FilterStatementFactory<FieldLikeFilter, FieldLikeFilterFactory>;
#[inline(always)]
pub fn field_like_filter() -> FieldLikeFilterStatementFactory {
FieldLikeFilterStatementFactory::new(FieldLikeFilterFactory)
}

View file

@ -7,7 +7,7 @@ use super::field_filter::{FieldFilterErrorHandling, VariableOrValue};
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
use crate::lang::LanguageDictionary;
use crate::lang::TypePrimitive;
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use super::{FieldFilterFactory, FieldFilterPredicate};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::processing::general::Type;
use crate::tokens::Token;
@ -32,7 +32,7 @@ impl Display for FieldRegexFilter {
}
}
impl FilterPredicate for FieldRegexFilter {
impl FieldFilterPredicate for FieldRegexFilter {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
@ -83,38 +83,30 @@ impl FilterPredicate for FieldRegexFilter {
//self.regex_cache = None;
Ok(())
}
fn box_clone(&self) -> Box<dyn FieldFilterPredicate + 'static> {
Box::new(self.clone())
}
}
pub struct FieldRegexFilterFactory;
impl FilterFactory<FieldRegexFilter> for FieldRegexFilterFactory {
fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool {
impl FieldFilterFactory<FieldRegexFilter> for FieldRegexFilterFactory {
fn is_filter(&self, tokens: &[Token]) -> bool {
let tokens_len = tokens.len();
(tokens_len >= 3 // .field like variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& check_name("matches", tokens[2]))
|| (tokens_len >= 4 // .field? like variable OR .field! like variable
&& tokens[0].is_dot()
&& tokens[1].is_name()
&& (tokens[2].is_interrogation() || tokens[2].is_exclamation())
&& check_name("matches", tokens[3]))
(tokens_len >= 1 // .field like variable
&& check_name("matches", &tokens[0]))
|| (tokens_len >= 2 // .field? like variable OR .field! like variable
&& (tokens[0].is_interrogation() || tokens[0].is_exclamation())
&& check_name("matches", &tokens[1]))
}
fn build_filter(
&self,
tokens: &mut VecDeque<Token>,
field: String,
_dict: &LanguageDictionary,
) -> Result<FieldRegexFilter, SyntaxError> {
assert_token_raw(Token::Dot, tokens)?;
let field = assert_token(
|t| match t {
Token::Name(n) => Some(n),
_ => None,
},
Token::Name("field_name".into()),
tokens,
)?;
let error_handling = if tokens[0].is_interrogation() {
assert_token_raw(Token::Interrogation, tokens)?;
FieldFilterErrorHandling::Ignore
@ -220,11 +212,3 @@ fn build_regex(pattern: &str, flags: u8) -> Result<Regex, regex::Error> {
.ignore_whitespace((flags & (1 << 5)) != 0)
.build()
}
pub type FieldRegexFilterStatementFactory =
FilterStatementFactory<FieldRegexFilter, FieldRegexFilterFactory>;
#[inline(always)]
pub fn field_re_filter() -> FieldRegexFilterStatementFactory {
FieldRegexFilterStatementFactory::new(FieldRegexFilterFactory)
}

View file

@ -0,0 +1,14 @@
mod field_filter;
mod field_filter_factory;
mod field_filter_maybe;
mod field_like_filter;
mod field_match_filter;
pub use field_filter::{
FieldFilter, FieldFilterErrorHandling, FieldFilterComparisonFactory,
};
pub use field_filter_maybe::FieldFilterMaybeFactory;
pub use field_like_filter::FieldLikeFilterFactory;
pub use field_match_filter::FieldRegexFilterFactory;
pub use field_filter_factory::*;

View file

@ -1,8 +1,5 @@
mod empty_filter;
mod field_filter;
mod field_filter_maybe;
mod field_like_filter;
mod field_match_filter;
pub mod field;
mod index_filter;
mod nonempty_filter;
mod range_filter;
@ -12,19 +9,6 @@ pub(crate) mod utility;
pub use empty_filter::{
empty_filter, EmptyFilter, EmptyFilterFactory, EmptyFilterStatementFactory,
};
pub use field_filter::{
field_filter, FieldFilter, FieldFilterErrorHandling, FieldFilterFactory,
FieldFilterStatementFactory,
};
pub use field_filter_maybe::{
field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory,
};
pub use field_like_filter::{
field_like_filter, FieldLikeFilterFactory, FieldLikeFilterStatementFactory,
};
pub use field_match_filter::{
field_re_filter, FieldRegexFilterFactory, FieldRegexFilterStatementFactory,
};
pub use index_filter::{
index_filter, IndexFilter, IndexFilterFactory, IndexFilterStatementFactory,
};

View file

@ -1,7 +1,7 @@
use std::collections::{HashSet, VecDeque};
use std::fmt::{Debug, Display, Error, Formatter};
use super::field_filter::FieldFilterErrorHandling;
use super::field::FieldFilterErrorHandling;
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use crate::lang::{LanguageDictionary, TypePrimitive};