1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
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
12 #include "numparse_types.h"
13 #include "numparse_scientific.h"
14 #include "static_unicode_sets.h"
17 using namespace icu::numparse
;
18 using namespace icu::numparse::impl
;
23 inline const UnicodeSet
& minusSignSet() {
24 return *unisets::get(unisets::MINUS_SIGN
);
27 inline const UnicodeSet
& plusSignSet() {
28 return *unisets::get(unisets::PLUS_SIGN
);
34 ScientificMatcher::ScientificMatcher(const DecimalFormatSymbols
& dfs
, const Grouper
& grouper
)
35 : fExponentSeparatorString(dfs
.getConstSymbol(DecimalFormatSymbols::kExponentialSymbol
)),
36 fExponentMatcher(dfs
, grouper
, PARSE_FLAG_INTEGER_ONLY
| PARSE_FLAG_GROUPING_DISABLED
) {
38 const UnicodeString
& minusSign
= dfs
.getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol
);
39 if (minusSignSet().contains(minusSign
)) {
40 fCustomMinusSign
.setToBogus();
42 fCustomMinusSign
= minusSign
;
45 const UnicodeString
& plusSign
= dfs
.getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol
);
46 if (plusSignSet().contains(plusSign
)) {
47 fCustomPlusSign
.setToBogus();
49 fCustomPlusSign
= plusSign
;
53 bool ScientificMatcher::match(StringSegment
& segment
, ParsedNumber
& result
, UErrorCode
& status
) const {
54 // Only accept scientific notation after the mantissa.
55 if (!result
.seenNumber()) {
59 // First match the scientific separator, and then match another number after it.
60 // NOTE: This is guarded by the smoke test; no need to check fExponentSeparatorString length again.
61 int overlap1
= segment
.getCommonPrefixLength(fExponentSeparatorString
);
62 if (overlap1
== fExponentSeparatorString
.length()) {
63 // Full exponent separator match.
65 // First attempt to get a code point, returning true if we can't get one.
66 if (segment
.length() == overlap1
) {
69 segment
.adjustOffset(overlap1
);
71 // Allow a sign, and then try to match digits.
72 int8_t exponentSign
= 1;
73 if (segment
.startsWith(minusSignSet())) {
75 segment
.adjustOffsetByCodePoint();
76 } else if (segment
.startsWith(plusSignSet())) {
77 segment
.adjustOffsetByCodePoint();
78 } else if (segment
.startsWith(fCustomMinusSign
)) {
79 // Note: call site is guarded with startsWith, which returns false on empty string
80 int32_t overlap2
= segment
.getCommonPrefixLength(fCustomMinusSign
);
81 if (overlap2
!= fCustomMinusSign
.length()) {
82 // Partial custom sign match; un-match the exponent separator.
83 segment
.adjustOffset(-overlap1
);
87 segment
.adjustOffset(overlap2
);
88 } else if (segment
.startsWith(fCustomPlusSign
)) {
89 // Note: call site is guarded with startsWith, which returns false on empty string
90 int32_t overlap2
= segment
.getCommonPrefixLength(fCustomPlusSign
);
91 if (overlap2
!= fCustomPlusSign
.length()) {
92 // Partial custom sign match; un-match the exponent separator.
93 segment
.adjustOffset(-overlap1
);
96 segment
.adjustOffset(overlap2
);
99 // We are supposed to accept E0 after NaN, so we need to make sure result.quantity is available.
100 bool wasBogus
= result
.quantity
.bogus
;
101 result
.quantity
.bogus
= false;
102 int digitsOffset
= segment
.getOffset();
103 bool digitsReturnValue
= fExponentMatcher
.match(segment
, result
, exponentSign
, status
);
104 result
.quantity
.bogus
= wasBogus
;
106 if (segment
.getOffset() != digitsOffset
) {
107 // At least one exponent digit was matched.
108 result
.flags
|= FLAG_HAS_EXPONENT
;
110 // No exponent digits were matched; un-match the exponent separator.
111 segment
.adjustOffset(-overlap1
);
113 return digitsReturnValue
;
115 } else if (overlap1
== segment
.length()) {
116 // Partial exponent separator match
124 bool ScientificMatcher::smokeTest(const StringSegment
& segment
) const {
125 return segment
.startsWith(fExponentSeparatorString
);
128 UnicodeString
ScientificMatcher::toString() const {
129 return u
"<Scientific>";
133 #endif /* #if !UCONFIG_NO_FORMATTING */