| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ******************************************************************************* |
| * Copyright (C) 2007-2016, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| * |
| * File PLURRULE_IMPL.H |
| * |
| ******************************************************************************* |
| */ |
| |
| |
| #ifndef PLURRULE_IMPL |
| #define PLURRULE_IMPL |
| |
| // Internal definitions for the PluralRules implementation. |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #include "unicode/format.h" |
| #include "unicode/locid.h" |
| #include "unicode/parseerr.h" |
| #include "unicode/strenum.h" |
| #include "unicode/ures.h" |
| #include "uvector.h" |
| #include "hash.h" |
| #include "uassert.h" |
| |
| /** |
| * A FixedDecimal version of UPLRULES_NO_UNIQUE_VALUE used in PluralRulesTest |
| * for parsing of samples. |
| */ |
| #define UPLRULES_NO_UNIQUE_VALUE_DECIMAL (FixedDecimal((double)-0.00123456777)) |
| |
| class PluralRulesTest; |
| |
| U_NAMESPACE_BEGIN |
| |
| class AndConstraint; |
| class RuleChain; |
| class DigitInterval; |
| class PluralRules; |
| class VisibleDigits; |
| |
| namespace pluralimpl { |
| |
| // TODO: Remove this and replace with u"" literals. Was for EBCDIC compatibility. |
| |
| static const UChar DOT = ((UChar) 0x002E); |
| static const UChar SINGLE_QUOTE = ((UChar) 0x0027); |
| static const UChar SLASH = ((UChar) 0x002F); |
| static const UChar BACKSLASH = ((UChar) 0x005C); |
| static const UChar SPACE = ((UChar) 0x0020); |
| static const UChar EXCLAMATION = ((UChar) 0x0021); |
| static const UChar QUOTATION_MARK = ((UChar) 0x0022); |
| static const UChar NUMBER_SIGN = ((UChar) 0x0023); |
| static const UChar PERCENT_SIGN = ((UChar) 0x0025); |
| static const UChar ASTERISK = ((UChar) 0x002A); |
| static const UChar COMMA = ((UChar) 0x002C); |
| static const UChar HYPHEN = ((UChar) 0x002D); |
| static const UChar U_ZERO = ((UChar) 0x0030); |
| static const UChar U_ONE = ((UChar) 0x0031); |
| static const UChar U_TWO = ((UChar) 0x0032); |
| static const UChar U_THREE = ((UChar) 0x0033); |
| static const UChar U_FOUR = ((UChar) 0x0034); |
| static const UChar U_FIVE = ((UChar) 0x0035); |
| static const UChar U_SIX = ((UChar) 0x0036); |
| static const UChar U_SEVEN = ((UChar) 0x0037); |
| static const UChar U_EIGHT = ((UChar) 0x0038); |
| static const UChar U_NINE = ((UChar) 0x0039); |
| static const UChar COLON = ((UChar) 0x003A); |
| static const UChar SEMI_COLON = ((UChar) 0x003B); |
| static const UChar EQUALS = ((UChar) 0x003D); |
| static const UChar AT = ((UChar) 0x0040); |
| static const UChar CAP_A = ((UChar) 0x0041); |
| static const UChar CAP_B = ((UChar) 0x0042); |
| static const UChar CAP_R = ((UChar) 0x0052); |
| static const UChar CAP_Z = ((UChar) 0x005A); |
| static const UChar LOWLINE = ((UChar) 0x005F); |
| static const UChar LEFTBRACE = ((UChar) 0x007B); |
| static const UChar RIGHTBRACE = ((UChar) 0x007D); |
| static const UChar TILDE = ((UChar) 0x007E); |
| static const UChar ELLIPSIS = ((UChar) 0x2026); |
| |
| static const UChar LOW_A = ((UChar) 0x0061); |
| static const UChar LOW_B = ((UChar) 0x0062); |
| static const UChar LOW_C = ((UChar) 0x0063); |
| static const UChar LOW_D = ((UChar) 0x0064); |
| static const UChar LOW_E = ((UChar) 0x0065); |
| static const UChar LOW_F = ((UChar) 0x0066); |
| static const UChar LOW_G = ((UChar) 0x0067); |
| static const UChar LOW_H = ((UChar) 0x0068); |
| static const UChar LOW_I = ((UChar) 0x0069); |
| static const UChar LOW_J = ((UChar) 0x006a); |
| static const UChar LOW_K = ((UChar) 0x006B); |
| static const UChar LOW_L = ((UChar) 0x006C); |
| static const UChar LOW_M = ((UChar) 0x006D); |
| static const UChar LOW_N = ((UChar) 0x006E); |
| static const UChar LOW_O = ((UChar) 0x006F); |
| static const UChar LOW_P = ((UChar) 0x0070); |
| static const UChar LOW_Q = ((UChar) 0x0071); |
| static const UChar LOW_R = ((UChar) 0x0072); |
| static const UChar LOW_S = ((UChar) 0x0073); |
| static const UChar LOW_T = ((UChar) 0x0074); |
| static const UChar LOW_U = ((UChar) 0x0075); |
| static const UChar LOW_V = ((UChar) 0x0076); |
| static const UChar LOW_W = ((UChar) 0x0077); |
| static const UChar LOW_Y = ((UChar) 0x0079); |
| static const UChar LOW_Z = ((UChar) 0x007A); |
| |
| } |
| |
| |
| static const int32_t PLURAL_RANGE_HIGH = 0x7fffffff; |
| |
| enum tokenType { |
| none, |
| tNumber, |
| tComma, |
| tSemiColon, |
| tSpace, |
| tColon, |
| tAt, // '@' |
| tDot, |
| tDot2, |
| tEllipsis, |
| tKeyword, |
| tAnd, |
| tOr, |
| tMod, // 'mod' or '%' |
| tNot, // 'not' only. |
| tIn, // 'in' only. |
| tEqual, // '=' only. |
| tNotEqual, // '!=' |
| tTilde, |
| tWithin, |
| tIs, |
| tVariableN, |
| tVariableI, |
| tVariableF, |
| tVariableV, |
| tVariableT, |
| tVariableE, |
| tDecimal, |
| tInteger, |
| tEOF |
| }; |
| |
| |
| class PluralRuleParser: public UMemory { |
| public: |
| PluralRuleParser(); |
| virtual ~PluralRuleParser(); |
| |
| void parse(const UnicodeString &rules, PluralRules *dest, UErrorCode &status); |
| void getNextToken(UErrorCode &status); |
| void checkSyntax(UErrorCode &status); |
| static int32_t getNumberValue(const UnicodeString &token); |
| |
| private: |
| static tokenType getKeyType(const UnicodeString& token, tokenType type); |
| static tokenType charType(UChar ch); |
| static UBool isValidKeyword(const UnicodeString& token); |
| |
| const UnicodeString *ruleSrc; // The rules string. |
| int32_t ruleIndex; // String index in the input rules, the current parse position. |
| UnicodeString token; // Token most recently scanned. |
| tokenType type; |
| tokenType prevType; |
| |
| // The items currently being parsed & built. |
| // Note: currentChain may not be the last RuleChain in the |
| // list because the "other" chain is forced to the end. |
| AndConstraint *curAndConstraint; |
| RuleChain *currentChain; |
| |
| int32_t rangeLowIdx; // Indices in the UVector of ranges of the |
| int32_t rangeHiIdx; // low and hi values currently being parsed. |
| |
| enum EParseState { |
| kKeyword, |
| kExpr, |
| kValue, |
| kRangeList, |
| kSamples |
| }; |
| }; |
| |
| enum PluralOperand { |
| /** |
| * The double value of the entire number. |
| */ |
| PLURAL_OPERAND_N, |
| |
| /** |
| * The integer value, with the fraction digits truncated off. |
| */ |
| PLURAL_OPERAND_I, |
| |
| /** |
| * All visible fraction digits as an integer, including trailing zeros. |
| */ |
| PLURAL_OPERAND_F, |
| |
| /** |
| * Visible fraction digits as an integer, not including trailing zeros. |
| */ |
| PLURAL_OPERAND_T, |
| |
| /** |
| * Number of visible fraction digits. |
| */ |
| PLURAL_OPERAND_V, |
| |
| /** |
| * Number of visible fraction digits, not including trailing zeros. |
| */ |
| PLURAL_OPERAND_W, |
| |
| /** |
| * Suppressed exponent for compact notation (exponent needed in |
| * scientific notation with compact notation to approximate i). |
| */ |
| PLURAL_OPERAND_E, |
| |
| /** |
| * THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC. |
| * |
| * <p>Returns the integer value, but will fail if the number has fraction digits. |
| * That is, using "j" instead of "i" is like implicitly adding "v is 0". |
| * |
| * <p>For example, "j is 3" is equivalent to "i is 3 and v is 0": it matches |
| * "3" but not "3.1" or "3.0". |
| */ |
| PLURAL_OPERAND_J |
| }; |
| |
| /** |
| * Converts from the tokenType enum to PluralOperand. Asserts that the given |
| * tokenType can be mapped to a PluralOperand. |
| */ |
| PluralOperand tokenTypeToPluralOperand(tokenType tt); |
| |
| /** |
| * An interface to FixedDecimal, allowing for other implementations. |
| * @internal |
| */ |
| class U_I18N_API IFixedDecimal { |
| public: |
| virtual ~IFixedDecimal(); |
| |
| /** |
| * Returns the value corresponding to the specified operand (n, i, f, t, v, or w). |
| * If the operand is 'n', returns a double; otherwise, returns an integer. |
| */ |
| virtual double getPluralOperand(PluralOperand operand) const = 0; |
| |
| virtual bool isNaN() const = 0; |
| |
| virtual bool isInfinite() const = 0; |
| |
| /** Whether the number has no nonzero fraction digits. */ |
| virtual bool hasIntegerValue() const = 0; |
| }; |
| |
| /** |
| * class FixedDecimal serves to communicate the properties |
| * of a formatted number from a decimal formatter to PluralRules::select() |
| * |
| * see DecimalFormat::getFixedDecimal() |
| * @internal |
| */ |
| class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject { |
| public: |
| /** |
| * @param n the number, e.g. 12.345 |
| * @param v The number of visible fraction digits, e.g. 3 |
| * @param f The fraction digits, e.g. 345 |
| * @param e The exponent, e.g. 7 in 1.2e7 (for compact/scientific) |
| */ |
| FixedDecimal(double n, int32_t v, int64_t f, int32_t e); |
| FixedDecimal(double n, int32_t v, int64_t f); |
| FixedDecimal(double n, int32_t); |
| explicit FixedDecimal(double n); |
| FixedDecimal(); |
| ~FixedDecimal() U_OVERRIDE; |
| FixedDecimal(const UnicodeString &s, UErrorCode &ec); |
| FixedDecimal(const FixedDecimal &other); |
| |
| static FixedDecimal createWithExponent(double n, int32_t v, int32_t e); |
| |
| double getPluralOperand(PluralOperand operand) const U_OVERRIDE; |
| bool isNaN() const U_OVERRIDE; |
| bool isInfinite() const U_OVERRIDE; |
| bool hasIntegerValue() const U_OVERRIDE; |
| |
| bool isNanOrInfinity() const; // used in decimfmtimpl.cpp |
| |
| int32_t getVisibleFractionDigitCount() const; |
| |
| void init(double n, int32_t v, int64_t f, int32_t e); |
| void init(double n, int32_t v, int64_t f); |
| void init(double n); |
| UBool quickInit(double n); // Try a fast-path only initialization, |
| // return true if successful. |
| void adjustForMinFractionDigits(int32_t min); |
| static int64_t getFractionalDigits(double n, int32_t v); |
| static int32_t decimals(double n); |
| |
| bool operator==(const FixedDecimal &other) const; |
| |
| UnicodeString toString() const; |
| |
| double source; |
| int32_t visibleDecimalDigitCount; |
| int64_t decimalDigits; |
| int64_t decimalDigitsWithoutTrailingZeros; |
| int64_t intValue; |
| int32_t exponent; |
| UBool _hasIntegerValue; |
| UBool isNegative; |
| UBool _isNaN; |
| UBool _isInfinite; |
| }; |
| |
| class AndConstraint : public UMemory { |
| public: |
| typedef enum RuleOp { |
| NONE, |
| MOD |
| } RuleOp; |
| RuleOp op = AndConstraint::NONE; |
| int32_t opNum = -1; // for mod expressions, the right operand of the mod. |
| int32_t value = -1; // valid for 'is' rules only. |
| UVector32 *rangeList = nullptr; // for 'in', 'within' rules. Null otherwise. |
| UBool negated = false; // true for negated rules. |
| UBool integerOnly = false; // true for 'within' rules. |
| tokenType digitsType = none; // n | i | v | f constraint. |
| AndConstraint *next = nullptr; |
| // Internal error status, used for errors that occur during the copy constructor. |
| UErrorCode fInternalStatus = U_ZERO_ERROR; |
| |
| AndConstraint() = default; |
| AndConstraint(const AndConstraint& other); |
| virtual ~AndConstraint(); |
| AndConstraint* add(UErrorCode& status); |
| // UBool isFulfilled(double number); |
| UBool isFulfilled(const IFixedDecimal &number); |
| }; |
| |
| class OrConstraint : public UMemory { |
| public: |
| AndConstraint *childNode = nullptr; |
| OrConstraint *next = nullptr; |
| // Internal error status, used for errors that occur during the copy constructor. |
| UErrorCode fInternalStatus = U_ZERO_ERROR; |
| |
| OrConstraint() = default; |
| OrConstraint(const OrConstraint& other); |
| virtual ~OrConstraint(); |
| AndConstraint* add(UErrorCode& status); |
| // UBool isFulfilled(double number); |
| UBool isFulfilled(const IFixedDecimal &number); |
| }; |
| |
| class RuleChain : public UMemory { |
| public: |
| UnicodeString fKeyword; |
| RuleChain *fNext = nullptr; |
| OrConstraint *ruleHeader = nullptr; |
| UnicodeString fDecimalSamples; // Samples strings from rule source |
| UnicodeString fIntegerSamples; // without @decimal or @integer, otherwise unprocessed. |
| UBool fDecimalSamplesUnbounded = false; |
| UBool fIntegerSamplesUnbounded = false; |
| // Internal error status, used for errors that occur during the copy constructor. |
| UErrorCode fInternalStatus = U_ZERO_ERROR; |
| |
| RuleChain() = default; |
| RuleChain(const RuleChain& other); |
| virtual ~RuleChain(); |
| |
| UnicodeString select(const IFixedDecimal &number) const; |
| void dumpRules(UnicodeString& result); |
| UErrorCode getKeywords(int32_t maxArraySize, UnicodeString *keywords, int32_t& arraySize) const; |
| UBool isKeyword(const UnicodeString& keyword) const; |
| }; |
| |
| class PluralKeywordEnumeration : public StringEnumeration { |
| public: |
| PluralKeywordEnumeration(RuleChain *header, UErrorCode& status); |
| virtual ~PluralKeywordEnumeration(); |
| static UClassID U_EXPORT2 getStaticClassID(void); |
| virtual UClassID getDynamicClassID(void) const; |
| virtual const UnicodeString* snext(UErrorCode& status); |
| virtual void reset(UErrorCode& status); |
| virtual int32_t count(UErrorCode& status) const; |
| private: |
| int32_t pos; |
| UVector fKeywordNames; |
| }; |
| |
| |
| class U_I18N_API PluralAvailableLocalesEnumeration: public StringEnumeration { |
| public: |
| PluralAvailableLocalesEnumeration(UErrorCode &status); |
| virtual ~PluralAvailableLocalesEnumeration(); |
| virtual const char* next(int32_t *resultLength, UErrorCode& status); |
| virtual void reset(UErrorCode& status); |
| virtual int32_t count(UErrorCode& status) const; |
| private: |
| UErrorCode fOpenStatus; |
| UResourceBundle *fLocales = nullptr; |
| UResourceBundle *fRes = nullptr; |
| }; |
| |
| U_NAMESPACE_END |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |
| |
| #endif // _PLURRULE_IMPL |
| //eof |