Skip to content
This repository has been archived by the owner on Jul 14, 2023. It is now read-only.

Commit

Permalink
Document iter block functionality and cargo fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
NGnius committed Feb 23, 2022
1 parent a915cbd commit bb492dc
Show file tree
Hide file tree
Showing 41 changed files with 762 additions and 354 deletions.
59 changes: 58 additions & 1 deletion mps-interpreter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,62 @@ Sort by the distance (similarity) from the first song in the iterator. Songs whi

Sort by the distance (similarity) between the last played song in the iterator. Similar to bliss_first. The song which is the most similar (lower distance) to the previous song in the iterator will be placed next to it, then the process is repeated. This uses the [bliss music analyser](https://github.com/polochon-street/bliss-rs), which is a very slow operation and can cause music playback interruptions for large iterators. This requires the `advanced` feature to be enabled (without the feature enabled this is still valid syntax but doesn't change the order).

### Procedures
Operations to apply to each item in an iterable: iterable.{step1, step2, ...}

License: LGPL-2.1-only OR GPL-2.0-or-later
Comma-separated procedure steps will be executed sequentially (like a for loop in regular programming languages). The variable item contains the current item of the iterable.

#### let variable = something -- e.g. let my_var = 42

Declare the variable and (optionally) set the initial value to something. The assignment will only be performed when the variable has not yet been declared. When the initial value (and equals sign) is omitted, the variable is initialized as empty().

#### variable = something -- e.g. my_var = 42

Assign something to the variable. The variable must have already been declared.

#### empty() -- e.g. empty()

The empty or null constant.

#### if condition { something } else { something_else } -- e.g.
```mps
if item.title == `Romantic Traffic` {
} else {
remove item
}
```

Branch based on a boolean condition. Multiple comma-separated procedure steps may be supplied in the if and else branches. This does not currently support if else chains, but they can be nested to accomplish similar behaviour.

#### something1 == something2
#### something1 != something2
#### something1 >= something2
#### something1 > something2
#### something1 <= something2
#### something1 < something2 -- e.g. item.filename != item.title

Compare something1 to something2. The result is a boolean which is useful for branch conditions.

#### op iterable_operation -- e.g. op files().(0..=42)~(shuffle)

An iterable operation inside of the procedure. When assigned to item, this can be used to replace item with multiple others. Note that iterable operations are never executed inside the procedure; when item is iterable, it will be executed immediately after the end of the procedure for the current item.

#### (something1)
#### -something1
#### something1 - something2
#### something1 + something2
#### something1 || something2
#### something1 && something2 -- e.g. 42 + (128 - 64)

Various algebraic operations: brackets (order of operations), negation, subtraction, addition, logical OR, logical AND; respectively.

#### Item(field1 = something1, field2 = something2, ...) - e.g. item = Item(title = item.title, filename = `/dev/null`)

Constructor for a new item. Each function parameter defines a new field and it's value.
#### ~`string_format` something -- e.g. ~`{filename}` item

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.


License: LGPL-2.1-only OR GPL-3.0-only
6 changes: 2 additions & 4 deletions mps-interpreter/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#[cfg(feature = "advanced")]
use super::processing::advanced::{MpsDefaultAnalyzer, MpsMusicAnalyzer};
use super::processing::database::{MpsDatabaseQuerier, MpsSQLiteExecutor};
use super::processing::general::{
MpsFilesystemExecutor, MpsFilesystemQuerier, MpsOpStorage, MpsVariableStorer,
};
#[cfg(feature = "advanced")]
use super::processing::advanced::{
MpsMusicAnalyzer, MpsDefaultAnalyzer
};
use std::fmt::{Debug, Display, Error, Formatter};

#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions mps-interpreter/src/interpretor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) {
.add(crate::lang::vocabulary::item_ops::SubtractItemOpFactory)
.add(crate::lang::vocabulary::item_ops::OrItemOpFactory)
.add(crate::lang::vocabulary::item_ops::AndItemOpFactory)
.add(crate::lang::vocabulary::item_ops::BracketsItemOpFactory)
)
.add(crate::lang::vocabulary::item_ops::BracketsItemOpFactory),
)
// functions and misc
// functions don't enforce bracket coherence
// -- function().() is valid despite the ).( in between brackets
Expand Down
2 changes: 1 addition & 1 deletion mps-interpreter/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl MpsItem {
self.fields.remove(name)
}

pub fn iter(&self) -> impl Iterator<Item=&String> {
pub fn iter(&self) -> impl Iterator<Item = &String> {
self.fields.keys()
}

Expand Down
23 changes: 12 additions & 11 deletions mps-interpreter/src/lang/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,13 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterStatement<P> {
predicate: self.predicate.clone(),
iterable: match &self.iterable {
VariableOrOp::Variable(s) => VariableOrOp::Variable(s.clone()),
VariableOrOp::Op(op) => VariableOrOp::Op(op.try_real_ref().unwrap().dup().into())
VariableOrOp::Op(op) => VariableOrOp::Op(op.try_real_ref().unwrap().dup().into()),
},
context: None,
other_filters: self.other_filters.as_ref().map(|x| PseudoOp::from(x.try_real_ref().unwrap().dup())),
other_filters: self
.other_filters
.as_ref()
.map(|x| PseudoOp::from(x.try_real_ref().unwrap().dup())),
})
}
}
Expand Down Expand Up @@ -350,15 +353,14 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
// handle other filters
// make fake inner item
let single_op = SingleItem::new_ok(item.clone());
match ctx.variables.declare(
INNER_VARIABLE_NAME,
MpsType::Op(Box::new(single_op)),
) {
match ctx
.variables
.declare(INNER_VARIABLE_NAME, MpsType::Op(Box::new(single_op)))
{
Ok(x) => x,
Err(e) => {
//self.context = Some(op.escape());
maybe_result =
Some(Err(e.with(RuntimeOp(fake.clone()))));
maybe_result = Some(Err(e.with(RuntimeOp(fake.clone()))));
self.context = Some(ctx);
break;
}
Expand All @@ -381,9 +383,8 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
Ok(_) => {}
Err(e) => match maybe_result {
Some(Ok(_)) => {
maybe_result = Some(Err(
e.with(RuntimeOp(fake.clone()))
))
maybe_result =
Some(Err(e.with(RuntimeOp(fake.clone()))))
}
Some(Err(e2)) => maybe_result = Some(Err(e2)), // already failing, do not replace error,
None => {} // impossible
Expand Down
7 changes: 5 additions & 2 deletions mps-interpreter/src/lang/filter_replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,14 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterReplaceStatement<P> {
predicate: self.predicate.clone(),
iterable: match &self.iterable {
VariableOrOp::Variable(s) => VariableOrOp::Variable(s.clone()),
VariableOrOp::Op(op) => VariableOrOp::Op(op.try_real_ref().unwrap().dup().into())
VariableOrOp::Op(op) => VariableOrOp::Op(op.try_real_ref().unwrap().dup().into()),
},
context: None,
op_if: PseudoOp::from(self.op_if.try_real_ref().unwrap().dup()),
op_else: self.op_else.as_ref().map(|x| PseudoOp::from(x.try_real_ref().unwrap().dup())),
op_else: self
.op_else
.as_ref()
.map(|x| PseudoOp::from(x.try_real_ref().unwrap().dup())),
item_cache: VecDeque::new(),
})
}
Expand Down
61 changes: 37 additions & 24 deletions mps-interpreter/src/lang/iter_block.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use core::ops::Deref;
use std::sync::Arc;
use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use std::iter::Iterator;
use std::marker::PhantomData;
use std::sync::Arc;

use crate::lang::utility::{assert_token_raw, assert_token_raw_back};
use crate::lang::MpsLanguageDictionary;
Expand Down Expand Up @@ -31,13 +31,16 @@ pub trait MpsItemOpFactory<T: Deref<Target = dyn MpsItemOp> + 'static> {
) -> Result<T, SyntaxError>;
}

pub struct MpsItemOpBoxer<X: MpsItemOpFactory<Y> + 'static, Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static> {
pub struct MpsItemOpBoxer<
X: MpsItemOpFactory<Y> + 'static,
Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static,
> {
idc: PhantomData<Y>,
factory: X,
}

impl<X: MpsItemOpFactory<Y> + 'static, Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static> MpsItemOpFactory<Box<dyn MpsItemOp>>
for MpsItemOpBoxer<X, Y>
impl<X: MpsItemOpFactory<Y> + 'static, Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static>
MpsItemOpFactory<Box<dyn MpsItemOp>> for MpsItemOpBoxer<X, Y>
{
fn is_item_op(&self, tokens: &VecDeque<MpsToken>) -> bool {
self.factory.is_item_op(tokens)
Expand Down Expand Up @@ -84,7 +87,6 @@ impl std::clone::Clone for MpsItemBlockStatement {
}
}


impl MpsOp for MpsItemBlockStatement {
fn enter(&mut self, ctx: MpsContext) {
self.iterable.try_real().unwrap().enter(ctx)
Expand Down Expand Up @@ -151,10 +153,12 @@ impl Iterator for MpsItemBlockStatement {
let old_var = replace_item_var(&mut ctx, MpsType::Item(item));
for op in self.statements.iter_mut() {
match op.execute(&mut ctx) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
#[allow(unused_must_use)]
{restore_item_var(&mut ctx, old_var);}
{
restore_item_var(&mut ctx, old_var);
}
real_op.enter(ctx);
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
}
Expand All @@ -169,9 +173,7 @@ impl Iterator for MpsItemBlockStatement {
};
real_op.enter(ctx);
match item {
Some(MpsType::Item(item)) => {
return Some(Ok(item))
},
Some(MpsType::Item(item)) => return Some(Ok(item)),
Some(MpsType::Op(mut op)) => {
op.enter(real_op.escape());
if let Some(item) = op.next() {
Expand All @@ -181,12 +183,17 @@ impl Iterator for MpsItemBlockStatement {
} else {
real_op.enter(op.escape());
}
},
Some(x) => return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Expected `item` like MpsType::Item(MpsItem[...]), got {}", x),
})),
}
Some(x) => {
return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!(
"Expected `item` like MpsType::Item(MpsItem[...]), got {}",
x
),
}))
}
None => {}
}
}
Expand All @@ -206,9 +213,12 @@ pub struct MpsItemBlockFactory {
}

impl MpsItemBlockFactory {
pub fn add<T: MpsItemOpFactory<Y> + 'static, Y: Deref<Target=dyn MpsItemOp> + MpsItemOp + 'static>(
pub fn add<
T: MpsItemOpFactory<Y> + 'static,
Y: Deref<Target = dyn MpsItemOp> + MpsItemOp + 'static,
>(
mut self,
factory: T
factory: T,
) -> Self {
self.vocabulary.push(Box::new(MpsItemOpBoxer {
factory: factory,
Expand Down Expand Up @@ -250,7 +260,7 @@ impl MpsItemBlockFactory {

impl BoxedMpsOpFactory for MpsItemBlockFactory {
fn is_op_boxed(&self, tokens: &VecDeque<MpsToken>) -> bool {
tokens[tokens.len()-1].is_close_curly()
tokens[tokens.len() - 1].is_close_curly()
}

fn build_op_boxed(
Expand Down Expand Up @@ -303,7 +313,10 @@ fn replace_item_var(ctx: &mut MpsContext, item: MpsType) -> Option<MpsType> {
old_var
}

fn restore_item_var(ctx: &mut MpsContext, old_var: Option<MpsType>) -> Result<Option<MpsType>, RuntimeMsg> {
fn restore_item_var(
ctx: &mut MpsContext,
old_var: Option<MpsType>,
) -> Result<Option<MpsType>, RuntimeMsg> {
let new_var;
if ctx.variables.exists(ITEM_VARIABLE_NAME) {
new_var = Some(ctx.variables.remove(ITEM_VARIABLE_NAME)?);
Expand All @@ -326,16 +339,16 @@ fn find_last_open_curly(tokens: &VecDeque<MpsToken>) -> Option<usize> {
if bracket_depth != 0 {
bracket_depth -= 1;
}
},
}
MpsToken::CloseCurly => {
bracket_depth += 1;
},
}
MpsToken::Dot => {
if bracket_depth == 0 && curly_found {
return Some(i+1);
return Some(i + 1);
}
}
_ => {},
_ => {}
}
if token.is_open_curly() {
curly_found = true;
Expand Down
39 changes: 30 additions & 9 deletions mps-interpreter/src/lang/type_primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,46 @@ impl MpsTypePrimitive {
match self {
Self::String(s) => match other {
Self::String(other_s) => Ok(Self::String(s.to_owned() + other_s)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
other => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
},
Self::Int(i) => match other {
Self::Int(other_i) => Ok(Self::Int(i + other_i)),
Self::UInt(u) => Ok(Self::Int(i + *u as i64)),
Self::Float(f) => Ok(Self::Float(*i as f64 + f)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
other => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
},
Self::UInt(u) => match other {
Self::UInt(other_u) => Ok(Self::UInt(u + other_u)),
Self::Int(i) => Ok(Self::UInt(u + *i as u64)),
Self::Float(f) => Ok(Self::Float(*u as f64 + f)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
other => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
},
Self::Float(f) => match other {
Self::Float(other_f) => Ok(Self::Float(f + other_f)),
Self::Int(i) => Ok(Self::Float(f + *i as f64)),
Self::UInt(u) => Ok(Self::Float(f + *u as f64)),
other => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
other => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
},
Self::Bool(_) => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
Self::Empty => Err(format!("Cannot add {} and {}: incompatible types", self, other)),
Self::Bool(_) => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
Self::Empty => Err(format!(
"Cannot add {} and {}: incompatible types",
self, other
)),
}
}

Expand Down Expand Up @@ -134,7 +152,10 @@ impl MpsTypePrimitive {
pub fn try_not(&self) -> Result<Self, String> {
match self {
Self::Bool(b) => Ok(Self::Bool(!*b)),
_ => Err(format!("Cannot apply logical NOT to {}: incompatible type", self)),
_ => Err(format!(
"Cannot apply logical NOT to {}: incompatible type",
self
)),
}
}
}
Expand Down Expand Up @@ -206,7 +227,7 @@ impl PartialOrd for MpsTypePrimitive {
Self::Empty => match other {
Self::Empty => Some(std::cmp::Ordering::Equal),
_ => None,
}
},
}
}
}
Expand All @@ -222,7 +243,7 @@ impl std::hash::Hash for MpsTypePrimitive {
Self::UInt(u) => u.hash(state),
Self::Float(f_) => (*f_ as u64).hash(state),
Self::Bool(b) => b.hash(state),
Self::Empty => {},
Self::Empty => {}
}
}
}
Expand Down
Loading

0 comments on commit bb492dc

Please sign in to comment.