1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 // Allow implicit conversion from char16_t* to UnicodeString for this file:
9 // Helpful in toString methods and elsewhere.
10 #define UNISTR_FROM_STRING_EXPLICIT
11 
12 #include "numparse_types.h"
13 #include "numparse_symbols.h"
14 #include "numparse_utils.h"
15 #include "string_segment.h"
16 
17 using namespace icu;
18 using namespace icu::numparse;
19 using namespace icu::numparse::impl;
20 
21 
SymbolMatcher(const UnicodeString & symbolString,unisets::Key key)22 SymbolMatcher::SymbolMatcher(const UnicodeString& symbolString, unisets::Key key) {
23     fUniSet = unisets::get(key);
24     if (fUniSet->contains(symbolString)) {
25         fString.setToBogus();
26     } else {
27         fString = symbolString;
28     }
29 }
30 
getSet() const31 const UnicodeSet* SymbolMatcher::getSet() const {
32     return fUniSet;
33 }
34 
match(StringSegment & segment,ParsedNumber & result,UErrorCode &) const35 bool SymbolMatcher::match(StringSegment& segment, ParsedNumber& result, UErrorCode&) const {
36     // Smoke test first; this matcher might be disabled.
37     if (isDisabled(result)) {
38         return false;
39     }
40 
41     // Test the string first in order to consume trailing chars greedily.
42     int overlap = 0;
43     if (!fString.isEmpty()) {
44         overlap = segment.getCommonPrefixLength(fString);
45         if (overlap == fString.length()) {
46             segment.adjustOffset(fString.length());
47             accept(segment, result);
48             return false;
49         }
50     }
51 
52     int cp = segment.getCodePoint();
53     if (cp != -1 && fUniSet->contains(cp)) {
54         segment.adjustOffset(U16_LENGTH(cp));
55         accept(segment, result);
56         return false;
57     }
58 
59     return overlap == segment.length();
60 }
61 
smokeTest(const StringSegment & segment) const62 bool SymbolMatcher::smokeTest(const StringSegment& segment) const {
63     return segment.startsWith(*fUniSet) || segment.startsWith(fString);
64 }
65 
toString() const66 UnicodeString SymbolMatcher::toString() const {
67     // TODO: Customize output for each symbol
68     return u"<Symbol>";
69 }
70 
71 
IgnorablesMatcher(parse_flags_t parseFlags)72 IgnorablesMatcher::IgnorablesMatcher(parse_flags_t parseFlags) :
73         SymbolMatcher(
74             {},
75             (0 != (parseFlags & PARSE_FLAG_STRICT_IGNORABLES)) ?
76                 unisets::STRICT_IGNORABLES :
77                 unisets::DEFAULT_IGNORABLES) {
78 }
79 
isFlexible() const80 bool IgnorablesMatcher::isFlexible() const {
81     return true;
82 }
83 
toString() const84 UnicodeString IgnorablesMatcher::toString() const {
85     return u"<Ignorables>";
86 }
87 
isDisabled(const ParsedNumber &) const88 bool IgnorablesMatcher::isDisabled(const ParsedNumber&) const {
89     return false;
90 }
91 
accept(StringSegment &,ParsedNumber &) const92 void IgnorablesMatcher::accept(StringSegment&, ParsedNumber&) const {
93     // No-op
94 }
95 
96 
InfinityMatcher(const DecimalFormatSymbols & dfs)97 InfinityMatcher::InfinityMatcher(const DecimalFormatSymbols& dfs)
98         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), unisets::INFINITY_SIGN) {
99 }
100 
isDisabled(const ParsedNumber & result) const101 bool InfinityMatcher::isDisabled(const ParsedNumber& result) const {
102     return 0 != (result.flags & FLAG_INFINITY);
103 }
104 
accept(StringSegment & segment,ParsedNumber & result) const105 void InfinityMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
106     result.flags |= FLAG_INFINITY;
107     result.setCharsConsumed(segment);
108 }
109 
110 
MinusSignMatcher(const DecimalFormatSymbols & dfs,bool allowTrailing)111 MinusSignMatcher::MinusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing)
112         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol), unisets::MINUS_SIGN),
113           fAllowTrailing(allowTrailing) {
114 }
115 
isDisabled(const ParsedNumber & result) const116 bool MinusSignMatcher::isDisabled(const ParsedNumber& result) const {
117     return !fAllowTrailing && result.seenNumber();
118 }
119 
accept(StringSegment & segment,ParsedNumber & result) const120 void MinusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
121     result.flags |= FLAG_NEGATIVE;
122     result.setCharsConsumed(segment);
123 }
124 
125 
NanMatcher(const DecimalFormatSymbols & dfs)126 NanMatcher::NanMatcher(const DecimalFormatSymbols& dfs)
127         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), unisets::EMPTY) {
128 }
129 
isDisabled(const ParsedNumber & result) const130 bool NanMatcher::isDisabled(const ParsedNumber& result) const {
131     return result.seenNumber();
132 }
133 
accept(StringSegment & segment,ParsedNumber & result) const134 void NanMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
135     result.flags |= FLAG_NAN;
136     result.setCharsConsumed(segment);
137 }
138 
139 
PaddingMatcher(const UnicodeString & padString)140 PaddingMatcher::PaddingMatcher(const UnicodeString& padString)
141         : SymbolMatcher(padString, unisets::EMPTY) {}
142 
isFlexible() const143 bool PaddingMatcher::isFlexible() const {
144     return true;
145 }
146 
isDisabled(const ParsedNumber &) const147 bool PaddingMatcher::isDisabled(const ParsedNumber&) const {
148     return false;
149 }
150 
accept(StringSegment &,ParsedNumber &) const151 void PaddingMatcher::accept(StringSegment&, ParsedNumber&) const {
152     // No-op
153 }
154 
155 
PercentMatcher(const DecimalFormatSymbols & dfs)156 PercentMatcher::PercentMatcher(const DecimalFormatSymbols& dfs)
157         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPercentSymbol), unisets::PERCENT_SIGN) {
158 }
159 
isDisabled(const ParsedNumber & result) const160 bool PercentMatcher::isDisabled(const ParsedNumber& result) const {
161     return 0 != (result.flags & FLAG_PERCENT);
162 }
163 
accept(StringSegment & segment,ParsedNumber & result) const164 void PercentMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
165     result.flags |= FLAG_PERCENT;
166     result.setCharsConsumed(segment);
167 }
168 
169 
PermilleMatcher(const DecimalFormatSymbols & dfs)170 PermilleMatcher::PermilleMatcher(const DecimalFormatSymbols& dfs)
171         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol), unisets::PERMILLE_SIGN) {
172 }
173 
isDisabled(const ParsedNumber & result) const174 bool PermilleMatcher::isDisabled(const ParsedNumber& result) const {
175     return 0 != (result.flags & FLAG_PERMILLE);
176 }
177 
accept(StringSegment & segment,ParsedNumber & result) const178 void PermilleMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
179     result.flags |= FLAG_PERMILLE;
180     result.setCharsConsumed(segment);
181 }
182 
183 
PlusSignMatcher(const DecimalFormatSymbols & dfs,bool allowTrailing)184 PlusSignMatcher::PlusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing)
185         : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol), unisets::PLUS_SIGN),
186           fAllowTrailing(allowTrailing) {
187 }
188 
isDisabled(const ParsedNumber & result) const189 bool PlusSignMatcher::isDisabled(const ParsedNumber& result) const {
190     return !fAllowTrailing && result.seenNumber();
191 }
192 
accept(StringSegment & segment,ParsedNumber & result) const193 void PlusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
194     result.setCharsConsumed(segment);
195 }
196 
197 
198 #endif /* #if !UCONFIG_NO_FORMATTING */
199