Implement reset functionality on existing statements

This commit is contained in:
NGnius (Graham) 2022-01-11 10:22:05 -05:00
parent 90a310bbbe
commit a8eb390ab8
15 changed files with 190 additions and 38 deletions

View file

@ -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,12 +119,12 @@ 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();

View file

@ -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,

View file

@ -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 {

View file

@ -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;

View file

@ -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;

View file

@ -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])))
} }

View file

@ -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 =

View file

@ -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,
}; };
if real_op.is_resetable() {
// give context to inner (should only occur on first run) // give context to inner (should only occur on first run)
if self.context.is_some() { if self.context.is_some() {
let ctx = self.context.take().unwrap(); let ctx = self.context.take().unwrap();
real_op.enter(ctx); real_op.enter(ctx);
} }
if real_op.is_resetable() {
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),
}) })
} }
} }

View file

@ -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)

View file

@ -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)

View file

@ -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(
root_path
.read_dir()
.map_err(|e| RuntimeError {
line: 0, line: 0,
op: op(), op: op(),
msg: format!("Directory read error: {}", e), msg: format!("Directory read error: {}", e),
})?.into()); })?
.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();
} }

View file

@ -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)
} }

View file

@ -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, "?"),
} }
} }
} }

View file

@ -119,7 +119,10 @@ where
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
))),
}; };
} }
_ => {} _ => {}

View file

@ -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)
} }