Add range filtering
This commit is contained in:
parent
ca17313b7b
commit
d23495a5ef
5 changed files with 216 additions and 2 deletions
|
@ -160,6 +160,7 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) {
|
||||||
.add(crate::lang::vocabulary::filters::field_filter())
|
.add(crate::lang::vocabulary::filters::field_filter())
|
||||||
.add(crate::lang::vocabulary::filters::field_filter_maybe())
|
.add(crate::lang::vocabulary::filters::field_filter_maybe())
|
||||||
.add(crate::lang::vocabulary::filters::index_filter())
|
.add(crate::lang::vocabulary::filters::index_filter())
|
||||||
|
.add(crate::lang::vocabulary::filters::range_filter())
|
||||||
.add(crate::lang::vocabulary::sql_function_factory())
|
.add(crate::lang::vocabulary::sql_function_factory())
|
||||||
.add(crate::lang::vocabulary::simple_sql_function_factory())
|
.add(crate::lang::vocabulary::simple_sql_function_factory())
|
||||||
.add(crate::lang::vocabulary::CommentStatementFactory)
|
.add(crate::lang::vocabulary::CommentStatementFactory)
|
||||||
|
|
|
@ -311,15 +311,16 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
|
||||||
} else {
|
} else {
|
||||||
// <some other op>.(predicate)
|
// <some other op>.(predicate)
|
||||||
//let mut new_tokens = tokens.range(0..start_of_op).map(|x| x.to_owned()).collect();
|
//let mut new_tokens = tokens.range(0..start_of_op).map(|x| x.to_owned()).collect();
|
||||||
let end_tokens = tokens.split_off(start_of_op);
|
let end_tokens = tokens.split_off(start_of_op); // don't parse filter in inner statement
|
||||||
let inner_op = dict.try_build_statement(tokens)?;
|
let inner_op = dict.try_build_statement(tokens)?;
|
||||||
tokens.extend(end_tokens);
|
tokens.extend(end_tokens);
|
||||||
op = VariableOrOp::Op(inner_op.into());
|
op = VariableOrOp::Op(inner_op.into());
|
||||||
}
|
}
|
||||||
assert_token_raw(MpsToken::Dot, tokens)?;
|
assert_token_raw(MpsToken::Dot, tokens)?;
|
||||||
assert_token_raw(MpsToken::OpenBracket, tokens)?;
|
assert_token_raw(MpsToken::OpenBracket, tokens)?;
|
||||||
|
let mut end_tokens = tokens.split_off(tokens.len()-1); // don't parse closing bracket in filter
|
||||||
let filter = self.filter_factory.build_filter(tokens, dict)?;
|
let filter = self.filter_factory.build_filter(tokens, dict)?;
|
||||||
assert_token_raw(MpsToken::CloseBracket, tokens)?;
|
assert_token_raw(MpsToken::CloseBracket, &mut end_tokens)?;
|
||||||
Ok(Box::new(MpsFilterStatement {
|
Ok(Box::new(MpsFilterStatement {
|
||||||
predicate: filter,
|
predicate: filter,
|
||||||
iterable: op,
|
iterable: op,
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod empty_filter;
|
||||||
mod field_filter;
|
mod field_filter;
|
||||||
mod field_filter_maybe;
|
mod field_filter_maybe;
|
||||||
mod index_filter;
|
mod index_filter;
|
||||||
|
mod range_filter;
|
||||||
pub(crate) mod utility;
|
pub(crate) mod utility;
|
||||||
|
|
||||||
pub use empty_filter::{
|
pub use empty_filter::{
|
||||||
|
@ -12,3 +13,4 @@ pub use field_filter::{
|
||||||
};
|
};
|
||||||
pub use field_filter_maybe::{field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory};
|
pub use field_filter_maybe::{field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory};
|
||||||
pub use index_filter::{index_filter, IndexFilter, IndexFilterFactory, IndexFilterStatementFactory};
|
pub use index_filter::{index_filter, IndexFilter, IndexFilterFactory, IndexFilterStatementFactory};
|
||||||
|
pub use range_filter::{range_filter, RangeFilter, RangeFilterFactory, RangeFilterStatementFactory};
|
||||||
|
|
186
mps-interpreter/src/lang/vocabulary/filters/range_filter.rs
Normal file
186
mps-interpreter/src/lang/vocabulary/filters/range_filter.rs
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt::{Debug, Display, Error, Formatter};
|
||||||
|
|
||||||
|
use crate::lang::{MpsLanguageDictionary, MpsTypePrimitive};
|
||||||
|
use crate::lang::{MpsFilterFactory, MpsFilterPredicate, MpsFilterStatementFactory};
|
||||||
|
use crate::lang::{RuntimeError, SyntaxError};
|
||||||
|
use crate::lang::Lookup;
|
||||||
|
use crate::lang::utility::assert_token_raw;
|
||||||
|
use crate::processing::{OpGetter, general::MpsType};
|
||||||
|
use crate::tokens::MpsToken;
|
||||||
|
use crate::MpsContext;
|
||||||
|
use crate::MpsMusicItem;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RangeFilter {
|
||||||
|
start: Option<Lookup>,
|
||||||
|
end: Option<Lookup>,
|
||||||
|
inclusive_end: bool,
|
||||||
|
// state
|
||||||
|
current: u64,
|
||||||
|
complete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RangeFilter {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||||
|
write!(f, "{}{}{}",
|
||||||
|
if let Some(start) = &self.start {format!("{}", start)} else {"".into()},
|
||||||
|
if self.inclusive_end {"="} else {""},
|
||||||
|
if let Some(end) = &self.end {format!("{}", end)} else {"".into()},)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MpsFilterPredicate for RangeFilter {
|
||||||
|
fn matches(
|
||||||
|
&mut self,
|
||||||
|
_item: &MpsMusicItem,
|
||||||
|
ctx: &mut MpsContext,
|
||||||
|
op: &mut OpGetter,
|
||||||
|
) -> Result<bool, RuntimeError> {
|
||||||
|
let start_index = if let Some(start) = &self.start {
|
||||||
|
lookup_to_index(start.get(ctx, op)?, op)?
|
||||||
|
} else {0};
|
||||||
|
let current = self.current;
|
||||||
|
self.current += 1;
|
||||||
|
if current >= start_index {
|
||||||
|
if let Some(end) = &self.end {
|
||||||
|
let end_index = lookup_to_index(end.get(ctx, op)?, op)?;
|
||||||
|
if self.inclusive_end && current <= end_index {
|
||||||
|
if current == end_index {
|
||||||
|
self.complete = true;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
} else if !self.inclusive_end && current < end_index {
|
||||||
|
if self.current == end_index {
|
||||||
|
self.complete = true;
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_complete(&self) -> bool {
|
||||||
|
self.complete
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
self.current = 0;
|
||||||
|
self.complete = false;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_to_index(item: &MpsType, op: &mut OpGetter) -> Result<u64, RuntimeError> {
|
||||||
|
match item {
|
||||||
|
MpsType::Primitive(val) => match val {
|
||||||
|
MpsTypePrimitive::Int(i) => Ok(*i as u64),
|
||||||
|
MpsTypePrimitive::UInt(u) => Ok(*u),
|
||||||
|
MpsTypePrimitive::Float(f) => Ok(*f as u64),
|
||||||
|
val => Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: op(),
|
||||||
|
msg: format!("Cannot use {} as index", val),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
val => Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: op(),
|
||||||
|
msg: format!("Cannot use {} as index", val),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RangeFilterFactory;
|
||||||
|
|
||||||
|
impl MpsFilterFactory<RangeFilter> for RangeFilterFactory {
|
||||||
|
fn is_filter(&self, tokens: &VecDeque<&MpsToken>) -> bool {
|
||||||
|
(
|
||||||
|
// ..
|
||||||
|
tokens.len() == 2
|
||||||
|
&& tokens[0].is_dot()
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
) || (
|
||||||
|
tokens.len() == 3
|
||||||
|
&& ((
|
||||||
|
// ..number
|
||||||
|
tokens[0].is_dot()
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
&& Lookup::check_is(&tokens[2])
|
||||||
|
) || (
|
||||||
|
// number..
|
||||||
|
Lookup::check_is(&tokens[0])
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
&& tokens[2].is_dot()
|
||||||
|
))
|
||||||
|
) || (
|
||||||
|
tokens.len() == 4
|
||||||
|
&& (( // number..number
|
||||||
|
Lookup::check_is(&tokens[0])
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
&& tokens[2].is_dot()
|
||||||
|
&& Lookup::check_is(&tokens[3])
|
||||||
|
) || ( // ..=number
|
||||||
|
tokens[0].is_dot()
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
&& tokens[2].is_equals()
|
||||||
|
&& Lookup::check_is(&tokens[3])
|
||||||
|
))
|
||||||
|
) || (
|
||||||
|
// number..=number
|
||||||
|
tokens.len() == 5
|
||||||
|
&& Lookup::check_is(&tokens[0])
|
||||||
|
&& tokens[1].is_dot()
|
||||||
|
&& tokens[2].is_dot()
|
||||||
|
&& tokens[3].is_equals()
|
||||||
|
&& Lookup::check_is(&tokens[4])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_filter(
|
||||||
|
&self,
|
||||||
|
tokens: &mut VecDeque<MpsToken>,
|
||||||
|
_dict: &MpsLanguageDictionary,
|
||||||
|
) -> Result<RangeFilter, SyntaxError> {
|
||||||
|
// start index
|
||||||
|
let start = if Lookup::check_is(&tokens[0]) {
|
||||||
|
Some(Lookup::parse(tokens)?)
|
||||||
|
} else {None};
|
||||||
|
// ..
|
||||||
|
assert_token_raw(MpsToken::Dot, tokens)?;
|
||||||
|
assert_token_raw(MpsToken::Dot, tokens)?;
|
||||||
|
// tokens VecDeque might now be empty (guaranteed to have tokens up to this point)
|
||||||
|
// = (optional)
|
||||||
|
let equals_at_end = if !tokens.is_empty() && tokens[0].is_equals() {
|
||||||
|
assert_token_raw(MpsToken::Equals, tokens)?;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
// end index
|
||||||
|
let end = if !tokens.is_empty() {
|
||||||
|
Some(Lookup::parse(tokens)?)
|
||||||
|
} else {None};
|
||||||
|
|
||||||
|
Ok(RangeFilter {
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
inclusive_end: equals_at_end,
|
||||||
|
current: 0,
|
||||||
|
complete: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RangeFilterStatementFactory = MpsFilterStatementFactory<RangeFilter, RangeFilterFactory>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn range_filter() -> RangeFilterStatementFactory {
|
||||||
|
RangeFilterStatementFactory::new(RangeFilterFactory)
|
||||||
|
}
|
|
@ -216,3 +216,27 @@ fn execute_indexfilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn execute_rangefilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||||
|
execute_single_line(
|
||||||
|
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(..)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
execute_single_line(
|
||||||
|
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(0..=4)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
execute_single_line(
|
||||||
|
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(..=4)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
execute_single_line(
|
||||||
|
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(0..5)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue