Add OR support for filters

This commit is contained in:
NGnius (Graham) 2022-01-16 20:55:46 -05:00
parent d23495a5ef
commit 592a5d035b
6 changed files with 221 additions and 18 deletions

View file

@ -33,7 +33,7 @@ impl MpsLanguageError for SyntaxError {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct RuntimeError {
pub line: usize,
pub op: PseudoOp,

View file

@ -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));
@ -281,9 +338,16 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
if start_of_predicate > tokens_len - 1 {
false
} else {
let tokens2: VecDeque<&MpsToken> =
VecDeque::from_iter(tokens.range(start_of_predicate..tokens_len - 1));
self.filter_factory.is_filter(&tokens2)
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
}

View file

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

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

View file

@ -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, "&"),
}
}
}

View file

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