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_maybe())
|
||||
.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::simple_sql_function_factory())
|
||||
.add(crate::lang::vocabulary::CommentStatementFactory)
|
||||
|
|
|
@ -311,15 +311,16 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
|
|||
} else {
|
||||
// <some other op>.(predicate)
|
||||
//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)?;
|
||||
tokens.extend(end_tokens);
|
||||
op = VariableOrOp::Op(inner_op.into());
|
||||
}
|
||||
assert_token_raw(MpsToken::Dot, 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)?;
|
||||
assert_token_raw(MpsToken::CloseBracket, tokens)?;
|
||||
assert_token_raw(MpsToken::CloseBracket, &mut end_tokens)?;
|
||||
Ok(Box::new(MpsFilterStatement {
|
||||
predicate: filter,
|
||||
iterable: op,
|
||||
|
|
|
@ -2,6 +2,7 @@ mod empty_filter;
|
|||
mod field_filter;
|
||||
mod field_filter_maybe;
|
||||
mod index_filter;
|
||||
mod range_filter;
|
||||
pub(crate) mod utility;
|
||||
|
||||
pub use empty_filter::{
|
||||
|
@ -12,3 +13,4 @@ pub use field_filter::{
|
|||
};
|
||||
pub use field_filter_maybe::{field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory};
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
#[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