Add empties(count) functionality and help docs

This commit is contained in:
NGnius (Graham) 2022-03-01 20:44:46 -05:00
parent 8b7046d257
commit da0596cc10
8 changed files with 188 additions and 1 deletions

View file

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

View file

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

View file

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

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

View file

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

View file

@ -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)`.
//!

View file

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

View file

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