Implement reset functionality on existing statements
This commit is contained in:
parent
90a310bbbe
commit
a8eb390ab8
15 changed files with 190 additions and 38 deletions
|
@ -20,6 +20,10 @@ pub trait MpsFilterPredicate: Clone + Debug + Display {
|
||||||
ctx: &mut MpsContext,
|
ctx: &mut MpsContext,
|
||||||
op: &mut OpGetter,
|
op: &mut OpGetter,
|
||||||
) -> Result<bool, RuntimeError>;
|
) -> Result<bool, RuntimeError>;
|
||||||
|
|
||||||
|
fn is_complete(&self) -> bool;
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MpsFilterFactory<P: MpsFilterPredicate + 'static> {
|
pub trait MpsFilterFactory<P: MpsFilterPredicate + 'static> {
|
||||||
|
@ -88,7 +92,7 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterStatement<P> {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
VariableOrOp::Op(PseudoOp::Real(op)) => op.is_resetable(),
|
VariableOrOp::Op(PseudoOp::Real(op)) => op.is_resetable(),
|
||||||
VariableOrOp::Op(PseudoOp::Fake(_)) => false,
|
VariableOrOp::Op(PseudoOp::Fake(_)) => false,
|
||||||
}
|
}
|
||||||
|
@ -96,10 +100,17 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterStatement<P> {
|
||||||
|
|
||||||
fn reset(&mut self) -> Result<(), RuntimeError> {
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
let fake = PseudoOp::Fake(format!("{}", self));
|
let fake = PseudoOp::Fake(format!("{}", self));
|
||||||
|
self.predicate.reset()?;
|
||||||
match &mut self.iterable {
|
match &mut self.iterable {
|
||||||
VariableOrOp::Variable(s) => {
|
VariableOrOp::Variable(s) => {
|
||||||
let fake_getter = &mut move || fake.clone();
|
let fake_getter = &mut move || fake.clone();
|
||||||
if let MpsType::Op(var) = self.context.as_mut().unwrap().variables.get_mut(s, fake_getter)? {
|
if let MpsType::Op(var) = self
|
||||||
|
.context
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.variables
|
||||||
|
.get_mut(s, fake_getter)?
|
||||||
|
{
|
||||||
var.reset()
|
var.reset()
|
||||||
} else {
|
} else {
|
||||||
Err(RuntimeError {
|
Err(RuntimeError {
|
||||||
|
@ -108,13 +119,13 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterStatement<P> {
|
||||||
msg: "Cannot reset non-iterable filter variable".to_string(),
|
msg: "Cannot reset non-iterable filter variable".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
VariableOrOp::Op(PseudoOp::Real(op)) => op.reset(),
|
VariableOrOp::Op(PseudoOp::Real(op)) => op.reset(),
|
||||||
VariableOrOp::Op(PseudoOp::Fake(_)) => Err(RuntimeError {
|
VariableOrOp::Op(PseudoOp::Fake(_)) => Err(RuntimeError {
|
||||||
line: 0,
|
line: 0,
|
||||||
op: fake,
|
op: fake,
|
||||||
msg: "Cannot reset fake filter".to_string(),
|
msg: "Cannot reset PseudoOp::Fake filter".to_string(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +134,9 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
||||||
type Item = Result<MpsMusicItem, RuntimeError>;
|
type Item = Result<MpsMusicItem, RuntimeError>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.predicate.is_complete() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let self_clone = self.clone();
|
let self_clone = self.clone();
|
||||||
let self_clone2 = self_clone.clone();
|
let self_clone2 = self_clone.clone();
|
||||||
let mut op_getter = move || (Box::new(self_clone.clone()) as Box<dyn MpsOp>).into();
|
let mut op_getter = move || (Box::new(self_clone.clone()) as Box<dyn MpsOp>).into();
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::fmt::{Debug, Display};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
|
|
||||||
use super::MpsLanguageDictionary;
|
use super::MpsLanguageDictionary;
|
||||||
use super::{RuntimeError, SyntaxError};
|
|
||||||
use super::PseudoOp;
|
use super::PseudoOp;
|
||||||
|
use super::{RuntimeError, SyntaxError};
|
||||||
use crate::tokens::MpsToken;
|
use crate::tokens::MpsToken;
|
||||||
use crate::MpsContext;
|
use crate::MpsContext;
|
||||||
use crate::MpsMusicItem;
|
use crate::MpsMusicItem;
|
||||||
|
@ -63,12 +63,17 @@ pub trait MpsOp: Iterator<Item = Result<MpsMusicItem, RuntimeError>> + Debug + D
|
||||||
|
|
||||||
fn escape(&mut self) -> MpsContext;
|
fn escape(&mut self) -> MpsContext;
|
||||||
|
|
||||||
fn is_resetable(&self) -> bool {false}
|
fn is_resetable(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Result<(), RuntimeError> {
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if self.is_resetable() {
|
if self.is_resetable() {
|
||||||
panic!("MpsOp reported that it can be reset but did not implement reset (op: {})", self)
|
panic!(
|
||||||
|
"MpsOp reported that it can be reset but did not implement reset (op: {})",
|
||||||
|
self
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Err(RuntimeError {
|
Err(RuntimeError {
|
||||||
line: 0,
|
line: 0,
|
||||||
|
|
|
@ -51,6 +51,10 @@ impl PseudoOp {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_printable<D: Display + Debug>(item: &D) -> Self {
|
||||||
|
Self::Fake(format!("{}", item))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for PseudoOp {
|
impl Clone for PseudoOp {
|
||||||
|
|
|
@ -106,6 +106,16 @@ impl MpsOp for FilesStatement {
|
||||||
fn escape(&mut self) -> MpsContext {
|
fn escape(&mut self) -> MpsContext {
|
||||||
self.context.take().unwrap()
|
self.context.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_resetable(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
self.has_tried = false;
|
||||||
|
self.file_iter = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilesFunctionFactory;
|
pub struct FilesFunctionFactory;
|
||||||
|
|
|
@ -27,6 +27,14 @@ impl MpsFilterPredicate for EmptyFilter {
|
||||||
) -> Result<bool, RuntimeError> {
|
) -> Result<bool, RuntimeError> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EmptyFilterFactory;
|
pub struct EmptyFilterFactory;
|
||||||
|
|
|
@ -77,6 +77,14 @@ impl MpsFilterPredicate for FieldFilter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_complete(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FieldFilterFactory;
|
pub struct FieldFilterFactory;
|
||||||
|
@ -88,9 +96,9 @@ impl MpsFilterFactory<FieldFilter> for FieldFilterFactory {
|
||||||
&& tokens[0].is_name()
|
&& tokens[0].is_name()
|
||||||
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket())
|
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket())
|
||||||
&& (tokens[2].is_name() || check_is_type(&tokens[2])))
|
&& (tokens[2].is_name() || check_is_type(&tokens[2])))
|
||||||
|| (tokens_len == 4 // field >= variable OR field <= variable
|
|| (tokens_len == 4 // field >= variable OR field <= variable OR field != variable
|
||||||
&& tokens[0].is_name()
|
&& tokens[0].is_name()
|
||||||
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket() || tokens[1].is_equals())
|
&& (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket() || tokens[1].is_equals() || tokens[1].is_exclamation())
|
||||||
&& tokens[2].is_equals()
|
&& tokens[2].is_equals()
|
||||||
&& (tokens[3].is_name() || check_is_type(&tokens[3])))
|
&& (tokens[3].is_name() || check_is_type(&tokens[3])))
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,10 @@ pub fn assert_comparison_operator(tokens: &mut VecDeque<MpsToken>) -> Result<[i8
|
||||||
Ok([1, 1])
|
Ok([1, 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MpsToken::Exclamation => {
|
||||||
|
assert_token_raw(MpsToken::Equals, tokens)?;
|
||||||
|
Ok([-1, 1])
|
||||||
|
}
|
||||||
_ => Err(SyntaxError {
|
_ => Err(SyntaxError {
|
||||||
line: 0,
|
line: 0,
|
||||||
token: MpsToken::Equals, // TODO this can be < > or =
|
token: MpsToken::Equals, // TODO this can be < > or =
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct RepeatStatement {
|
||||||
cache_position: usize,
|
cache_position: usize,
|
||||||
repetitions: usize,
|
repetitions: usize,
|
||||||
loop_forever: bool,
|
loop_forever: bool,
|
||||||
|
original_repetitions: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RepeatStatement {
|
impl Display for RepeatStatement {
|
||||||
|
@ -38,6 +39,7 @@ impl std::clone::Clone for RepeatStatement {
|
||||||
cache_position: self.cache_position,
|
cache_position: self.cache_position,
|
||||||
repetitions: self.repetitions,
|
repetitions: self.repetitions,
|
||||||
loop_forever: self.loop_forever,
|
loop_forever: self.loop_forever,
|
||||||
|
original_repetitions: self.original_repetitions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,12 +52,12 @@ impl Iterator for RepeatStatement {
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
Ok(real) => real,
|
Ok(real) => real,
|
||||||
};
|
};
|
||||||
|
// give context to inner (should only occur on first run)
|
||||||
|
if self.context.is_some() {
|
||||||
|
let ctx = self.context.take().unwrap();
|
||||||
|
real_op.enter(ctx);
|
||||||
|
}
|
||||||
if real_op.is_resetable() {
|
if real_op.is_resetable() {
|
||||||
// give context to inner (should only occur on first run)
|
|
||||||
if self.context.is_some() {
|
|
||||||
let ctx = self.context.take().unwrap();
|
|
||||||
real_op.enter(ctx);
|
|
||||||
}
|
|
||||||
while self.loop_forever || !self.inner_done {
|
while self.loop_forever || !self.inner_done {
|
||||||
while let Some(item) = real_op.next() {
|
while let Some(item) = real_op.next() {
|
||||||
return Some(item);
|
return Some(item);
|
||||||
|
@ -69,14 +71,14 @@ impl Iterator for RepeatStatement {
|
||||||
self.repetitions -= 1;
|
self.repetitions -= 1;
|
||||||
match real_op.reset() {
|
match real_op.reset() {
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// always reset in infinite loop mode
|
// always reset in infinite loop mode
|
||||||
match real_op.reset() {
|
match real_op.reset() {
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +153,42 @@ impl MpsOp for RepeatStatement {
|
||||||
self.inner_statement.try_real().unwrap().escape()
|
self.inner_statement.try_real().unwrap().escape()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_resetable(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
let real_op = self.inner_statement.try_real()?;
|
||||||
|
if self.context.is_some() {
|
||||||
|
let ctx = self.context.take().unwrap();
|
||||||
|
real_op.enter(ctx);
|
||||||
|
}
|
||||||
|
if real_op.is_resetable() {
|
||||||
|
real_op.reset()?;
|
||||||
|
if self.original_repetitions == 0 {
|
||||||
|
self.repetitions = 0;
|
||||||
|
self.inner_done = true;
|
||||||
|
} else {
|
||||||
|
self.repetitions = self.original_repetitions - 1;
|
||||||
|
self.inner_done = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.inner_done {
|
||||||
|
self.repetitions = self.original_repetitions;
|
||||||
|
self.cache_position = 0;
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: PseudoOp::from_printable(self),
|
||||||
|
msg:
|
||||||
|
"Cannot reset part way through repeat when inner statement is not resetable"
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatFunctionFactory;
|
pub struct RepeatFunctionFactory;
|
||||||
|
@ -177,14 +215,17 @@ impl MpsFunctionFactory<RepeatStatement> for RepeatFunctionFactory {
|
||||||
assert_token_raw(MpsToken::Comma, tokens)?;
|
assert_token_raw(MpsToken::Comma, tokens)?;
|
||||||
count = Some(assert_token(
|
count = Some(assert_token(
|
||||||
|t| match t {
|
|t| match t {
|
||||||
MpsToken::Name(n) => n.parse::<usize>().map(
|
MpsToken::Name(n) => n
|
||||||
|d| if d == 0 {
|
.parse::<usize>()
|
||||||
|
.map(|d| {
|
||||||
|
if d == 0 {
|
||||||
inner_done = true;
|
inner_done = true;
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
d-1
|
d - 1
|
||||||
}
|
}
|
||||||
).ok(),
|
})
|
||||||
|
.ok(),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
MpsToken::Name("usize".into()),
|
MpsToken::Name("usize".into()),
|
||||||
|
@ -199,6 +240,7 @@ impl MpsFunctionFactory<RepeatStatement> for RepeatFunctionFactory {
|
||||||
cache_position: 0,
|
cache_position: 0,
|
||||||
repetitions: count.unwrap_or(0),
|
repetitions: count.unwrap_or(0),
|
||||||
loop_forever: count.is_none(),
|
loop_forever: count.is_none(),
|
||||||
|
original_repetitions: count.and_then(|c| Some(c + 1)).unwrap_or(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,16 @@ impl MpsOp for SqlStatement {
|
||||||
fn escape(&mut self) -> MpsContext {
|
fn escape(&mut self) -> MpsContext {
|
||||||
self.context.take().unwrap()
|
self.context.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_resetable(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
self.rows = None;
|
||||||
|
self.current = 0;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::clone::Clone for SqlStatement {
|
impl std::clone::Clone for SqlStatement {
|
||||||
|
@ -88,7 +98,7 @@ impl Iterator for SqlStatement {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.rows = Some(Vec::with_capacity(0));
|
self.rows = Some(Vec::with_capacity(0));
|
||||||
return Some(Err(e));
|
return Some(Err(e));
|
||||||
},
|
}
|
||||||
Ok(rows) => {
|
Ok(rows) => {
|
||||||
self.rows = Some(rows);
|
self.rows = Some(rows);
|
||||||
self.get_item(false)
|
self.get_item(false)
|
||||||
|
|
|
@ -107,6 +107,16 @@ impl MpsOp for SimpleSqlStatement {
|
||||||
fn escape(&mut self) -> MpsContext {
|
fn escape(&mut self) -> MpsContext {
|
||||||
self.context.take().unwrap()
|
self.context.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_resetable(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
self.rows = None;
|
||||||
|
self.current = 0;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::clone::Clone for SimpleSqlStatement {
|
impl std::clone::Clone for SimpleSqlStatement {
|
||||||
|
@ -150,7 +160,7 @@ impl Iterator for SimpleSqlStatement {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.rows = Some(Vec::with_capacity(0));
|
self.rows = Some(Vec::with_capacity(0));
|
||||||
return Some(Err(e));
|
return Some(Err(e));
|
||||||
},
|
}
|
||||||
Ok(rows) => {
|
Ok(rows) => {
|
||||||
self.rows = Some(rows);
|
self.rows = Some(rows);
|
||||||
self.get_item(false)
|
self.get_item(false)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt::{Debug, Display, Error, Formatter};
|
use std::fmt::{Debug, Display, Error, Formatter};
|
||||||
use std::fs::{ReadDir, DirEntry};
|
use std::fs::{DirEntry, ReadDir};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
@ -86,11 +86,16 @@ impl FileIter {
|
||||||
};
|
};
|
||||||
let dir_vec = if root_path.is_dir() {
|
let dir_vec = if root_path.is_dir() {
|
||||||
let mut vec = Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE);
|
let mut vec = Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE);
|
||||||
vec.push(root_path.read_dir().map_err(|e| RuntimeError {
|
vec.push(
|
||||||
line: 0,
|
root_path
|
||||||
op: op(),
|
.read_dir()
|
||||||
msg: format!("Directory read error: {}", e),
|
.map_err(|e| RuntimeError {
|
||||||
})?.into());
|
line: 0,
|
||||||
|
op: op(),
|
||||||
|
msg: format!("Directory read error: {}", e),
|
||||||
|
})?
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
vec
|
vec
|
||||||
} else {
|
} else {
|
||||||
Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE)
|
Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE)
|
||||||
|
@ -257,7 +262,7 @@ impl Iterator for FileIter {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.is_complete = true;
|
self.is_complete = true;
|
||||||
return Some(Err(format!("Directory read error: {}", e)));
|
return Some(Err(format!("Directory read error: {}", e)));
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
return self.next();
|
return self.next();
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,6 @@ pub struct MpsOpStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MpsVariableStorer for MpsOpStorage {
|
impl MpsVariableStorer for MpsOpStorage {
|
||||||
|
|
||||||
fn get_opt(&self, key: &str) -> Option<&MpsType> {
|
fn get_opt(&self, key: &str) -> Option<&MpsType> {
|
||||||
self.storage.get(key)
|
self.storage.get(key)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ pub enum MpsToken {
|
||||||
OpenAngleBracket,
|
OpenAngleBracket,
|
||||||
CloseAngleBracket,
|
CloseAngleBracket,
|
||||||
Dot,
|
Dot,
|
||||||
|
Exclamation,
|
||||||
|
Interrogation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MpsToken {
|
impl MpsToken {
|
||||||
|
@ -30,6 +32,8 @@ impl MpsToken {
|
||||||
"<" => Ok(Self::OpenAngleBracket),
|
"<" => Ok(Self::OpenAngleBracket),
|
||||||
">" => Ok(Self::CloseAngleBracket),
|
">" => Ok(Self::CloseAngleBracket),
|
||||||
"." => Ok(Self::Dot),
|
"." => Ok(Self::Dot),
|
||||||
|
"!" => Ok(Self::Exclamation),
|
||||||
|
"?" => Ok(Self::Interrogation),
|
||||||
_ => {
|
_ => {
|
||||||
// name validation
|
// name validation
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
|
@ -138,6 +142,20 @@ impl MpsToken {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_exclamation(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Exclamation => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_interrogation(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Interrogation => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for MpsToken {
|
impl Display for MpsToken {
|
||||||
|
@ -156,6 +174,8 @@ impl Display for MpsToken {
|
||||||
Self::OpenAngleBracket => write!(f, "<"),
|
Self::OpenAngleBracket => write!(f, "<"),
|
||||||
Self::CloseAngleBracket => write!(f, ">"),
|
Self::CloseAngleBracket => write!(f, ">"),
|
||||||
Self::Dot => write!(f, "."),
|
Self::Dot => write!(f, "."),
|
||||||
|
Self::Exclamation => write!(f, "!"),
|
||||||
|
Self::Interrogation => write!(f, "?"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,12 +114,15 @@ where
|
||||||
}
|
}
|
||||||
ReaderStateMachine::Invalid { .. } => {
|
ReaderStateMachine::Invalid { .. } => {
|
||||||
let invalid_char = bigger_buf.pop().unwrap(); // invalid single char
|
let invalid_char = bigger_buf.pop().unwrap(); // invalid single char
|
||||||
// clear everything, to avoid further errors
|
// clear everything, to avoid further errors
|
||||||
bigger_buf.clear();
|
bigger_buf.clear();
|
||||||
buf.clear();
|
buf.clear();
|
||||||
return match invalid_char {
|
return match invalid_char {
|
||||||
0 => Err(self.error(format!("EOF"))),
|
0 => Err(self.error(format!("EOF"))),
|
||||||
_ => Err(self.error(format!("character {:?} ({})", invalid_char as char, invalid_char)))
|
_ => Err(self.error(format!(
|
||||||
|
"character {:?} ({})",
|
||||||
|
invalid_char as char, invalid_char
|
||||||
|
))),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -136,14 +136,24 @@ fn execute_emptyfilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_fieldfilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
fn execute_fieldfilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||||
execute_single_line("song(`lov`).(year >= 2020)", false, true)
|
execute_single_line(
|
||||||
|
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(year >= 2000)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_files_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
fn execute_files_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||||
execute_single_line(
|
execute_single_line(
|
||||||
r"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`, re=``, recursive=false)",
|
r"files(folder=`~/Music/MusicFlac/Bruno Mars/24K Magic/`, re=``, recursive=false)",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)
|
)?;
|
||||||
|
execute_single_line(
|
||||||
|
r"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`)",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
execute_single_line(r"files()", false, true)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue