Add empties(count) functionality and help docs
This commit is contained in:
parent
8b7046d257
commit
da0596cc10
8 changed files with 188 additions and 1 deletions
|
@ -154,6 +154,10 @@ Combine multiple iterables such that only items that exist in iterable1 and iter
|
|||
|
||||
Empty iterator. Useful for deleting items using replacement filters.
|
||||
|
||||
#### empties(count);
|
||||
|
||||
Iterate over count empty items. The items in this iterator have no fields (i.e. are empty).
|
||||
|
||||
### Sorters
|
||||
Operations to sort the items in an iterable: `iterable~(sorter)` OR `iterable.sort(sorter)`.
|
||||
|
||||
|
@ -231,5 +235,9 @@ Various algebraic operations: brackets (order of operations), negation, subtract
|
|||
|
||||
Format a value into a string. This behaves differently depending on the value's type: When the value is an Item, the item's corresponding field will replace all `{field}` instances in the format string. When the value is a primitive type (String, Int, Bool, etc.), the value's text equivalent will replace all `{}` instances in the format string. When the value is an iterable operation (Op), the operation's script equivalent will replace all `{}` instances in the format string.
|
||||
|
||||
#### file(filepath) -- e.g. `file("~/Music/Romantic Traffic.flac"),`
|
||||
|
||||
Load a item from file, populating the item with the song's tags.
|
||||
|
||||
|
||||
License: LGPL-2.1-only OR GPL-3.0-only
|
||||
|
|
|
@ -200,6 +200,7 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) {
|
|||
.add(crate::lang::vocabulary::sql_init_function_factory())
|
||||
.add(crate::lang::vocabulary::files_function_factory())
|
||||
.add(crate::lang::vocabulary::empty_function_factory())
|
||||
.add(crate::lang::vocabulary::empties_function_factory())
|
||||
.add(crate::lang::vocabulary::reset_function_factory())
|
||||
.add(crate::lang::vocabulary::union_function_factory())
|
||||
.add(crate::lang::vocabulary::intersection_function_factory());
|
||||
|
|
|
@ -18,6 +18,7 @@ impl Lookup {
|
|||
pub fn check_is(token: &MpsToken) -> bool {
|
||||
token.is_name() || check_is_type(token)
|
||||
}
|
||||
|
||||
pub fn parse(tokens: &mut VecDeque<MpsToken>) -> Result<Self, SyntaxError> {
|
||||
if tokens.is_empty() {
|
||||
Err(SyntaxError {
|
||||
|
|
152
mps-interpreter/src/lang/vocabulary/empties.rs
Normal file
152
mps-interpreter/src/lang/vocabulary/empties.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::Iterator;
|
||||
|
||||
use crate::tokens::MpsToken;
|
||||
use crate::MpsContext;
|
||||
|
||||
use crate::lang::{Lookup, MpsLanguageDictionary, PseudoOp};
|
||||
use crate::lang::{MpsFunctionFactory, MpsFunctionStatementFactory, MpsIteratorItem, MpsOp};
|
||||
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
|
||||
use crate::processing::general::MpsType;
|
||||
use crate::MpsItem;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EmptiesStatement {
|
||||
count: Lookup,
|
||||
context: Option<MpsContext>,
|
||||
// state
|
||||
current_i: u64,
|
||||
is_errored: bool,
|
||||
}
|
||||
|
||||
impl Display for EmptiesStatement {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
write!(f, "empties({})", self.count)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::clone::Clone for EmptiesStatement {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
count: self.count.clone(),
|
||||
context: None,
|
||||
current_i: self.current_i,
|
||||
is_errored: self.is_errored,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for EmptiesStatement {
|
||||
type Item = MpsIteratorItem;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let val = self.count.get(self.context.as_ref().unwrap());
|
||||
match val {
|
||||
Ok(val) => {
|
||||
if let MpsType::Primitive(val) = val {
|
||||
if let Some(val) = val.clone().to_u64() {
|
||||
if self.current_i < val {
|
||||
self.current_i += 1;
|
||||
Some(Ok(MpsItem::new()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
self.is_errored = true;
|
||||
Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!(
|
||||
"Cannot use primitive {} ({}) as count (should be UInt)",
|
||||
self.count, val
|
||||
),
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
self.is_errored = true;
|
||||
Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!(
|
||||
"Cannot use non-primitive {} ({}) as count (should be UInt)",
|
||||
self.count, val
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if self.is_errored {
|
||||
None
|
||||
} else {
|
||||
self.is_errored = true;
|
||||
Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl MpsOp for EmptiesStatement {
|
||||
fn enter(&mut self, ctx: MpsContext) {
|
||||
self.context = Some(ctx)
|
||||
}
|
||||
|
||||
fn escape(&mut self) -> MpsContext {
|
||||
self.context.take().unwrap()
|
||||
}
|
||||
|
||||
fn is_resetable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||
self.current_i = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dup(&self) -> Box<dyn MpsOp> {
|
||||
Box::new(Self {
|
||||
count: self.count.clone(),
|
||||
context: None,
|
||||
current_i: 0,
|
||||
is_errored: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptiesFunctionFactory;
|
||||
|
||||
impl MpsFunctionFactory<EmptiesStatement> for EmptiesFunctionFactory {
|
||||
fn is_function(&self, name: &str) -> bool {
|
||||
name == "empties"
|
||||
}
|
||||
|
||||
fn build_function_params(
|
||||
&self,
|
||||
_name: String,
|
||||
tokens: &mut VecDeque<MpsToken>,
|
||||
_dict: &MpsLanguageDictionary,
|
||||
) -> Result<EmptiesStatement, SyntaxError> {
|
||||
// empties(count)
|
||||
let count_lookup = Lookup::parse(tokens)?;
|
||||
Ok(EmptiesStatement {
|
||||
count: count_lookup,
|
||||
context: None,
|
||||
current_i: 0,
|
||||
is_errored: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type EmptiesStatementFactory =
|
||||
MpsFunctionStatementFactory<EmptiesStatement, EmptiesFunctionFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn empties_function_factory() -> EmptiesStatementFactory {
|
||||
EmptiesStatementFactory::new(EmptiesFunctionFactory)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod comment;
|
||||
mod empties;
|
||||
mod empty;
|
||||
mod files;
|
||||
mod intersection;
|
||||
|
@ -11,6 +12,7 @@ mod union;
|
|||
mod variable_assign;
|
||||
|
||||
pub use comment::{CommentStatement, CommentStatementFactory};
|
||||
pub use empties::{empties_function_factory, EmptiesStatementFactory};
|
||||
pub use empty::{empty_function_factory, EmptyStatementFactory};
|
||||
pub use files::{files_function_factory, FilesStatementFactory};
|
||||
pub use intersection::{intersection_function_factory, IntersectionStatementFactory};
|
||||
|
|
|
@ -152,6 +152,10 @@
|
|||
//!
|
||||
//! Empty iterator. Useful for deleting items using replacement filters.
|
||||
//!
|
||||
//! ### empties(count);
|
||||
//!
|
||||
//! Iterate over count empty items. The items in this iterator have no fields (i.e. are empty).
|
||||
//!
|
||||
//! ## Sorters
|
||||
//! Operations to sort the items in an iterable: `iterable~(sorter)` OR `iterable.sort(sorter)`.
|
||||
//!
|
||||
|
|
|
@ -754,9 +754,25 @@ fn execute_uniquefilter_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
|||
fn execute_fileitemop_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||
execute_single_line(
|
||||
"files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).{
|
||||
item.title = `something else`,
|
||||
item = file(item.filename),
|
||||
}",
|
||||
false,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_emptiesop_line() -> Result<(), Box<dyn MpsLanguageError>> {
|
||||
execute_single_line(
|
||||
"empties(1).{let count = 0, item.title = ~`title #{}` count+1, item.filename = ~`filename_{}` count, count = count + 1}",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line(
|
||||
"empties(42).{let count = 0, item.title = ~`title #{}` count+1, item.filename = ~`filename_{}` count, count = count + 1}",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line("empties(0)", true, true)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,10 @@ These always return an iterable which can be manipulated.
|
|||
Combine multiple iterables such that only items that exist in iterable1 and iterable2 and ... are returned. The order of items from iterable1 is maintained. There is no limit on the amount of iterables which can be provided as parameters.
|
||||
|
||||
empty()
|
||||
Empty iterator. Useful for deleting items using replacement filters.";
|
||||
Empty iterator. Useful for deleting items using replacement filters.
|
||||
|
||||
empties(count);
|
||||
Iterate over count empty items. The items in this iterator have no fields (i.e. are empty).";
|
||||
|
||||
pub const FILTERS: &str =
|
||||
"FILTERS (?filters)
|
||||
|
|
Loading…
Reference in a new issue