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 struct RuntimeError {
pub line: usize, pub line: usize,
pub op: PseudoOp, 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::MpsLanguageDictionary;
use crate::lang::{BoxedMpsOpFactory, MpsOp, PseudoOp}; use crate::lang::{BoxedMpsOpFactory, MpsOp, PseudoOp};
use crate::lang::{RuntimeError, SyntaxError}; use crate::lang::{RuntimeError, SyntaxError};
use crate::lang::SingleItem;
use crate::processing::general::MpsType; use crate::processing::general::MpsType;
use crate::processing::OpGetter; use crate::processing::OpGetter;
use crate::tokens::MpsToken; use crate::tokens::MpsToken;
use crate::MpsContext; use crate::MpsContext;
use crate::MpsMusicItem; use crate::MpsMusicItem;
const INNER_VARIABLE_NAME: &str = "[inner variable]";
pub trait MpsFilterPredicate: Clone + Debug + Display { pub trait MpsFilterPredicate: Clone + Debug + Display {
fn matches( fn matches(
&mut self, &mut self,
@ -56,6 +59,7 @@ pub struct MpsFilterStatement<P: MpsFilterPredicate + 'static> {
predicate: P, predicate: P,
iterable: VariableOrOp, iterable: VariableOrOp,
context: Option<MpsContext>, context: Option<MpsContext>,
other_filters: Option<PseudoOp>,
} }
impl<P: MpsFilterPredicate + 'static> std::clone::Clone for MpsFilterStatement<P> { 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(), predicate: self.predicate.clone(),
iterable: self.iterable.clone(), iterable: self.iterable.clone(),
context: None, 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>; type Item = Result<MpsMusicItem, RuntimeError>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.predicate.is_complete() { if self.predicate.is_complete() && self.other_filters.is_none() {
return None; return None;
} }
let self_clone = self.clone(); let self_clone = self.clone();
@ -155,7 +160,7 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
maybe_result = Some(Err(e)); maybe_result = Some(Err(e));
self.context = Some(ctx); self.context = Some(ctx);
break; break;
} },
Ok(item) => { Ok(item) => {
let matches_result = let matches_result =
self.predicate.matches(&item, &mut ctx, &mut op_getter); self.predicate.matches(&item, &mut ctx, &mut op_getter);
@ -167,6 +172,58 @@ impl<P: MpsFilterPredicate + 'static> Iterator for MpsFilterStatement<P> {
} }
Ok(b) => b, 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 { if matches {
//self.context = Some(op.escape()); //self.context = Some(op.escape());
maybe_result = Some(Ok(item)); 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 { if start_of_predicate > tokens_len - 1 {
false false
} else { } else {
let tokens2: VecDeque<&MpsToken> = if let Some(pipe_location) = first_double_pipe(tokens) {
VecDeque::from_iter(tokens.range(start_of_predicate..tokens_len - 1)); let tokens2: VecDeque<&MpsToken> =
self.filter_factory.is_filter(&tokens2) 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 { } else {
false false
@ -318,13 +382,31 @@ impl<P: MpsFilterPredicate + 'static, F: MpsFilterFactory<P> + 'static> BoxedMps
} }
assert_token_raw(MpsToken::Dot, tokens)?; assert_token_raw(MpsToken::Dot, tokens)?;
assert_token_raw(MpsToken::OpenBracket, 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)?; 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 { Ok(Box::new(MpsFilterStatement {
predicate: filter, predicate: filter,
iterable: op, iterable: op,
context: None, context: None,
other_filters: another_filter,
})) }))
} }
} }
@ -360,3 +442,19 @@ fn last_dot_before_open_bracket(tokens: &VecDeque<MpsToken>) -> usize {
} }
0 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 operation;
mod pseudo_op; mod pseudo_op;
mod repeated_meme; mod repeated_meme;
mod single_op;
//mod statement; //mod statement;
mod type_primitives; mod type_primitives;
pub(crate) mod utility; pub(crate) mod utility;
@ -21,6 +22,7 @@ pub use lookup::Lookup;
pub use operation::{BoxedMpsOpFactory, MpsOp, MpsOpFactory, SimpleMpsOpFactory}; pub use operation::{BoxedMpsOpFactory, MpsOp, MpsOpFactory, SimpleMpsOpFactory};
pub use pseudo_op::PseudoOp; pub use pseudo_op::PseudoOp;
pub use repeated_meme::{repeated_tokens, RepeatedTokens}; pub use repeated_meme::{repeated_tokens, RepeatedTokens};
pub use single_op::SingleItem;
//pub(crate) use statement::MpsStatement; //pub(crate) use statement::MpsStatement;
pub use type_primitives::MpsTypePrimitive; 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, Dot,
Exclamation, Exclamation,
Interrogation, Interrogation,
Pipe,
Ampersand,
} }
impl MpsToken { impl MpsToken {
@ -34,10 +36,12 @@ impl MpsToken {
"." => Ok(Self::Dot), "." => Ok(Self::Dot),
"!" => Ok(Self::Exclamation), "!" => Ok(Self::Exclamation),
"?" => Ok(Self::Interrogation), "?" => Ok(Self::Interrogation),
"|" => Ok(Self::Pipe),
"&" => Ok(Self::Ampersand),
_ => { _ => {
// name validation // name validation
let mut ok = true; let mut ok = true;
for invalid_c in ["-", "+", ",", " ", "/", "\n", "\r", "!", "?", "=", "."] { for invalid_c in ["-", "+", ",", " ", "/", "\n", "\r", "!", "?", "=", ".", "&", "|"] {
if s.contains(invalid_c) { if s.contains(invalid_c) {
ok = false; ok = false;
break; break;
@ -156,6 +160,20 @@ impl MpsToken {
_ => false, _ => 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 { impl Display for MpsToken {
@ -176,6 +194,8 @@ impl Display for MpsToken {
Self::Dot => write!(f, "."), Self::Dot => write!(f, "."),
Self::Exclamation => write!(f, "!"), Self::Exclamation => write!(f, "!"),
Self::Interrogation => 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)))?; .map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
buf.push_back( buf.push_back(
MpsToken::parse_from_string(token) 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(); bigger_buf.clear();
} }
@ -92,7 +92,7 @@ where
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?; .map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
buf.push_back( buf.push_back(
MpsToken::parse_from_string(token) 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(); bigger_buf.clear();
} }
@ -102,7 +102,7 @@ where
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?; .map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
buf.push_back( buf.push_back(
MpsToken::parse_from_string(token) 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(); bigger_buf.clear();
} }
@ -146,7 +146,7 @@ where
.map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?; .map_err(|e| self.error(format!("UTF-8 encoding error: {}", e)))?;
buf.push_back( buf.push_back(
MpsToken::parse_from_string(token) 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(); bigger_buf.clear();
} }
@ -158,7 +158,7 @@ where
if input as char == '\n' { if input as char == '\n' {
self.line += 1; self.line += 1;
self.column = 0; self.column = 0;
} else { } else if input != 0 {
self.column += 1; // TODO correctly track columns with utf-8 characters longer than one byte 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 { fn current_column(&self) -> usize {
println!("Current column: {}", self.column);
self.column self.column
} }
@ -257,10 +258,10 @@ impl ReaderStateMachine {
'#' => Self::Octothorpe { out: input }, '#' => Self::Octothorpe { out: input },
'`' => Self::StartTickLiteral {}, '`' => Self::StartTickLiteral {},
'"' => Self::StartQuoteLiteral {}, '"' => Self::StartQuoteLiteral {},
' ' => Self::EndToken {}, '\n'| '\r' | '\t' | ' ' => Self::EndToken {},
'\n' | ';' => Self::EndStatement {}, ';' => Self::EndStatement {},
'\0' => Self::EndOfFile {}, '\0' => Self::EndOfFile {},
'(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' => Self::SingleCharToken { out: input }, '(' | ')' | ',' | '=' | '<' | '>' | '.' | '!' | '?' | '|' => Self::SingleCharToken { out: input },
_ => Self::Regular { out: input }, _ => Self::Regular { out: input },
}, },
Self::Escaped { inside } => match inside { Self::Escaped { inside } => match inside {
@ -284,7 +285,7 @@ impl ReaderStateMachine {
'/' => Self::Comment { out: input }, '/' => Self::Comment { out: input },
' ' => Self::EndToken {}, ' ' => Self::EndToken {},
'\0' => Self::EndOfFile {}, '\0' => Self::EndOfFile {},
'\n' | ';' => Self::EndStatement {}, ';' => Self::EndStatement {},
_ => Self::Regular { out: input }, _ => Self::Regular { out: input },
}, },
Self::Octothorpe { .. } => match input_char { Self::Octothorpe { .. } => match input_char {