Add OR support for filters
This commit is contained in:
parent
d23495a5ef
commit
592a5d035b
6 changed files with 221 additions and 18 deletions
|
@ -33,7 +33,7 @@ impl MpsLanguageError for SyntaxError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RuntimeError {
|
||||
pub line: usize,
|
||||
pub op: PseudoOp,
|
||||
|
|
|
@ -7,12 +7,15 @@ use crate::lang::utility::{assert_token, assert_token_raw};
|
|||
use crate::lang::MpsLanguageDictionary;
|
||||
use crate::lang::{BoxedMpsOpFactory, MpsOp, PseudoOp};
|
||||
use crate::lang::{RuntimeError, SyntaxError};
|
||||
use crate::lang::SingleItem;
|
||||
use crate::processing::general::MpsType;
|
||||
use crate::processing::OpGetter;
|
||||
use crate::tokens::MpsToken;
|
||||
use crate::MpsContext;
|
||||
use crate::MpsMusicItem;
|
||||
|
||||
const INNER_VARIABLE_NAME: &str = "[inner variable]";
|
||||
|
||||
pub trait MpsFilterPredicate: Clone + Debug + Display {
|
||||
fn matches(
|
||||
&mut self,
|
||||
|
@ -56,6 +59,7 @@ pub struct MpsFilterStatement<P: MpsFilterPredicate + 'static> {
|
|||
predicate: P,
|
||||
iterable: VariableOrOp,
|
||||
context: Option<MpsContext>,
|
||||
other_filters: Option<PseudoOp>,
|
||||
}
|
||||
|
||||
impl<P: MpsFilterPredicate + 'static> std::clone::Clone for MpsFilterStatement<P> {
|
||||
|
@ -64,6 +68,7 @@ impl<P: MpsFilterPredicate + 'static> std::clone::Clone for MpsFilterStatement<P
|
|||
predicate: self.predicate.clone(),
|
||||
iterable: self.iterable.clone(),
|
||||
context: None,
|
||||
other_filters: self.other_filters.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +139,7 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
|||
type Item = Result<MpsMusicItem, RuntimeError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.predicate.is_complete() {
|
||||
if self.predicate.is_complete() && self.other_filters.is_none() {
|
||||
return None;
|
||||
}
|
||||
let self_clone = self.clone();
|
||||
|
@ -155,7 +160,7 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
|||
maybe_result = Some(Err(e));
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Ok(item) => {
|
||||
let matches_result =
|
||||
self.predicate.matches(&item, &mut ctx, &mut op_getter);
|
||||
|
@ -167,6 +172,58 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
|
|||
}
|
||||
Ok(b) => b,
|
||||
};
|
||||
if let Some(inner) = &mut self.other_filters {
|
||||
// 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)), &mut op_getter) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
//self.context = Some(op.escape());
|
||||
maybe_result = Some(Err(e));
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
},
|
||||
};
|
||||
let inner_real = match inner.try_real() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
//self.context = Some(op.escape());
|
||||
maybe_result = Some(Err(e));
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
},
|
||||
};
|
||||
inner_real.enter(ctx);
|
||||
match inner_real.next() {
|
||||
Some(item) => {
|
||||
maybe_result = Some(item);
|
||||
ctx = inner_real.escape();
|
||||
match ctx.variables.remove(INNER_VARIABLE_NAME, &mut op_getter) {
|
||||
Ok(_) => {},
|
||||
Err(e) => match maybe_result {
|
||||
Some(Ok(_)) => maybe_result = Some(Err(e)),
|
||||
Some(Err(e2)) => maybe_result = Some(Err(e2)), // already failing, do not replace error,
|
||||
None => {}, // impossible
|
||||
}
|
||||
}
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
},
|
||||
None => {
|
||||
ctx = inner_real.escape(); // move ctx back to expected spot
|
||||
match ctx.variables.remove(INNER_VARIABLE_NAME, &mut op_getter) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
//self.context = Some(op.escape());
|
||||
maybe_result = Some(Err(e));
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
//self.context = Some(op.escape());
|
||||
maybe_result = Some(Ok(item));
|
||||
|
@ -280,11 +337,18 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
|
|||
let start_of_predicate = last_dot_before_open_bracket(tokens) + 2; // .(predicate)
|
||||
if start_of_predicate > tokens_len - 1 {
|
||||
false
|
||||
} else {
|
||||
if let Some(pipe_location) = first_double_pipe(tokens) {
|
||||
let tokens2: VecDeque<&MpsToken> =
|
||||
VecDeque::from_iter(tokens.range(start_of_predicate..pipe_location));
|
||||
self.filter_factory.is_filter(&tokens2)
|
||||
} else {
|
||||
let tokens2: VecDeque<&MpsToken> =
|
||||
VecDeque::from_iter(tokens.range(start_of_predicate..tokens_len - 1));
|
||||
self.filter_factory.is_filter(&tokens2)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -318,13 +382,31 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
|
|||
}
|
||||
assert_token_raw(MpsToken::Dot, tokens)?;
|
||||
assert_token_raw(MpsToken::OpenBracket, tokens)?;
|
||||
let mut end_tokens = tokens.split_off(tokens.len()-1); // don't parse closing bracket in filter
|
||||
let mut another_filter = None;
|
||||
let (has_or, end_tokens) = if let Some(pipe_location) = first_double_pipe(tokens) {
|
||||
(true, tokens.split_off(pipe_location)) // parse up to OR operator
|
||||
} else {
|
||||
(false, tokens.split_off(tokens.len()-1)) // don't parse closing bracket in filter
|
||||
};
|
||||
let filter = self.filter_factory.build_filter(tokens, dict)?;
|
||||
assert_token_raw(MpsToken::CloseBracket, &mut end_tokens)?;
|
||||
tokens.extend(end_tokens);
|
||||
if has_or {
|
||||
// recursively build other filters for OR operation
|
||||
assert_token_raw(MpsToken::Pipe, tokens)?;
|
||||
assert_token_raw(MpsToken::Pipe, tokens)?;
|
||||
// emit fake filter syntax
|
||||
tokens.push_front(MpsToken::OpenBracket);
|
||||
tokens.push_front(MpsToken::Dot);
|
||||
tokens.push_front(MpsToken::Name(INNER_VARIABLE_NAME.into())); // impossible to obtain through parsing on purpose
|
||||
another_filter = Some(dict.try_build_statement(tokens)?.into());
|
||||
} else {
|
||||
assert_token_raw(MpsToken::CloseBracket, tokens)?; // remove closing bracket
|
||||
}
|
||||
Ok(Box::new(MpsFilterStatement {
|
||||
predicate: filter,
|
||||
iterable: op,
|
||||
context: None,
|
||||
other_filters: another_filter,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -360,3 +442,19 @@ fn last_dot_before_open_bracket(tokens: &VecDeque<MpsToken>) -> usize {
|
|||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn first_double_pipe(tokens: &VecDeque<MpsToken>) -> Option<usize> {
|
||||
let mut pipe_found = false;
|
||||
for i in 0..tokens.len() {
|
||||
if tokens[i].is_pipe() {
|
||||
if pipe_found {
|
||||
return Some(i-1);
|
||||
} else {
|
||||
pipe_found = true;
|
||||
}
|
||||
} else {
|
||||
pipe_found = false;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ mod lookup;
|
|||
mod operation;
|
||||
mod pseudo_op;
|
||||
mod repeated_meme;
|
||||
mod single_op;
|
||||
//mod statement;
|
||||
mod type_primitives;
|
||||
pub(crate) mod utility;
|
||||
|
@ -21,6 +22,7 @@ pub use lookup::Lookup;
|
|||
pub use operation::{BoxedMpsOpFactory, MpsOp, MpsOpFactory, SimpleMpsOpFactory};
|
||||
pub use pseudo_op::PseudoOp;
|
||||
pub use repeated_meme::{repeated_tokens, RepeatedTokens};
|
||||
pub use single_op::SingleItem;
|
||||
//pub(crate) use statement::MpsStatement;
|
||||
pub use type_primitives::MpsTypePrimitive;
|
||||
|
||||
|
|
82
mps-interpreter/src/lang/single_op.rs
Normal file
82
mps-interpreter/src/lang/single_op.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::Iterator;
|
||||
|
||||
use crate::lang::{MpsOp, RuntimeError};
|
||||
use crate::MpsContext;
|
||||
use crate::MpsMusicItem;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleItem {
|
||||
context: Option<MpsContext>,
|
||||
item: Result<MpsMusicItem, RuntimeError>,
|
||||
is_complete: bool,
|
||||
}
|
||||
|
||||
impl SingleItem {
|
||||
pub fn new(item: Result<MpsMusicItem, RuntimeError>) -> Self {
|
||||
Self {
|
||||
context: None,
|
||||
item: item,
|
||||
is_complete: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_ok(item: MpsMusicItem) -> Self {
|
||||
Self::new(Ok(item))
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, new_item: Result<MpsMusicItem, RuntimeError>) {
|
||||
self.item = new_item
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SingleItem {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match &self.item {
|
||||
Ok(item) => write!(f, "*single item*[Ok({})]", item.filename),
|
||||
Err(e) => write!(f, "*single-item*[Err({})]", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::clone::Clone for SingleItem {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
context: None,
|
||||
item: self.item.clone(),
|
||||
is_complete: self.is_complete,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SingleItem {
|
||||
type Item = Result<MpsMusicItem, RuntimeError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_complete {
|
||||
None
|
||||
} else {
|
||||
self.is_complete = true;
|
||||
Some(self.item.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MpsOp for SingleItem {
|
||||
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.is_complete = false;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ pub enum MpsToken {
|
|||
Dot,
|
||||
Exclamation,
|
||||
Interrogation,
|
||||
Pipe,
|
||||
Ampersand,
|
||||
}
|
||||
|
||||
impl MpsToken {
|
||||
|
@ -34,10 +36,12 @@ impl MpsToken {
|
|||
"." => Ok(Self::Dot),
|
||||
"!" => Ok(Self::Exclamation),
|
||||
"?" => Ok(Self::Interrogation),
|
||||
"|" => Ok(Self::Pipe),
|
||||
"&" => Ok(Self::Ampersand),
|
||||
_ => {
|
||||
// name validation
|
||||
let mut ok = true;
|
||||
for invalid_c in ["-", "+", ",", " ", "/", "\n", "\r", "!", "?", "=", "."] {
|
||||
for invalid_c in ["-", "+", ",", " ", "/", "\n", "\r", "!", "?", "=", ".", "&", "|"] {
|
||||
if s.contains(invalid_c) {
|
||||
ok = false;
|
||||
break;
|
||||
|
@ -156,6 +160,20 @@ impl MpsToken {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pipe(&self) -> bool {
|
||||
match self {
|
||||
Self::Pipe => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ampersand(&self) -> bool {
|
||||
match self {
|
||||
Self::Ampersand => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MpsToken {
|
||||
|
@ -176,6 +194,8 @@ impl Display for MpsToken {
|
|||
Self::Dot => write!(f, "."),
|
||||
Self::Exclamation => write!(f, "!"),
|
||||
Self::Interrogation => write!(f, "?"),
|
||||
Self::Pipe => write!(f, "|"),
|
||||
Self::Ampersand => write!(f, "&"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ where
|
|||
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
|
||||
buf.push_back(
|
||||
MpsToken::parse_from_string(token)
|
||||
.map_err(|e| self.error(format!("Invalid token {}", e)))?,
|
||||
.map_err(|e| self.error(format!("invalid token `{}`", e)))?,
|
||||
);
|
||||
bigger_buf.clear();
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ where
|
|||
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
|
||||
buf.push_back(
|
||||
MpsToken::parse_from_string(token)
|
||||
.map_err(|e| self.error(format!("Invalid token {}", e)))?,
|
||||
.map_err(|e| self.error(format!("invalid token `{}`", e)))?,
|
||||
);
|
||||
bigger_buf.clear();
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ where
|
|||
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
|
||||
buf.push_back(
|
||||
MpsToken::parse_from_string(token)
|
||||
.map_err(|e| self.error(format!("Invalid token {}", e)))?,
|
||||
.map_err(|e| self.error(format!("invalid token `{}`", e)))?,
|
||||
);
|
||||
bigger_buf.clear();
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ where
|
|||
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
|
||||
buf.push_back(
|
||||
MpsToken::parse_from_string(token)
|
||||
.map_err(|e| self.error(format!("invalid token {}", e)))?,
|
||||
.map_err(|e| self.error(format!("invalid token `{}`", e)))?,
|
||||
);
|
||||
bigger_buf.clear();
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ where
|
|||
if input as char == '\n' {
|
||||
self.line += 1;
|
||||
self.column = 0;
|
||||
} else {
|
||||
} else if input != 0 {
|
||||
self.column += 1; // TODO correctly track columns with utf-8 characters longer than one byte
|
||||
}
|
||||
}
|
||||
|
@ -182,6 +182,7 @@ where
|
|||
}
|
||||
|
||||
fn current_column(&self) -> usize {
|
||||
println!("Current column: {}", self.column);
|
||||
self.column
|
||||
}
|
||||
|
||||
|
@ -257,10 +258,10 @@ impl ReaderStateMachine {
|
|||
'#' => Self::Octothorpe { out: input },
|
||||
'`' => Self::StartTickLiteral {},
|
||||
'"' => Self::StartQuoteLiteral {},
|
||||
' ' => Self::EndToken {},
|
||||
'\n' | ';' => Self::EndStatement {},
|
||||
'\n'| '\r' | '\t' | ' ' => Self::EndToken {},
|
||||
';' => Self::EndStatement {},
|
||||
'\0' => Self::EndOfFile {},
|
||||
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' => Self::SingleCharToken { out: input },
|
||||
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' => Self::SingleCharToken { out: input },
|
||||
_ => Self::Regular { out: input },
|
||||
},
|
||||
Self::Escaped { inside } => match inside {
|
||||
|
@ -284,7 +285,7 @@ impl ReaderStateMachine {
|
|||
'/' => Self::Comment { out: input },
|
||||
' ' => Self::EndToken {},
|
||||
'\0' => Self::EndOfFile {},
|
||||
'\n' | ';' => Self::EndStatement {},
|
||||
';' => Self::EndStatement {},
|
||||
_ => Self::Regular { out: input },
|
||||
},
|
||||
Self::Octothorpe { .. } => match input_char {
|
||||
|
|
Loading…
Reference in a new issue