Initial API work for reset() support on statements
This commit is contained in:
parent
e93d79f28d
commit
90a310bbbe
6 changed files with 191 additions and 58 deletions
16
README.md
16
README.md
|
@ -5,5 +5,21 @@ This project implements the interpreter (mps-interpreter), music player (mps-pla
|
||||||
The CLI interface includes a REPL for running scripts.
|
The CLI interface includes a REPL for running scripts.
|
||||||
The REPL interactive mode also provides more details about using MPS through the `?help` command.
|
The REPL interactive mode also provides more details about using MPS through the `?help` command.
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
#### Is MPS Turing-Complete?
|
||||||
|
**No**. It can't perform arbitrary calculations (yet), which easily disqualifies MPS from being Turing-complete.
|
||||||
|
|
||||||
|
#### Can I use MPS right now?
|
||||||
|
**Sure!** It's not complete, but MPS is completely useable for basic music queries right now. Hopefully most of the bugs have been ironed out as well...
|
||||||
|
|
||||||
|
#### Why write a new language?
|
||||||
|
**I thought it would be fun**. I also wanted to be able to play my music without having to be at the whim of someone else's algorithm (and music), and playing just by album or artist was getting boring. I also thought designing a language specifically for iteration would be a novel approach to a language (though every approach is a novel approach for me).
|
||||||
|
|
||||||
|
#### What is MPS?
|
||||||
|
**Music Playlist Script (MPS) is technically a query language for music files.** It uses an (auto-generated) SQLite3 database for SQL queries and can also directly query the filesystem. Queries can be filtered and extended by using filters and functions built-in to MPS (see mps-interpreter's README.md).
|
||||||
|
|
||||||
|
#### Is MPS a scripting language?
|
||||||
|
**No**. Technically, it was designed to be one, but it doesn't meet the requirements of a scripting language (yet). One day, I would like it be Turing-complete and then it could be considered a scripting language. At the moment it is barely a query language.
|
||||||
|
|
||||||
|
|
||||||
License: LGPL-2.1-only OR GPL-2.0-or-later
|
License: LGPL-2.1-only OR GPL-2.0-or-later
|
||||||
|
|
|
@ -78,6 +78,45 @@ impl<P: MpsFilterPredicate + 'static> MpsOp for MpsFilterStatement<P> {
|
||||||
fn escape(&mut self) -> MpsContext {
|
fn escape(&mut self) -> MpsContext {
|
||||||
self.context.take().unwrap()
|
self.context.take().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_resetable(&self) -> bool {
|
||||||
|
match &self.iterable {
|
||||||
|
VariableOrOp::Variable(s) => {
|
||||||
|
let var = self.context.as_ref().unwrap().variables.get_opt(s);
|
||||||
|
if let Some(MpsType::Op(var)) = var {
|
||||||
|
var.is_resetable()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
VariableOrOp::Op(PseudoOp::Real(op)) => op.is_resetable(),
|
||||||
|
VariableOrOp::Op(PseudoOp::Fake(_)) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||||
|
let fake = PseudoOp::Fake(format!("{}", self));
|
||||||
|
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)? {
|
||||||
|
var.reset()
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: PseudoOp::Fake(format!("{}", self)),
|
||||||
|
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(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::iter::Iterator;
|
||||||
|
|
||||||
use super::MpsLanguageDictionary;
|
use super::MpsLanguageDictionary;
|
||||||
use super::{RuntimeError, SyntaxError};
|
use super::{RuntimeError, SyntaxError};
|
||||||
|
use super::PseudoOp;
|
||||||
use crate::tokens::MpsToken;
|
use crate::tokens::MpsToken;
|
||||||
use crate::MpsContext;
|
use crate::MpsContext;
|
||||||
use crate::MpsMusicItem;
|
use crate::MpsMusicItem;
|
||||||
|
@ -61,4 +62,18 @@ pub trait MpsOp: Iterator<Item = Result<MpsMusicItem, RuntimeError>> + Debug + D
|
||||||
fn enter(&mut self, ctx: MpsContext);
|
fn enter(&mut self, ctx: MpsContext);
|
||||||
|
|
||||||
fn escape(&mut self) -> MpsContext;
|
fn escape(&mut self) -> MpsContext;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: PseudoOp::Fake(format!("{}", self)),
|
||||||
|
msg: "Op does not support reset()".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,11 +46,47 @@ impl Iterator for RepeatStatement {
|
||||||
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.inner_done {
|
|
||||||
let real_op = match self.inner_statement.try_real() {
|
let real_op = match self.inner_statement.try_real() {
|
||||||
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)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if !self.loop_forever {
|
||||||
|
if self.repetitions == 0 {
|
||||||
|
self.inner_done = true;
|
||||||
|
// take context from inner (should only occur when inner is no longer needed)
|
||||||
|
self.context = Some(real_op.escape());
|
||||||
|
} else {
|
||||||
|
self.repetitions -= 1;
|
||||||
|
match real_op.reset() {
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
Ok(_) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// always reset in infinite loop mode
|
||||||
|
match real_op.reset() {
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
Ok(_) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.context.is_none() {
|
||||||
|
self.context = Some(real_op.escape());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// cache items in RepeatStatement since inner_statement cannot be reset
|
||||||
|
if !self.inner_done {
|
||||||
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);
|
||||||
|
@ -67,6 +103,7 @@ impl Iterator for RepeatStatement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
// inner has completed it's only run
|
||||||
self.inner_done = true;
|
self.inner_done = true;
|
||||||
self.context = Some(real_op.escape());
|
self.context = Some(real_op.escape());
|
||||||
}
|
}
|
||||||
|
@ -100,6 +137,7 @@ impl Iterator for RepeatStatement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MpsOp for RepeatStatement {
|
impl MpsOp for RepeatStatement {
|
||||||
fn enter(&mut self, ctx: MpsContext) {
|
fn enter(&mut self, ctx: MpsContext) {
|
||||||
|
|
|
@ -24,9 +24,31 @@ impl Display for MpsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MpsVariableStorer: Debug {
|
pub trait MpsVariableStorer: Debug {
|
||||||
fn get(&self, name: &str, op: &mut OpGetter) -> Result<&MpsType, RuntimeError>;
|
fn get(&self, name: &str, op: &mut OpGetter) -> Result<&MpsType, RuntimeError> {
|
||||||
|
match self.get_opt(name) {
|
||||||
|
Some(item) => Ok(item),
|
||||||
|
None => Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: op(),
|
||||||
|
msg: format!("Variable {} not found", name),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, name: &str, op: &mut OpGetter) -> Result<&mut MpsType, RuntimeError>;
|
fn get_opt(&self, name: &str) -> Option<&MpsType>;
|
||||||
|
|
||||||
|
fn get_mut(&mut self, name: &str, op: &mut OpGetter) -> Result<&mut MpsType, RuntimeError> {
|
||||||
|
match self.get_mut_opt(name) {
|
||||||
|
Some(item) => Ok(item),
|
||||||
|
None => Err(RuntimeError {
|
||||||
|
line: 0,
|
||||||
|
op: op(),
|
||||||
|
msg: format!("Variable {} not found", name),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut_opt(&mut self, name: &str) -> Option<&mut MpsType>;
|
||||||
|
|
||||||
fn assign(&mut self, name: &str, value: MpsType, op: &mut OpGetter)
|
fn assign(&mut self, name: &str, value: MpsType, op: &mut OpGetter)
|
||||||
-> Result<(), RuntimeError>;
|
-> Result<(), RuntimeError>;
|
||||||
|
@ -47,26 +69,13 @@ pub struct MpsOpStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MpsVariableStorer for MpsOpStorage {
|
impl MpsVariableStorer for MpsOpStorage {
|
||||||
fn get(&self, key: &str, op: &mut OpGetter) -> Result<&MpsType, RuntimeError> {
|
|
||||||
match self.storage.get(key) {
|
fn get_opt(&self, key: &str) -> Option<&MpsType> {
|
||||||
Some(item) => Ok(item),
|
self.storage.get(key)
|
||||||
None => Err(RuntimeError {
|
|
||||||
line: 0,
|
|
||||||
op: op(),
|
|
||||||
msg: format!("Variable {} not found", key),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, key: &str, op: &mut OpGetter) -> Result<&mut MpsType, RuntimeError> {
|
fn get_mut_opt(&mut self, key: &str) -> Option<&mut MpsType> {
|
||||||
match self.storage.get_mut(key) {
|
self.storage.get_mut(key)
|
||||||
Some(item) => Ok(item),
|
|
||||||
None => Err(RuntimeError {
|
|
||||||
line: 0,
|
|
||||||
op: op(),
|
|
||||||
msg: format!("Variable {} not found", key),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign(&mut self, key: &str, item: MpsType, op: &mut OpGetter) -> Result<(), RuntimeError> {
|
fn assign(&mut self, key: &str, item: MpsType, op: &mut OpGetter) -> Result<(), RuntimeError> {
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -3,6 +3,22 @@
|
||||||
//! The CLI interface includes a REPL for running scripts.
|
//! The CLI interface includes a REPL for running scripts.
|
||||||
//! The REPL interactive mode also provides more details about using MPS through the `?help` command.
|
//! The REPL interactive mode also provides more details about using MPS through the `?help` command.
|
||||||
//!
|
//!
|
||||||
|
//! ## FAQ
|
||||||
|
//! ### Is MPS Turing-Complete?
|
||||||
|
//! **No**. It can't perform arbitrary calculations (yet), which easily disqualifies MPS from being Turing-complete.
|
||||||
|
//!
|
||||||
|
//! ### Can I use MPS right now?
|
||||||
|
//! **Sure!** It's not complete, but MPS is completely useable for basic music queries right now. Hopefully most of the bugs have been ironed out as well...
|
||||||
|
//!
|
||||||
|
//! ### Why write a new language?
|
||||||
|
//! **I thought it would be fun**. I also wanted to be able to play my music without having to be at the whim of someone else's algorithm (and music), and playing just by album or artist was getting boring. I also thought designing a language specifically for iteration would be a novel approach to a language (though every approach is a novel approach for me).
|
||||||
|
//!
|
||||||
|
//! ### What is MPS?
|
||||||
|
//! **Music Playlist Script (MPS) is technically a query language for music files.** It uses an (auto-generated) SQLite3 database for SQL queries and can also directly query the filesystem. Queries can be filtered and extended by using filters and functions built-in to MPS (see mps-interpreter's README.md).
|
||||||
|
//!
|
||||||
|
//! ### Is MPS a scripting language?
|
||||||
|
//! **No**. Technically, it was designed to be one, but it doesn't meet the requirements of a scripting language (yet). One day, I would like it be Turing-complete and then it could be considered a scripting language. At the moment it is barely a query language.
|
||||||
|
//!
|
||||||
|
|
||||||
mod channel_io;
|
mod channel_io;
|
||||||
mod cli;
|
mod cli;
|
||||||
|
|
Loading…
Reference in a new issue