From ca17313b7b27929e5c723d1466d77a8aae937a4c Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 12 Jan 2022 11:06:32 -0500 Subject: [PATCH] Add single-index filter --- mps-interpreter/src/interpretor.rs | 1 + mps-interpreter/src/lang/lookup.rs | 74 ++++++++++++++ mps-interpreter/src/lang/mod.rs | 2 + .../lang/vocabulary/filters/index_filter.rs | 99 +++++++++++++++++++ .../src/lang/vocabulary/filters/mod.rs | 2 + mps-interpreter/tests/single_line.rs | 19 ++++ 6 files changed, 197 insertions(+) create mode 100644 mps-interpreter/src/lang/lookup.rs create mode 100644 mps-interpreter/src/lang/vocabulary/filters/index_filter.rs diff --git a/mps-interpreter/src/interpretor.rs b/mps-interpreter/src/interpretor.rs index 1361867..7e59c60 100644 --- a/mps-interpreter/src/interpretor.rs +++ b/mps-interpreter/src/interpretor.rs @@ -159,6 +159,7 @@ pub(crate) fn standard_vocab(vocabulary: &mut MpsLanguageDictionary) { .add(crate::lang::vocabulary::filters::empty_filter()) .add(crate::lang::vocabulary::filters::field_filter()) .add(crate::lang::vocabulary::filters::field_filter_maybe()) + .add(crate::lang::vocabulary::filters::index_filter()) .add(crate::lang::vocabulary::sql_function_factory()) .add(crate::lang::vocabulary::simple_sql_function_factory()) .add(crate::lang::vocabulary::CommentStatementFactory) diff --git a/mps-interpreter/src/lang/lookup.rs b/mps-interpreter/src/lang/lookup.rs new file mode 100644 index 0000000..f881e88 --- /dev/null +++ b/mps-interpreter/src/lang/lookup.rs @@ -0,0 +1,74 @@ +use std::collections::VecDeque; +use std::fmt::{Display, Error, Formatter}; + +//use super::MpsTypePrimitive; +use crate::processing::general::MpsType; +use crate::lang::{RuntimeError, SyntaxError}; +use super::utility::{assert_type, assert_token, check_is_type}; +use crate::tokens::MpsToken; +use crate::MpsContext; +use crate::processing::OpGetter; + +#[derive(Debug)] +pub enum Lookup { + Static(MpsType), + Variable(String) +} + +impl Lookup { + pub fn check_is(token: &MpsToken) -> bool { + token.is_name() || check_is_type(token) + } + pub fn parse(tokens: &mut VecDeque) -> Result { + if tokens.is_empty() { + Err(SyntaxError { + line: 0, + token: MpsToken::Name("Float | UInt | Int | Bool".into()), + got: None, + }) + } else if check_is_type(&tokens[0]) { + Ok(Self::Static(MpsType::Primitive(assert_type(tokens)?))) + } else { + Ok(Self::Variable(assert_token(|t| match t { + MpsToken::Name(s) => Some(s), + _ => None, + }, MpsToken::Name("variable_name".into()), tokens)?)) + } + } + + pub fn get_mut<'a, 'b: 'a>(&'b mut self, ctx: &'a mut MpsContext, op: &mut OpGetter) -> Result<&'a mut MpsType, RuntimeError> { + match self { + Self::Static(var) => Ok(var), + Self::Variable(name) => ctx.variables.get_mut(name, op) + } + } + + pub fn get<'a, 'b: 'a>(&'b self, ctx: &'a MpsContext, op: &mut OpGetter) -> Result<&'a MpsType, RuntimeError> { + match self { + Self::Static(var) => Ok(var), + Self::Variable(name) => ctx.variables.get(name, op) + } + } +} + +impl Display for Lookup { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + Self::Static(var) => write!(f, "{}", var), + Self::Variable(name) => write!(f, "{}", name), + } + + } +} + +impl std::clone::Clone for Lookup { + fn clone(&self) -> Self { + match self { + Self::Static(var) => match var { + MpsType::Primitive(p) => Self::Static(MpsType::Primitive(p.clone())), + _ => panic!("Can't clone static operator (invalid state)") + }, + Self::Variable(name) => Self::Variable(name.clone()), + } + } +} diff --git a/mps-interpreter/src/lang/mod.rs b/mps-interpreter/src/lang/mod.rs index 57cf818..fead6b4 100644 --- a/mps-interpreter/src/lang/mod.rs +++ b/mps-interpreter/src/lang/mod.rs @@ -3,6 +3,7 @@ mod dictionary; mod error; mod filter; mod function; +mod lookup; mod operation; mod pseudo_op; mod repeated_meme; @@ -16,6 +17,7 @@ pub use filter::{ MpsFilterFactory, MpsFilterPredicate, MpsFilterStatement, MpsFilterStatementFactory, }; pub use function::{MpsFunctionFactory, MpsFunctionStatementFactory}; +pub use lookup::Lookup; pub use operation::{BoxedMpsOpFactory, MpsOp, MpsOpFactory, SimpleMpsOpFactory}; pub use pseudo_op::PseudoOp; pub use repeated_meme::{repeated_tokens, RepeatedTokens}; diff --git a/mps-interpreter/src/lang/vocabulary/filters/index_filter.rs b/mps-interpreter/src/lang/vocabulary/filters/index_filter.rs new file mode 100644 index 0000000..d18a9c4 --- /dev/null +++ b/mps-interpreter/src/lang/vocabulary/filters/index_filter.rs @@ -0,0 +1,99 @@ +use std::collections::VecDeque; +use std::fmt::{Debug, Display, Error, Formatter}; + +use crate::lang::{MpsLanguageDictionary, MpsTypePrimitive}; +use crate::lang::{MpsFilterFactory, MpsFilterPredicate, MpsFilterStatementFactory}; +use crate::lang::{RuntimeError, SyntaxError}; +use crate::lang::Lookup; +use crate::processing::{OpGetter, general::MpsType}; +use crate::tokens::MpsToken; +use crate::MpsContext; +use crate::MpsMusicItem; + +#[derive(Debug, Clone)] +pub struct IndexFilter { + index: Lookup, + // state + current: u64, + complete: bool, +} + +impl Display for IndexFilter { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "{}", self.index) + } +} + +impl MpsFilterPredicate for IndexFilter { + fn matches( + &mut self, + _item: &MpsMusicItem, + ctx: &mut MpsContext, + op: &mut OpGetter, + ) -> Result { + let index: u64 = match self.index.get(ctx, op)? { + MpsType::Primitive(val) => match val { + MpsTypePrimitive::Int(i) => *i as u64, + MpsTypePrimitive::UInt(u) => *u, + MpsTypePrimitive::Float(f) => *f as u64, + val => return Err(RuntimeError { + line: 0, + op: op(), + msg: format!("Cannot use {} as index", val), + }) + }, + val => return Err(RuntimeError { + line: 0, + op: op(), + msg: format!("Cannot use {} as index", val), + }) + }; + if self.current == index { + self.current += 1; + self.complete = true; + Ok(true) + } else { + self.current += 1; + Ok(false) + } + } + + fn is_complete(&self) -> bool { + self.complete + } + + fn reset(&mut self) -> Result<(), RuntimeError> { + self.current = 0; + self.complete = false; + Ok(()) + } +} + +pub struct IndexFilterFactory; + +impl MpsFilterFactory for IndexFilterFactory { + fn is_filter(&self, tokens: &VecDeque<&MpsToken>) -> bool { + tokens.len() == 1 + && Lookup::check_is(&tokens[0]) + } + + fn build_filter( + &self, + tokens: &mut VecDeque, + _dict: &MpsLanguageDictionary, + ) -> Result { + let lookup = Lookup::parse(tokens)?; + Ok(IndexFilter { + index: lookup, + current: 0, + complete: false, + }) + } +} + +pub type IndexFilterStatementFactory = MpsFilterStatementFactory; + +#[inline(always)] +pub fn index_filter() -> IndexFilterStatementFactory { + IndexFilterStatementFactory::new(IndexFilterFactory) +} diff --git a/mps-interpreter/src/lang/vocabulary/filters/mod.rs b/mps-interpreter/src/lang/vocabulary/filters/mod.rs index f02416d..7a343fc 100644 --- a/mps-interpreter/src/lang/vocabulary/filters/mod.rs +++ b/mps-interpreter/src/lang/vocabulary/filters/mod.rs @@ -1,6 +1,7 @@ mod empty_filter; mod field_filter; mod field_filter_maybe; +mod index_filter; pub(crate) mod utility; pub use empty_filter::{ @@ -10,3 +11,4 @@ pub use field_filter::{ field_filter, FieldFilter, FieldFilterFactory, FieldFilterStatementFactory, FieldFilterErrorHandling, }; pub use field_filter_maybe::{field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory}; +pub use index_filter::{index_filter, IndexFilter, IndexFilterFactory, IndexFilterStatementFactory}; diff --git a/mps-interpreter/tests/single_line.rs b/mps-interpreter/tests/single_line.rs index d297505..f974b11 100644 --- a/mps-interpreter/tests/single_line.rs +++ b/mps-interpreter/tests/single_line.rs @@ -197,3 +197,22 @@ fn execute_files_line() -> Result<(), Box> { )?; execute_single_line(r"files()", false, true) } + +#[test] +fn execute_indexfilter_line() -> Result<(), Box> { + execute_single_line( + "files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(2)", + false, + true, + )?; + execute_single_line( + "files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(0)", + false, + true, + )?; + execute_single_line( + "files(`~/Music/MusicFlac/Bruno Mars/24K Magic/`).(200)", + true, + true, + ) +}