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
17using namespace icu;
18using namespace icu::numparse;
19using namespace icu::numparse::impl;
20
21
22SymbolMatcher::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
31const UnicodeSet* SymbolMatcher::getSet() const {
32 return fUniSet;
33}
34
35bool 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
62bool SymbolMatcher::smokeTest(const StringSegment& segment) const {
63 return segment.startsWith(*fUniSet) || segment.startsWith(fString);
64}
65
66UnicodeString SymbolMatcher::toString() const {
67 // TODO: Customize output for each symbol
68 return u"<Symbol>";
69}
70
71
72IgnorablesMatcher::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
80bool IgnorablesMatcher::isFlexible() const {
81 return true;
82}
83
84UnicodeString IgnorablesMatcher::toString() const {
85 return u"<Ignorables>";
86}
87
88bool IgnorablesMatcher::isDisabled(const ParsedNumber&) const {
89 return false;
90}
91
92void IgnorablesMatcher::accept(StringSegment&, ParsedNumber&) const {
93 // No-op
94}
95
96
97InfinityMatcher::InfinityMatcher(const DecimalFormatSymbols& dfs)
98 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kInfinitySymbol), unisets::INFINITY_SIGN) {
99}
100
101bool InfinityMatcher::isDisabled(const ParsedNumber& result) const {
102 return 0 != (result.flags & FLAG_INFINITY);
103}
104
105void InfinityMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
106 result.flags |= FLAG_INFINITY;
107 result.setCharsConsumed(segment);
108}
109
110
111MinusSignMatcher::MinusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing)
112 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol), unisets::MINUS_SIGN),
113 fAllowTrailing(allowTrailing) {
114}
115
116bool MinusSignMatcher::isDisabled(const ParsedNumber& result) const {
117 return !fAllowTrailing && result.seenNumber();
118}
119
120void MinusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
121 result.flags |= FLAG_NEGATIVE;
122 result.setCharsConsumed(segment);
123}
124
125
126NanMatcher::NanMatcher(const DecimalFormatSymbols& dfs)
127 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kNaNSymbol), unisets::EMPTY) {
128}
129
130bool NanMatcher::isDisabled(const ParsedNumber& result) const {
131 return result.seenNumber();
132}
133
134void NanMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
135 result.flags |= FLAG_NAN;
136 result.setCharsConsumed(segment);
137}
138
139
140PaddingMatcher::PaddingMatcher(const UnicodeString& padString)
141 : SymbolMatcher(padString, unisets::EMPTY) {}
142
143bool PaddingMatcher::isFlexible() const {
144 return true;
145}
146
147bool PaddingMatcher::isDisabled(const ParsedNumber&) const {
148 return false;
149}
150
151void PaddingMatcher::accept(StringSegment&, ParsedNumber&) const {
152 // No-op
153}
154
155
156PercentMatcher::PercentMatcher(const DecimalFormatSymbols& dfs)
157 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPercentSymbol), unisets::PERCENT_SIGN) {
158}
159
160bool PercentMatcher::isDisabled(const ParsedNumber& result) const {
161 return 0 != (result.flags & FLAG_PERCENT);
162}
163
164void PercentMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
165 result.flags |= FLAG_PERCENT;
166 result.setCharsConsumed(segment);
167}
168
169
170PermilleMatcher::PermilleMatcher(const DecimalFormatSymbols& dfs)
171 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPerMillSymbol), unisets::PERMILLE_SIGN) {
172}
173
174bool PermilleMatcher::isDisabled(const ParsedNumber& result) const {
175 return 0 != (result.flags & FLAG_PERMILLE);
176}
177
178void PermilleMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
179 result.flags |= FLAG_PERMILLE;
180 result.setCharsConsumed(segment);
181}
182
183
184PlusSignMatcher::PlusSignMatcher(const DecimalFormatSymbols& dfs, bool allowTrailing)
185 : SymbolMatcher(dfs.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol), unisets::PLUS_SIGN),
186 fAllowTrailing(allowTrailing) {
187}
188
189bool PlusSignMatcher::isDisabled(const ParsedNumber& result) const {
190 return !fAllowTrailing && result.seenNumber();
191}
192
193void PlusSignMatcher::accept(StringSegment& segment, ParsedNumber& result) const {
194 result.setCharsConsumed(segment);
195}
196
197
198#endif /* #if !UCONFIG_NO_FORMATTING */
199