diff --git a/mps-interpreter/src/lang/filter.rs b/mps-interpreter/src/lang/filter.rs index 86ee13c..cdbf419 100644 --- a/mps-interpreter/src/lang/filter.rs +++ b/mps-interpreter/src/lang/filter.rs @@ -20,6 +20,10 @@ pub trait MpsFilterPredicate: Clone + Debug + Display { ctx: &mut MpsContext, op: &mut OpGetter, ) -> Result; + + fn is_complete(&self) -> bool; + + fn reset(&mut self) -> Result<(), RuntimeError>; } pub trait MpsFilterFactory { @@ -88,7 +92,7 @@ impl MpsOp for MpsFilterStatement

{ } else { false } - }, + } VariableOrOp::Op(PseudoOp::Real(op)) => op.is_resetable(), VariableOrOp::Op(PseudoOp::Fake(_)) => false, } @@ -96,10 +100,17 @@ impl MpsOp for MpsFilterStatement

{ fn reset(&mut self) -> Result<(), RuntimeError> { let fake = PseudoOp::Fake(format!("{}", self)); + self.predicate.reset()?; match &mut self.iterable { VariableOrOp::Variable(s) => { 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() } else { Err(RuntimeError { @@ -108,13 +119,13 @@ impl MpsOp for MpsFilterStatement

{ msg: "Cannot reset non-iterable filter variable".to_string(), }) } - }, + } VariableOrOp::Op(PseudoOp::Real(op)) => op.reset(), VariableOrOp::Op(PseudoOp::Fake(_)) => Err(RuntimeError { - line: 0, - op: fake, - msg: "Cannot reset fake filter".to_string(), - }), + line: 0, + op: fake, + msg: "Cannot reset PseudoOp::Fake filter".to_string(), + }), } } } @@ -123,6 +134,9 @@ impl Iterator for MpsFilterStatement

{ type Item = Result; fn next(&mut self) -> Option { + if self.predicate.is_complete() { + return None; + } let self_clone = self.clone(); let self_clone2 = self_clone.clone(); let mut op_getter = move || (Box::new(self_clone.clone()) as Box).into(); diff --git a/mps-interpreter/src/lang/operation.rs b/mps-interpreter/src/lang/operation.rs index cf02517..b438492 100644 --- a/mps-interpreter/src/lang/operation.rs +++ b/mps-interpreter/src/lang/operation.rs @@ -3,8 +3,8 @@ use std::fmt::{Debug, Display}; use std::iter::Iterator; use super::MpsLanguageDictionary; -use super::{RuntimeError, SyntaxError}; use super::PseudoOp; +use super::{RuntimeError, SyntaxError}; use crate::tokens::MpsToken; use crate::MpsContext; use crate::MpsMusicItem; @@ -63,12 +63,17 @@ pub trait MpsOp: Iterator> + Debug + D fn escape(&mut self) -> MpsContext; - fn is_resetable(&self) -> bool {false} + fn is_resetable(&self) -> bool { + false + } fn reset(&mut self) -> Result<(), RuntimeError> { #[cfg(debug_assertions)] 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 { line: 0, diff --git a/mps-interpreter/src/lang/pseudo_op.rs b/mps-interpreter/src/lang/pseudo_op.rs index e17e191..bb0229c 100644 --- a/mps-interpreter/src/lang/pseudo_op.rs +++ b/mps-interpreter/src/lang/pseudo_op.rs @@ -51,6 +51,10 @@ impl PseudoOp { _ => false, } } + + pub fn from_printable(item: &D) -> Self { + Self::Fake(format!("{}", item)) + } } impl Clone for PseudoOp { diff --git a/mps-interpreter/src/lang/vocabulary/files.rs b/mps-interpreter/src/lang/vocabulary/files.rs index 9605c54..5e012cf 100644 --- a/mps-interpreter/src/lang/vocabulary/files.rs +++ b/mps-interpreter/src/lang/vocabulary/files.rs @@ -106,6 +106,16 @@ impl MpsOp for FilesStatement { fn escape(&mut self) -> MpsContext { 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; diff --git a/mps-interpreter/src/lang/vocabulary/filters/empty_filter.rs b/mps-interpreter/src/lang/vocabulary/filters/empty_filter.rs index 5d22904..f859a36 100644 --- a/mps-interpreter/src/lang/vocabulary/filters/empty_filter.rs +++ b/mps-interpreter/src/lang/vocabulary/filters/empty_filter.rs @@ -27,6 +27,14 @@ impl MpsFilterPredicate for EmptyFilter { ) -> Result { Ok(true) } + + fn is_complete(&self) -> bool { + false + } + + fn reset(&mut self) -> Result<(), RuntimeError> { + Ok(()) + } } pub struct EmptyFilterFactory; diff --git a/mps-interpreter/src/lang/vocabulary/filters/field_filter.rs b/mps-interpreter/src/lang/vocabulary/filters/field_filter.rs index c5a19c6..8da2351 100644 --- a/mps-interpreter/src/lang/vocabulary/filters/field_filter.rs +++ b/mps-interpreter/src/lang/vocabulary/filters/field_filter.rs @@ -77,6 +77,14 @@ impl MpsFilterPredicate for FieldFilter { }) } } + + fn is_complete(&self) -> bool { + false + } + + fn reset(&mut self) -> Result<(), RuntimeError> { + Ok(()) + } } pub struct FieldFilterFactory; @@ -88,9 +96,9 @@ impl MpsFilterFactory for FieldFilterFactory { && tokens[0].is_name() && (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket()) && (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[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[3].is_name() || check_is_type(&tokens[3]))) } diff --git a/mps-interpreter/src/lang/vocabulary/filters/utility.rs b/mps-interpreter/src/lang/vocabulary/filters/utility.rs index 9a158b2..4060843 100644 --- a/mps-interpreter/src/lang/vocabulary/filters/utility.rs +++ b/mps-interpreter/src/lang/vocabulary/filters/utility.rs @@ -74,6 +74,10 @@ pub fn assert_comparison_operator(tokens: &mut VecDeque) -> Result<[i8 Ok([1, 1]) } } + MpsToken::Exclamation => { + assert_token_raw(MpsToken::Equals, tokens)?; + Ok([-1, 1]) + } _ => Err(SyntaxError { line: 0, token: MpsToken::Equals, // TODO this can be < > or = diff --git a/mps-interpreter/src/lang/vocabulary/repeat.rs b/mps-interpreter/src/lang/vocabulary/repeat.rs index ec4ec52..444880f 100644 --- a/mps-interpreter/src/lang/vocabulary/repeat.rs +++ b/mps-interpreter/src/lang/vocabulary/repeat.rs @@ -20,6 +20,7 @@ pub struct RepeatStatement { cache_position: usize, repetitions: usize, loop_forever: bool, + original_repetitions: usize, } impl Display for RepeatStatement { @@ -38,6 +39,7 @@ impl std::clone::Clone for RepeatStatement { cache_position: self.cache_position, repetitions: self.repetitions, loop_forever: self.loop_forever, + original_repetitions: self.original_repetitions, } } } @@ -50,12 +52,12 @@ impl Iterator for RepeatStatement { Err(e) => return Some(Err(e)), 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() { - // 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 let Some(item) = real_op.next() { return Some(item); @@ -69,14 +71,14 @@ impl Iterator for RepeatStatement { self.repetitions -= 1; match real_op.reset() { Err(e) => return Some(Err(e)), - Ok(_) => {}, + Ok(_) => {} } } } else { // always reset in infinite loop mode match real_op.reset() { Err(e) => return Some(Err(e)), - Ok(_) => {}, + Ok(_) => {} } } } @@ -151,6 +153,42 @@ impl MpsOp for RepeatStatement { 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; @@ -177,14 +215,17 @@ impl MpsFunctionFactory for RepeatFunctionFactory { assert_token_raw(MpsToken::Comma, tokens)?; count = Some(assert_token( |t| match t { - MpsToken::Name(n) => n.parse::().map( - |d| if d == 0 { + MpsToken::Name(n) => n + .parse::() + .map(|d| { + if d == 0 { inner_done = true; 0 } else { - d-1 + d - 1 } - ).ok(), + }) + .ok(), _ => None, }, MpsToken::Name("usize".into()), @@ -199,6 +240,7 @@ impl MpsFunctionFactory for RepeatFunctionFactory { cache_position: 0, repetitions: count.unwrap_or(0), loop_forever: count.is_none(), + original_repetitions: count.and_then(|c| Some(c + 1)).unwrap_or(0), }) } } diff --git a/mps-interpreter/src/lang/vocabulary/sql_query.rs b/mps-interpreter/src/lang/vocabulary/sql_query.rs index c1d9934..93d63ac 100644 --- a/mps-interpreter/src/lang/vocabulary/sql_query.rs +++ b/mps-interpreter/src/lang/vocabulary/sql_query.rs @@ -58,6 +58,16 @@ impl MpsOp for SqlStatement { fn escape(&mut self) -> MpsContext { 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 { @@ -88,7 +98,7 @@ impl Iterator for SqlStatement { Err(e) => { self.rows = Some(Vec::with_capacity(0)); return Some(Err(e)); - }, + } Ok(rows) => { self.rows = Some(rows); self.get_item(false) diff --git a/mps-interpreter/src/lang/vocabulary/sql_simple_query.rs b/mps-interpreter/src/lang/vocabulary/sql_simple_query.rs index c6b554d..796a350 100644 --- a/mps-interpreter/src/lang/vocabulary/sql_simple_query.rs +++ b/mps-interpreter/src/lang/vocabulary/sql_simple_query.rs @@ -107,6 +107,16 @@ impl MpsOp for SimpleSqlStatement { fn escape(&mut self) -> MpsContext { 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 { @@ -150,7 +160,7 @@ impl Iterator for SimpleSqlStatement { Err(e) => { self.rows = Some(Vec::with_capacity(0)); return Some(Err(e)); - }, + } Ok(rows) => { self.rows = Some(rows); self.get_item(false) diff --git a/mps-interpreter/src/processing/filesystem.rs b/mps-interpreter/src/processing/filesystem.rs index c561011..b633645 100644 --- a/mps-interpreter/src/processing/filesystem.rs +++ b/mps-interpreter/src/processing/filesystem.rs @@ -1,5 +1,5 @@ use std::fmt::{Debug, Display, Error, Formatter}; -use std::fs::{ReadDir, DirEntry}; +use std::fs::{DirEntry, ReadDir}; use std::iter::Iterator; use std::path::{Path, PathBuf}; @@ -86,11 +86,16 @@ impl FileIter { }; let dir_vec = if root_path.is_dir() { let mut vec = Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE); - vec.push(root_path.read_dir().map_err(|e| RuntimeError { - line: 0, - op: op(), - msg: format!("Directory read error: {}", e), - })?.into()); + vec.push( + root_path + .read_dir() + .map_err(|e| RuntimeError { + line: 0, + op: op(), + msg: format!("Directory read error: {}", e), + })? + .into(), + ); vec } else { Vec::with_capacity(DEFAULT_VEC_CACHE_SIZE) @@ -257,7 +262,7 @@ impl Iterator for FileIter { Err(e) => { self.is_complete = true; return Some(Err(format!("Directory read error: {}", e))); - }, + } }); return self.next(); } diff --git a/mps-interpreter/src/processing/variables.rs b/mps-interpreter/src/processing/variables.rs index c4856b9..c02321f 100644 --- a/mps-interpreter/src/processing/variables.rs +++ b/mps-interpreter/src/processing/variables.rs @@ -69,7 +69,6 @@ pub struct MpsOpStorage { } impl MpsVariableStorer for MpsOpStorage { - fn get_opt(&self, key: &str) -> Option<&MpsType> { self.storage.get(key) } diff --git a/mps-interpreter/src/tokens/token_enum.rs b/mps-interpreter/src/tokens/token_enum.rs index 282fbd7..27497fd 100644 --- a/mps-interpreter/src/tokens/token_enum.rs +++ b/mps-interpreter/src/tokens/token_enum.rs @@ -15,6 +15,8 @@ pub enum MpsToken { OpenAngleBracket, CloseAngleBracket, Dot, + Exclamation, + Interrogation, } impl MpsToken { @@ -30,6 +32,8 @@ impl MpsToken { "<" => Ok(Self::OpenAngleBracket), ">" => Ok(Self::CloseAngleBracket), "." => Ok(Self::Dot), + "!" => Ok(Self::Exclamation), + "?" => Ok(Self::Interrogation), _ => { // name validation let mut ok = true; @@ -138,6 +142,20 @@ impl MpsToken { _ => 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 { @@ -156,6 +174,8 @@ impl Display for MpsToken { Self::OpenAngleBracket => write!(f, "<"), Self::CloseAngleBracket => write!(f, ">"), Self::Dot => write!(f, "."), + Self::Exclamation => write!(f, "!"), + Self::Interrogation => write!(f, "?"), } } } diff --git a/mps-interpreter/src/tokens/tokenizer.rs b/mps-interpreter/src/tokens/tokenizer.rs index 0219cb3..5c3676b 100644 --- a/mps-interpreter/src/tokens/tokenizer.rs +++ b/mps-interpreter/src/tokens/tokenizer.rs @@ -114,12 +114,15 @@ where } ReaderStateMachine::Invalid { .. } => { 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(); buf.clear(); return match invalid_char { 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 + ))), }; } _ => {} diff --git a/mps-interpreter/tests/single_line.rs b/mps-interpreter/tests/single_line.rs index a71be0c..40bd729 100644 --- a/mps-interpreter/tests/single_line.rs +++ b/mps-interpreter/tests/single_line.rs @@ -136,14 +136,24 @@ fn execute_emptyfilter_line() -> Result<(), Box> { #[test] fn execute_fieldfilter_line() -> Result<(), Box> { - execute_single_line("song(`lov`).(year >= 2020)", false, true) + execute_single_line( + "files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(year >= 2000)", + false, + true, + ) } #[test] fn execute_files_line() -> Result<(), Box> { 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, true, - ) + )?; + execute_single_line( + r"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`)", + false, + true, + )?; + execute_single_line(r"files()", false, true) }