diff --git a/components/pluralrules/Cargo.toml b/components/pluralrules/Cargo.toml index 08b2c62b066..1f49c609726 100644 --- a/components/pluralrules/Cargo.toml +++ b/components/pluralrules/Cargo.toml @@ -17,6 +17,7 @@ include = [ [dependencies] icu-locale = { path = "../locale" } icu-data-provider = { path = "../data-provider" } +smallvec = "1.4" [dev-dependencies] criterion = "0.3" diff --git a/components/pluralrules/src/rules/ast.rs b/components/pluralrules/src/rules/ast.rs index 62279dcac13..9e19eedea1a 100644 --- a/components/pluralrules/src/rules/ast.rs +++ b/components/pluralrules/src/rules/ast.rs @@ -7,33 +7,35 @@ //! ``` //! use icu_pluralrules::rules::parse_condition; //! use icu_pluralrules::rules::ast::*; +//! use smallvec::{SmallVec,smallvec}; //! //! let input = "i = 1"; //! //! let ast = parse_condition(input.as_bytes()) //! .expect("Parsing failed."); //! -//! assert_eq!(ast, Condition(Box::new([ -//! AndCondition(Box::new([ +//! assert_eq!(ast, Condition(smallvec![ +//! AndCondition(smallvec![ //! Relation { //! expression: Expression { //! operand: Operand::I, //! modulus: None, //! }, //! operator: Operator::Eq, -//! range_list: RangeList(Box::new([ +//! range_list: RangeList(smallvec![ //! RangeListItem::Value( //! Value(1) //! ) -//! ])) +//! ]) //! } -//! ])) -//! ]))); +//! ]) +//! ])); //! ``` //! //! [`PluralCategory`]: ../enum.PluralCategory.html //! [`parse`]: ../fn.parse.html //! [`test_condition`]: ../fn.test_condition.html +use smallvec::SmallVec; use std::ops::RangeInclusive; /// A complete AST representation of a plural rule. @@ -89,25 +91,26 @@ pub struct Rule { /// ``` /// use icu_pluralrules::rules::ast::*; /// use icu_pluralrules::rules::parse_condition; +/// use smallvec::{SmallVec,smallvec}; /// -/// let condition = Condition(Box::new([ -/// AndCondition(Box::new([Relation { +/// let condition = Condition(smallvec![ +/// AndCondition(smallvec![Relation { /// expression: Expression { /// operand: Operand::I, /// modulus: None, /// }, /// operator: Operator::Eq, -/// range_list: RangeList(Box::new([RangeListItem::Value(Value(5))])), -/// }])), -/// AndCondition(Box::new([Relation { +/// range_list: RangeList(smallvec![RangeListItem::Value(Value(5))]), +/// }]), +/// AndCondition(smallvec![Relation { /// expression: Expression { /// operand: Operand::V, /// modulus: None, /// }, /// operator: Operator::Eq, -/// range_list: RangeList(Box::new([RangeListItem::Value(Value(2))])), -/// }])), -/// ])); +/// range_list: RangeList(smallvec![RangeListItem::Value(Value(2))]), +/// }]), +/// ]); /// /// assert_eq!( /// condition, @@ -116,7 +119,7 @@ pub struct Rule { /// ) /// ``` #[derive(Debug, Clone, PartialEq)] -pub struct Condition(pub Box<[AndCondition]>); +pub struct Condition(pub SmallVec<[AndCondition; 2]>); /// An incomplete AST representation of a plural rule. Comprises a vector of Relations. /// @@ -132,15 +135,16 @@ pub struct Condition(pub Box<[AndCondition]>); /// /// ``` /// use icu_pluralrules::rules::ast::*; +/// use smallvec::{SmallVec,smallvec}; /// -/// AndCondition(Box::new([ +/// AndCondition(smallvec![ /// Relation { /// expression: Expression { /// operand: Operand::I, /// modulus: None, /// }, /// operator: Operator::Eq, -/// range_list: RangeList(Box::new([RangeListItem::Value(Value(5))])), +/// range_list: RangeList(smallvec![RangeListItem::Value(Value(5))]), /// }, /// Relation { /// expression: Expression { @@ -148,13 +152,13 @@ pub struct Condition(pub Box<[AndCondition]>); /// modulus: None, /// }, /// operator: Operator::NotEq, -/// range_list: RangeList(Box::new([RangeListItem::Value(Value(2))])), +/// range_list: RangeList(smallvec![RangeListItem::Value(Value(2))]), /// }, -/// ])); +/// ]); /// /// ``` #[derive(Debug, Clone, PartialEq)] -pub struct AndCondition(pub Box<[Relation]>); +pub struct AndCondition(pub SmallVec<[Relation; 2]>); /// An incomplete AST representation of a plural rule. Comprises an Expression, an Operator, and a RangeList. /// @@ -170,6 +174,7 @@ pub struct AndCondition(pub Box<[Relation]>); /// /// ``` /// use icu_pluralrules::rules::ast::*; +/// use smallvec::{SmallVec,smallvec}; /// /// Relation { /// expression: Expression { @@ -177,7 +182,7 @@ pub struct AndCondition(pub Box<[Relation]>); /// modulus: None, /// }, /// operator: Operator::Eq, -/// range_list: RangeList(Box::new([RangeListItem::Value(Value(3))])), +/// range_list: RangeList(smallvec![RangeListItem::Value(Value(3))]), /// }; /// /// ``` @@ -277,15 +282,16 @@ pub enum Operand { /// /// ``` /// use icu_pluralrules::rules::ast::*; +/// use smallvec::{SmallVec,smallvec}; /// -/// RangeList(Box::new([ +/// RangeList(smallvec![ /// RangeListItem::Value(Value(5)), /// RangeListItem::Value(Value(7)), /// RangeListItem::Value(Value(9)), -/// ])); +/// ]); /// ``` #[derive(Debug, Clone, PartialEq)] -pub struct RangeList(pub Box<[RangeListItem]>); +pub struct RangeList(pub SmallVec<[RangeListItem; 2]>); /// An enum of items that appear in a RangeList: Range or a Value. /// @@ -376,6 +382,8 @@ pub struct Samples { /// /// ``` /// use icu_pluralrules::rules::ast::*; +/// use smallvec::{SmallVec,smallvec}; +/// /// SampleList { /// sample_ranges: Box::new([ /// SampleRange { diff --git a/components/pluralrules/src/rules/mod.rs b/components/pluralrules/src/rules/mod.rs index 327a065c20f..5555f2a1978 100644 --- a/components/pluralrules/src/rules/mod.rs +++ b/components/pluralrules/src/rules/mod.rs @@ -48,6 +48,7 @@ //! When parsed, the resulting [`AST`] will look like this: //! //! ``` +//! use smallvec::{SmallVec,smallvec}; //! use icu_pluralrules::rules::parse_condition; //! use icu_pluralrules::rules::ast::*; //! @@ -55,19 +56,19 @@ //! //! let ast = parse_condition(input.as_bytes()) //! .expect("Parsing failed."); -//! assert_eq!(ast, Condition(Box::new([ -//! AndCondition(Box::new([ +//! assert_eq!(ast, Condition(smallvec![ +//! AndCondition(smallvec![ //! Relation { //! expression: Expression { //! operand: Operand::I, //! modulus: None, //! }, //! operator: Operator::Eq, -//! range_list: RangeList(Box::new([ +//! range_list: RangeList(smallvec![ //! RangeListItem::Value( //! Value(1) //! ) -//! ])) +//! ]) //! }, //! Relation { //! expression: Expression { @@ -75,14 +76,14 @@ //! modulus: None, //! }, //! operator: Operator::Eq, -//! range_list: RangeList(Box::new([ +//! range_list: RangeList(smallvec![ //! RangeListItem::Value( //! Value(0) //! ) -//! ])) -//! }, -//! ])), -//! ]))); +//! ]) +//! } +//! ]), +//! ])); //! ``` //! //! Finally, we can pass this [`AST`] (in fact, just the [`Condition`] node), diff --git a/components/pluralrules/src/rules/parser.rs b/components/pluralrules/src/rules/parser.rs index ad32a4e8d45..d963ee4ebee 100644 --- a/components/pluralrules/src/rules/parser.rs +++ b/components/pluralrules/src/rules/parser.rs @@ -1,5 +1,6 @@ use super::ast; use super::lexer::{Lexer, Token}; +use smallvec::smallvec; use std::iter::Peekable; #[derive(Debug, PartialEq, Eq)] @@ -99,12 +100,12 @@ impl<'p> Parser<'p> { } fn get_condition(&mut self) -> Result { - let mut result = vec![]; + let mut result = smallvec![]; if let Some(cond) = self.get_and_condition()? { result.push(cond); } else { - return Ok(ast::Condition(result.into_boxed_slice())); + return Ok(ast::Condition(result)); } while self.take_if(Token::Or) { @@ -115,12 +116,12 @@ impl<'p> Parser<'p> { } } // If lexer is not done, error? - Ok(ast::Condition(result.into_boxed_slice())) + Ok(ast::Condition(result)) } fn get_and_condition(&mut self) -> Result, ParserError> { if let Some(relation) = self.get_relation()? { - let mut rel = vec![relation]; + let mut rel = smallvec![relation]; while self.take_if(Token::And) { if let Some(relation) = self.get_relation()? { @@ -129,7 +130,7 @@ impl<'p> Parser<'p> { return Err(ParserError::ExpectedRelation); } } - Ok(Some(ast::AndCondition(rel.into_boxed_slice()))) + Ok(Some(ast::AndCondition(rel))) } else { Ok(None) } @@ -168,14 +169,14 @@ impl<'p> Parser<'p> { } fn get_range_list(&mut self) -> Result { - let mut range_list = Vec::with_capacity(1); + let mut range_list = smallvec![]; loop { range_list.push(self.get_range_list_item()?); if !self.take_if(Token::Comma) { break; } } - Ok(ast::RangeList(range_list.into_boxed_slice())) + Ok(ast::RangeList(range_list)) } fn take_if(&mut self, token: Token) -> bool {