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