diff --git a/interpreter/src/interpretor.rs b/interpreter/src/interpretor.rs index d38a67b..958be65 100644 --- a/interpreter/src/interpretor.rs +++ b/interpreter/src/interpretor.rs @@ -6,21 +6,25 @@ pub(crate) fn standard_vocab(vocabulary: &mut LanguageDictionary) { // filters .add(crate::lang::vocabulary::filters::empty_filter()) .add(crate::lang::vocabulary::filters::unique_filter()) // accepts .(unique) - .add(crate::lang::vocabulary::filters::field_filter()) // accepts any .(something) - .add(crate::lang::vocabulary::filters::field_filter_maybe()) - .add(crate::lang::vocabulary::filters::index_filter()) .add(crate::lang::vocabulary::filters::range_filter()) - .add(crate::lang::vocabulary::filters::field_like_filter()) - .add(crate::lang::vocabulary::filters::field_re_filter()) + .add( // accepts any .(.something) + crate::lang::vocabulary::filters::field::FieldFilterBlockFactory::new() + .push(crate::lang::vocabulary::filters::field::FieldFilterComparisonFactory) + .push(crate::lang::vocabulary::filters::field::FieldFilterMaybeFactory) + .push(crate::lang::vocabulary::filters::field::FieldLikeFilterFactory) + .push(crate::lang::vocabulary::filters::field::FieldRegexFilterFactory) + .to_statement_factory() + ) + .add(crate::lang::vocabulary::filters::index_filter()) .add(crate::lang::vocabulary::filters::unique_field_filter()) .add(crate::lang::vocabulary::filters::nonempty_filter()) // sorters .add(crate::lang::vocabulary::sorters::empty_sort()) - .add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(shuffle) + .add(crate::lang::vocabulary::sorters::shuffle_sort()) // accepts ~(~shuffle) .add(crate::lang::vocabulary::sorters::bliss_sort()) .add(crate::lang::vocabulary::sorters::bliss_next_sort()) .add(crate::lang::vocabulary::sorters::radio_sort()) - .add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(name) + .add(crate::lang::vocabulary::sorters::field_sort()) // accepts any ~(.name) // iter blocks .add( crate::lang::ItemBlockFactory::new() diff --git a/interpreter/src/lang/vocabulary/filters/field_filter.rs b/interpreter/src/lang/vocabulary/filters/field/field_filter.rs similarity index 77% rename from interpreter/src/lang/vocabulary/filters/field_filter.rs rename to interpreter/src/lang/vocabulary/filters/field/field_filter.rs index e12a9ed..c3bcd32 100644 --- a/interpreter/src/lang/vocabulary/filters/field_filter.rs +++ b/interpreter/src/lang/vocabulary/filters/field/field_filter.rs @@ -1,11 +1,11 @@ use std::collections::VecDeque; use std::fmt::{Debug, Display, Error, Formatter}; -use super::utility::{assert_comparison_operator, comparison_op}; -use crate::lang::utility::{assert_token, assert_type, check_is_type, assert_token_raw}; +use super::super::utility::{assert_comparison_operator, comparison_op}; +use super::{FieldFilterFactory, FieldFilterPredicate}; +use crate::lang::utility::{assert_token, assert_type, check_is_type}; use crate::lang::LanguageDictionary; use crate::lang::TypePrimitive; -use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory}; use crate::lang::{RuntimeMsg, SyntaxError}; use crate::processing::general::Type; use crate::tokens::Token; @@ -46,7 +46,7 @@ impl Display for FieldFilter { } } -impl FilterPredicate for FieldFilter { +impl FieldFilterPredicate for FieldFilter { fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result { let variable = match &self.val { VariableOrValue::Variable(name) => match ctx.variables.get(name)? { @@ -93,41 +93,33 @@ impl FilterPredicate for FieldFilter { fn reset(&mut self) -> Result<(), RuntimeMsg> { Ok(()) } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } -pub struct FieldFilterFactory; +pub struct FieldFilterComparisonFactory; -impl FilterFactory for FieldFilterFactory { - fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool { +impl FieldFilterFactory for FieldFilterComparisonFactory { + fn is_filter(&self, tokens: &[Token]) -> bool { let tokens_len = tokens.len(); - (tokens_len >= 3 + (tokens_len >= 1 // .field > variable OR .field < variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_open_angle_bracket() || tokens[2].is_close_angle_bracket())) - || (tokens_len >= 4 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_open_angle_bracket() || tokens[2].is_close_angle_bracket() || tokens[2].is_equals() || tokens[2].is_exclamation()) - && tokens[3].is_equals() - && !(tokens_len > 4 && tokens[4].is_equals()) + && (tokens[0].is_open_angle_bracket() || tokens[0].is_close_angle_bracket())) + || (tokens_len >= 2 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable + && (tokens[0].is_open_angle_bracket() || tokens[0].is_close_angle_bracket() || tokens[0].is_equals() || tokens[0].is_exclamation()) + && tokens[1].is_equals() + && !(tokens_len > 2 && tokens[2].is_equals()) ) } fn build_filter( &self, tokens: &mut VecDeque, + field: String, _dict: &LanguageDictionary, ) -> Result { - assert_token_raw(Token::Dot, tokens)?; - let field = assert_token( - |t| match t { - Token::Name(n) => Some(n), - _ => None, - }, - Token::Name("field_name".into()), - tokens, - )?; let compare_operator = assert_comparison_operator(tokens)?; if check_is_type(&tokens[0]) { let value = VariableOrValue::Value(assert_type(tokens)?); @@ -159,10 +151,3 @@ impl FilterFactory for FieldFilterFactory { } } } - -pub type FieldFilterStatementFactory = FilterStatementFactory; - -#[inline(always)] -pub fn field_filter() -> FieldFilterStatementFactory { - FieldFilterStatementFactory::new(FieldFilterFactory) -} diff --git a/interpreter/src/lang/vocabulary/filters/field/field_filter_factory.rs b/interpreter/src/lang/vocabulary/filters/field/field_filter_factory.rs new file mode 100644 index 0000000..f8523de --- /dev/null +++ b/interpreter/src/lang/vocabulary/filters/field/field_filter_factory.rs @@ -0,0 +1,160 @@ +use std::collections::VecDeque; +use std::fmt::{Debug, Display, Error, Formatter}; + +use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory}; +use crate::tokens::Token; +use crate::lang::utility::{assert_token, assert_token_raw}; +use crate::lang::LanguageDictionary; +use crate::lang::{RuntimeMsg, SyntaxError}; +use crate::Context; +use crate::Item; + +pub trait FieldFilterFactory: Send + Sync { + fn is_filter(&self, tokens: &[Token]) -> bool; + + fn build_filter( + &self, + tokens: &mut VecDeque, + field: String, + dict: &LanguageDictionary, + ) -> Result; +} + +pub trait FieldFilterPredicate: Send + Sync + Debug + Display { + fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result; + + fn is_complete(&self) -> bool; + + fn reset(&mut self) -> Result<(), RuntimeMsg>; + + fn box_clone(&self) -> Box; +} + +pub struct FieldFilterFactoryBoxer { + inner: Box> +} + +#[derive(Debug)] +pub struct BoxedFilterPredicate { + inner: Box +} + +impl Clone for BoxedFilterPredicate { + fn clone(&self) -> Self { + Self { + inner: self.inner.box_clone() + } + } +} + +impl Display for BoxedFilterPredicate { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + (&self.inner as &dyn Display).fmt(f) + } +} + +impl FilterPredicate for BoxedFilterPredicate { + fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result { + self.inner.matches(item, ctx) + } + + fn is_complete(&self) -> bool { + self.inner.is_complete() + } + + fn reset(&mut self) -> Result<(), RuntimeMsg> { + self.inner.reset() + } +} + +impl FieldFilterPredicate for BoxedFilterPredicate { + fn matches(&mut self, item: &Item, ctx: &mut Context) -> Result { + self.inner.matches(item, ctx) + } + + fn is_complete(&self) -> bool { + self.inner.is_complete() + } + + fn reset(&mut self) -> Result<(), RuntimeMsg> { + self.inner.reset() + } + + fn box_clone(&self) -> Box { + self.inner.box_clone() + } +} + +impl FieldFilterFactory for FieldFilterFactoryBoxer { + fn is_filter(&self, tokens: &[Token]) -> bool { + self.inner.is_filter(tokens) + } + + fn build_filter( + &self, + tokens: &mut VecDeque, + field: String, + dict: &LanguageDictionary, + ) -> Result { + self.inner.build_filter(tokens, field, dict).map(|x| BoxedFilterPredicate { inner: Box::new(x) }) + } +} + +pub struct FieldFilterBlockFactory { + field_filters: Vec>>, +} + +impl FieldFilterBlockFactory { + pub fn new() -> Self { + Self { + field_filters: Vec::new(), + } + } + + pub fn push + 'static>(mut self, factory: F) -> Self { + self.field_filters.push( + Box::new(FieldFilterFactoryBoxer { inner: Box::new(factory) }) + ); + self + } + + #[inline(always)] + pub fn to_statement_factory(self) -> FieldFilterStatementFactory { + FieldFilterStatementFactory::new(self) + } +} + +impl FilterFactory for FieldFilterBlockFactory { + fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool { + tokens.len() > 1 && tokens[0].is_dot() + } + + fn build_filter( + &self, + tokens: &mut VecDeque, + dict: &LanguageDictionary, + ) -> Result { + assert_token_raw(Token::Dot, tokens)?; + let field = assert_token( + |t| match t { + Token::Name(n) => Some(n), + _ => None, + }, + Token::Name("field_name".into()), + tokens, + )?; + for filter in &self.field_filters { + if filter.is_filter(tokens.make_contiguous()) { + return filter.build_filter(tokens, field, dict); + } + } + Err(SyntaxError { + got: tokens.front().cloned(), + token: Token::Name("".into()), + line: 0, + }) + } +} + +pub type FieldFilterStatementFactory = + FilterStatementFactory; diff --git a/interpreter/src/lang/vocabulary/filters/field_filter_maybe.rs b/interpreter/src/lang/vocabulary/filters/field/field_filter_maybe.rs similarity index 60% rename from interpreter/src/lang/vocabulary/filters/field_filter_maybe.rs rename to interpreter/src/lang/vocabulary/filters/field/field_filter_maybe.rs index eb8ec67..7b9059a 100644 --- a/interpreter/src/lang/vocabulary/filters/field_filter_maybe.rs +++ b/interpreter/src/lang/vocabulary/filters/field/field_filter_maybe.rs @@ -1,45 +1,34 @@ use std::collections::VecDeque; -use super::utility::assert_comparison_operator; +use super::super::utility::assert_comparison_operator; use super::{field_filter::VariableOrValue, FieldFilter, FieldFilterErrorHandling}; use crate::lang::utility::{assert_token, assert_token_raw, assert_type, check_is_type}; use crate::lang::LanguageDictionary; use crate::lang::SyntaxError; -use crate::lang::{FilterFactory, FilterStatementFactory}; +//use crate::lang::{FilterFactory, FilterStatementFactory}; +use super::FieldFilterFactory; use crate::tokens::Token; pub struct FieldFilterMaybeFactory; -impl FilterFactory for FieldFilterMaybeFactory { - fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool { +impl FieldFilterFactory for FieldFilterMaybeFactory { + fn is_filter(&self, tokens: &[Token]) -> bool { let tokens_len = tokens.len(); - (tokens_len >= 4 // .field > variable OR .field < variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_interrogation() || tokens[2].is_exclamation()) - && (tokens[3].is_open_angle_bracket() || tokens[3].is_close_angle_bracket())) - || (tokens_len >= 5 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_interrogation() || tokens[2].is_exclamation()) - && (tokens[3].is_open_angle_bracket() || tokens[3].is_close_angle_bracket() || tokens[3].is_equals() || tokens[3].is_exclamation()) - && tokens[4].is_equals()) + (tokens_len >= 2 // .field > variable OR .field < variable + && (tokens[0].is_interrogation() || tokens[0].is_exclamation()) + && (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket())) + || (tokens_len >= 3 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable + && (tokens[0].is_interrogation() || tokens[0].is_exclamation()) + && (tokens[1].is_open_angle_bracket() || tokens[1].is_close_angle_bracket() || tokens[1].is_equals() || tokens[1].is_exclamation()) + && tokens[2].is_equals()) } fn build_filter( &self, tokens: &mut VecDeque, + field: String, _dict: &LanguageDictionary, ) -> Result { - assert_token_raw(Token::Dot, tokens)?; - let field = assert_token( - |t| match t { - Token::Name(n) => Some(n), - _ => None, - }, - Token::Name("field_name".into()), - tokens, - )?; let error_f; let error_c; if tokens[0].is_interrogation() { @@ -82,11 +71,3 @@ impl FilterFactory for FieldFilterMaybeFactory { } } } - -pub type FieldFilterMaybeStatementFactory = - FilterStatementFactory; - -#[inline(always)] -pub fn field_filter_maybe() -> FieldFilterMaybeStatementFactory { - FieldFilterMaybeStatementFactory::new(FieldFilterMaybeFactory) -} diff --git a/interpreter/src/lang/vocabulary/filters/field_like_filter.rs b/interpreter/src/lang/vocabulary/filters/field/field_like_filter.rs similarity index 78% rename from interpreter/src/lang/vocabulary/filters/field_like_filter.rs rename to interpreter/src/lang/vocabulary/filters/field/field_like_filter.rs index 49331c6..3e700c0 100644 --- a/interpreter/src/lang/vocabulary/filters/field_like_filter.rs +++ b/interpreter/src/lang/vocabulary/filters/field/field_like_filter.rs @@ -5,7 +5,7 @@ use super::field_filter::{FieldFilterErrorHandling, VariableOrValue}; use crate::lang::utility::{assert_token, assert_token_raw, check_name}; use crate::lang::LanguageDictionary; use crate::lang::TypePrimitive; -use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory}; +use super::{FieldFilterFactory, FieldFilterPredicate}; use crate::lang::{RuntimeMsg, SyntaxError}; use crate::processing::general::Type; use crate::tokens::Token; @@ -22,7 +22,7 @@ pub struct FieldLikeFilter { impl FieldLikeFilter { fn sanitise_string(s: &str) -> String { - super::utility::sanitise_string(s) + super::super::utility::sanitise_string(s) } } @@ -35,7 +35,7 @@ impl Display for FieldLikeFilter { } } -impl FilterPredicate for FieldLikeFilter { +impl FieldFilterPredicate for FieldLikeFilter { fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result { let variable = match &self.val { VariableOrValue::Variable(name) => match ctx.variables.get(name)? { @@ -74,38 +74,30 @@ impl FilterPredicate for FieldLikeFilter { fn reset(&mut self) -> Result<(), RuntimeMsg> { Ok(()) } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } pub struct FieldLikeFilterFactory; -impl FilterFactory for FieldLikeFilterFactory { - fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool { +impl FieldFilterFactory for FieldLikeFilterFactory { + fn is_filter(&self, tokens: &[Token]) -> bool { let tokens_len = tokens.len(); - (tokens_len >= 3 // field like variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (check_name("like", tokens[2]) || check_name("unlike", tokens[2]))) - || (tokens_len >= 4 // field? like variable OR field! like variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_interrogation() || tokens[2].is_exclamation()) - && (check_name("like", tokens[3]) || check_name("unlike", tokens[3]))) + (tokens_len >= 1 // field like variable + && (check_name("like", &tokens[0]) || check_name("unlike", &tokens[0]))) + || (tokens_len >= 2 // field? like variable OR field! like variable + && (tokens[0].is_interrogation() || tokens[0].is_exclamation()) + && (check_name("like", &tokens[1]) || check_name("unlike", &tokens[1]))) } fn build_filter( &self, tokens: &mut VecDeque, + field: String, _dict: &LanguageDictionary, ) -> Result { - assert_token_raw(Token::Dot, tokens)?; - let field = assert_token( - |t| match t { - Token::Name(n) => Some(n), - _ => None, - }, - Token::Name("field_name".into()), - tokens, - )?; let error_handling = if tokens[0].is_interrogation() { assert_token_raw(Token::Interrogation, tokens)?; FieldFilterErrorHandling::Ignore @@ -164,11 +156,3 @@ impl FilterFactory for FieldLikeFilterFactory { } } } - -pub type FieldLikeFilterStatementFactory = - FilterStatementFactory; - -#[inline(always)] -pub fn field_like_filter() -> FieldLikeFilterStatementFactory { - FieldLikeFilterStatementFactory::new(FieldLikeFilterFactory) -} diff --git a/interpreter/src/lang/vocabulary/filters/field_match_filter.rs b/interpreter/src/lang/vocabulary/filters/field/field_match_filter.rs similarity index 85% rename from interpreter/src/lang/vocabulary/filters/field_match_filter.rs rename to interpreter/src/lang/vocabulary/filters/field/field_match_filter.rs index adab26d..0cc1f3c 100644 --- a/interpreter/src/lang/vocabulary/filters/field_match_filter.rs +++ b/interpreter/src/lang/vocabulary/filters/field/field_match_filter.rs @@ -7,7 +7,7 @@ use super::field_filter::{FieldFilterErrorHandling, VariableOrValue}; use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name}; use crate::lang::LanguageDictionary; use crate::lang::TypePrimitive; -use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory}; +use super::{FieldFilterFactory, FieldFilterPredicate}; use crate::lang::{RuntimeMsg, SyntaxError}; use crate::processing::general::Type; use crate::tokens::Token; @@ -32,7 +32,7 @@ impl Display for FieldRegexFilter { } } -impl FilterPredicate for FieldRegexFilter { +impl FieldFilterPredicate for FieldRegexFilter { fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result { let variable = match &self.val { VariableOrValue::Variable(name) => match ctx.variables.get(name)? { @@ -83,38 +83,30 @@ impl FilterPredicate for FieldRegexFilter { //self.regex_cache = None; Ok(()) } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } pub struct FieldRegexFilterFactory; -impl FilterFactory for FieldRegexFilterFactory { - fn is_filter(&self, tokens: &VecDeque<&Token>) -> bool { +impl FieldFilterFactory for FieldRegexFilterFactory { + fn is_filter(&self, tokens: &[Token]) -> bool { let tokens_len = tokens.len(); - (tokens_len >= 3 // .field like variable - && tokens[0].is_dot() - && tokens[1].is_name() - && check_name("matches", tokens[2])) - || (tokens_len >= 4 // .field? like variable OR .field! like variable - && tokens[0].is_dot() - && tokens[1].is_name() - && (tokens[2].is_interrogation() || tokens[2].is_exclamation()) - && check_name("matches", tokens[3])) + (tokens_len >= 1 // .field like variable + && check_name("matches", &tokens[0])) + || (tokens_len >= 2 // .field? like variable OR .field! like variable + && (tokens[0].is_interrogation() || tokens[0].is_exclamation()) + && check_name("matches", &tokens[1])) } fn build_filter( &self, tokens: &mut VecDeque, + field: String, _dict: &LanguageDictionary, ) -> Result { - assert_token_raw(Token::Dot, tokens)?; - let field = assert_token( - |t| match t { - Token::Name(n) => Some(n), - _ => None, - }, - Token::Name("field_name".into()), - tokens, - )?; let error_handling = if tokens[0].is_interrogation() { assert_token_raw(Token::Interrogation, tokens)?; FieldFilterErrorHandling::Ignore @@ -220,11 +212,3 @@ fn build_regex(pattern: &str, flags: u8) -> Result { .ignore_whitespace((flags & (1 << 5)) != 0) .build() } - -pub type FieldRegexFilterStatementFactory = - FilterStatementFactory; - -#[inline(always)] -pub fn field_re_filter() -> FieldRegexFilterStatementFactory { - FieldRegexFilterStatementFactory::new(FieldRegexFilterFactory) -} diff --git a/interpreter/src/lang/vocabulary/filters/field/mod.rs b/interpreter/src/lang/vocabulary/filters/field/mod.rs new file mode 100644 index 0000000..f419ae5 --- /dev/null +++ b/interpreter/src/lang/vocabulary/filters/field/mod.rs @@ -0,0 +1,14 @@ +mod field_filter; +mod field_filter_factory; +mod field_filter_maybe; +mod field_like_filter; +mod field_match_filter; + +pub use field_filter::{ + FieldFilter, FieldFilterErrorHandling, FieldFilterComparisonFactory, +}; +pub use field_filter_maybe::FieldFilterMaybeFactory; +pub use field_like_filter::FieldLikeFilterFactory; +pub use field_match_filter::FieldRegexFilterFactory; + +pub use field_filter_factory::*; diff --git a/interpreter/src/lang/vocabulary/filters/mod.rs b/interpreter/src/lang/vocabulary/filters/mod.rs index fa3dbbf..52cd832 100644 --- a/interpreter/src/lang/vocabulary/filters/mod.rs +++ b/interpreter/src/lang/vocabulary/filters/mod.rs @@ -1,8 +1,5 @@ mod empty_filter; -mod field_filter; -mod field_filter_maybe; -mod field_like_filter; -mod field_match_filter; +pub mod field; mod index_filter; mod nonempty_filter; mod range_filter; @@ -12,19 +9,6 @@ pub(crate) mod utility; pub use empty_filter::{ empty_filter, EmptyFilter, EmptyFilterFactory, EmptyFilterStatementFactory, }; -pub use field_filter::{ - field_filter, FieldFilter, FieldFilterErrorHandling, FieldFilterFactory, - FieldFilterStatementFactory, -}; -pub use field_filter_maybe::{ - field_filter_maybe, FieldFilterMaybeFactory, FieldFilterMaybeStatementFactory, -}; -pub use field_like_filter::{ - field_like_filter, FieldLikeFilterFactory, FieldLikeFilterStatementFactory, -}; -pub use field_match_filter::{ - field_re_filter, FieldRegexFilterFactory, FieldRegexFilterStatementFactory, -}; pub use index_filter::{ index_filter, IndexFilter, IndexFilterFactory, IndexFilterStatementFactory, }; diff --git a/interpreter/src/lang/vocabulary/filters/unique.rs b/interpreter/src/lang/vocabulary/filters/unique.rs index 5fba264..dcb12d9 100644 --- a/interpreter/src/lang/vocabulary/filters/unique.rs +++ b/interpreter/src/lang/vocabulary/filters/unique.rs @@ -1,7 +1,7 @@ use std::collections::{HashSet, VecDeque}; use std::fmt::{Debug, Display, Error, Formatter}; -use super::field_filter::FieldFilterErrorHandling; +use super::field::FieldFilterErrorHandling; use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name}; use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory}; use crate::lang::{LanguageDictionary, TypePrimitive};