icu4c/source/tools/ctestfw/release
icu4c/source/tools/ctestfw/x64
icu4c/source/tools/ctestfw/x86
+icu4c/source/tools/escapesrc/*.d
+icu4c/source/tools/escapesrc/Makefile
icu4c/source/tools/genbrk/*.d
icu4c/source/tools/genbrk/*.o
icu4c/source/tools/genbrk/*.pdb
#endif
UBool
-NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result) const
+NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, int32_t nonNumericalExecutedRuleMask, Formattable& result) const
{
// try matching each rule in the rule set against the text being
// parsed. Whichever one matches the most characters is the one
#endif
// Try each of the negative rules, fraction rules, infinity rules and NaN rules
for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
- if (nonNumericalRules[i]) {
+ if (nonNumericalRules[i] && ((nonNumericalExecutedRuleMask >> i) & 1) == 0) {
+ // Mark this rule as being executed so that we don't try to execute it again.
+ nonNumericalExecutedRuleMask |= 1 << i;
+
Formattable tempResult;
- UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, tempResult);
+ UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, tempResult);
if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
result = tempResult;
highWaterMark = workingPos;
continue;
}
Formattable tempResult;
- UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, tempResult);
+ UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, tempResult);
if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
result = tempResult;
highWaterMark = workingPos;
void format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
void format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
- UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result) const;
+ UBool parse(const UnicodeString& text, ParsePosition& pos, double upperBound, int32_t nonNumericalExecutedRuleMask, Formattable& result) const;
void appendRules(UnicodeString& result) const; // toString
ParsePosition& parsePosition,
UBool isFractionRule,
double upperBound,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& resVal) const
{
// internally we operate on a copy of the string being parsed
temp.setTo(ruleText, sub1Pos, sub2Pos - sub1Pos);
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
temp, pp, sub1,
+ nonNumericalExecutedRuleMask,
upperBound);
// if we got a successful match (or were trying to match a
temp.setTo(ruleText, sub2Pos, ruleText.length() - sub2Pos);
partialResult = matchToDelimiter(workText2, 0, partialResult,
temp, pp2, sub2,
+ nonNumericalExecutedRuleMask,
upperBound);
// if we got a successful match on this second
const UnicodeString& delimiter,
ParsePosition& pp,
const NFSubstitution* sub,
+ int32_t nonNumericalExecutedRuleMask,
double upperBound) const
{
UErrorCode status = U_ZERO_ERROR;
#else
formatter->isLenient(),
#endif
+ nonNumericalExecutedRuleMask,
result);
// if the substitution could match all the text up to
#else
formatter->isLenient(),
#endif
+ nonNumericalExecutedRuleMask,
result);
if (success && (tempPP.getIndex() != 0)) {
// if there's a successful match (or it's a null
ParsePosition& pos,
UBool isFractional,
double upperBound,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const;
UBool shouldRollBack(int64_t number) const;
int32_t indexOfAnyRulePrefix() const;
double matchToDelimiter(const UnicodeString& text, int32_t startPos, double baseValue,
const UnicodeString& delimiter, ParsePosition& pp, const NFSubstitution* sub,
+ int32_t nonNumericalExecutedRuleMask,
double upperBound) const;
void stripPrefix(UnicodeString& text, const UnicodeString& prefix, ParsePosition& pp) const;
double baseValue,
double upperBound,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const;
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const {
double baseValue,
double upperBound,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const;
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue + oldRuleValue; }
double baseValue,
double upperBound,
UBool /*lenientParse*/,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const;
virtual double composeRuleValue(double newRuleValue, double oldRuleValue) const { return newRuleValue / oldRuleValue; }
double baseValue,
double upperBound,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const
{
#ifdef RBNF_DEBUG
// on), then also try parsing the text using a default-
// constructed NumberFormat
if (ruleSet != NULL) {
- ruleSet->parse(text, parsePosition, upperBound, result);
+ ruleSet->parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask, result);
if (lenientParse && !ruleSet->isFractionRuleSet() && parsePosition.getIndex() == 0) {
UErrorCode status = U_ZERO_ERROR;
NumberFormat* fmt = NumberFormat::createInstance(status);
double baseValue,
double upperBound,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const
{
// if this isn't a >>> substitution, we can just use the
// inherited parse() routine to do the parsing
if (ruleToUse == NULL) {
- return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, result);
+ return NFSubstitution::doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask, result);
// but if it IS a >>> substitution, we have to do it here: we
// use the specific rule's doParse() method, and then we have to
// do some of the other work of NFRuleSet.parse()
} else {
- ruleToUse->doParse(text, parsePosition, FALSE, upperBound, result);
+ ruleToUse->doParse(text, parsePosition, FALSE, upperBound, nonNumericalExecutedRuleMask, result);
if (parsePosition.getIndex() != 0) {
UErrorCode status = U_ZERO_ERROR;
double baseValue,
double /*upperBound*/,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& resVal) const
{
// if we're not in byDigits mode, we can just use the inherited
// doParse()
if (!byDigits) {
- return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal);
+ return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask, resVal);
// if we ARE in byDigits mode, parse the text one digit at a time
// using this substitution's owning rule set (we do this by setting
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
Formattable temp;
- getRuleSet()->parse(workText, workPos, 10, temp);
+ getRuleSet()->parse(workText, workPos, 10, nonNumericalExecutedRuleMask, temp);
UErrorCode status = U_ZERO_ERROR;
digit = temp.getLong(status);
// digit = temp.getType() == Formattable::kLong ?
double baseValue,
double upperBound,
UBool /*lenientParse*/,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const
{
// we don't have to do anything special to do the parsing here,
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
- getRuleSet()->parse(workText, workPos, 1, temp); // parse zero or nothing at all
+ getRuleSet()->parse(workText, workPos, 1, nonNumericalExecutedRuleMask, temp); // parse zero or nothing at all
if (workPos.getIndex() == 0) {
// we failed, either there were no more zeros, or the number was formatted with digits
// either way, we're done
}
// we've parsed off the zeros, now let's parse the rest from our current position
- NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, result);
+ NFSubstitution::doParse(workText, parsePosition, withZeros ? 1 : baseValue, upperBound, FALSE, nonNumericalExecutedRuleMask, result);
if (withZeros) {
// any base value will do in this case. is there a way to
double baseValue,
double upperBound,
UBool lenientParse,
+ int32_t nonNumericalExecutedRuleMask,
Formattable& result) const;
/**
ParsePosition working_pp(0);
Formattable working_result;
- rp->parse(workingText, working_pp, kMaxDouble, working_result);
+ rp->parse(workingText, working_pp, kMaxDouble, 0, working_result);
if (working_pp.getIndex() > high_pp.getIndex()) {
high_pp = working_pp;
high_result = working_result;
TESTCASE(23, TestVariableDecimalPoint);
TESTCASE(24, TestLargeNumbers);
TESTCASE(25, TestCompactDecimalFormatStyle);
+ TESTCASE(26, TestParseFailure);
#else
TESTCASE(0, TestRBNFDisabled);
#endif
doTest(&rbnf, enTestFullData, false);
}
+void IntlTestRBNF::TestParseFailure() {
+ UErrorCode status = U_ZERO_ERROR;
+ RuleBasedNumberFormat rbnf(URBNF_SPELLOUT, Locale::getJapanese(), status);
+ static const char* testData[][1] = {
+ { "\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB" },
+ { NULL }
+ };
+ for (int i = 0; testData[i][0]; ++i) {
+ const char* spelledNumber = testData[i][0]; // spelled-out number
+
+ UnicodeString spelledNumberString = UnicodeString(spelledNumber).unescape();
+ Formattable actualNumber;
+ rbnf.parse(spelledNumberString, actualNumber, status);
+ if (status != U_INVALID_FORMAT_ERROR) { // I would have expected U_PARSE_ERROR, but NumberFormat::parse gives U_INVALID_FORMAT_ERROR
+ errln("FAIL: string should be unparseable %s %s", spelledNumber, u_errorName(status));
+ }
+ }
+}
+
void
IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing)
{
void TestRounding();
void TestLargeNumbers();
void TestCompactDecimalFormatStyle();
+ void TestParseFailure();
protected:
virtual void doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing);
* result is an integer and Double otherwise. The result is never null.
*/
public Number doParse(String text, ParsePosition parsePosition, boolean isFractionRule,
- double upperBound) {
+ double upperBound, int nonNumericalExecutedRuleMask) {
// internally we operate on a copy of the string being parsed
// (because we're going to change it) and use our own ParsePosition
pp.setIndex(0);
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
ruleText.substring(sub1Pos, sub2Pos), rulePatternFormat,
- pp, sub1, upperBound).doubleValue();
+ pp, sub1, upperBound, nonNumericalExecutedRuleMask).doubleValue();
// if we got a successful match (or were trying to match a
// null substitution), pp is now pointing at the first unmatched
// a real result
partialResult = matchToDelimiter(workText2, 0, partialResult,
ruleText.substring(sub2Pos), rulePatternFormat, pp2, sub2,
- upperBound).doubleValue();
+ upperBound, nonNumericalExecutedRuleMask).doubleValue();
// if we got a successful match on this second
// matchToDelimiter() call, update the high-water mark
* Double.
*/
private Number matchToDelimiter(String text, int startPos, double baseVal,
- String delimiter, PluralFormat pluralFormatDelimiter, ParsePosition pp, NFSubstitution sub, double upperBound) {
+ String delimiter, PluralFormat pluralFormatDelimiter, ParsePosition pp, NFSubstitution sub,
+ double upperBound, int nonNumericalExecutedRuleMask) {
// if "delimiter" contains real (i.e., non-ignorable) text, search
// it for "delimiter" beginning at "start". If that succeeds, then
// use "sub"'s doParse() method to match the text before the
String subText = text.substring(0, dPos);
if (subText.length() > 0) {
tempResult = sub.doParse(subText, tempPP, baseVal, upperBound,
- formatter.lenientParseEnabled());
+ formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask);
// if the substitution could match all the text up to
// where we found "delimiter", then this function has
Number result = ZERO;
// try to match the whole string against the substitution
Number tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
- formatter.lenientParseEnabled());
+ formatter.lenientParseEnabled(), nonNumericalExecutedRuleMask);
if (tempPP.getIndex() != 0) {
// if there's a successful match (or it's a null
// substitution), update pp to point to the first
* this function returns new Long(0), and the parse position is
* left unchanged.
*/
- public Number parse(String text, ParsePosition parsePosition, double upperBound) {
+ public Number parse(String text, ParsePosition parsePosition, double upperBound, int nonNumericalExecutedRuleMask) {
// try matching each rule in the rule set against the text being
// parsed. Whichever one matches the most characters is the one
// that determines the value we return.
}
// Try each of the negative rules, fraction rules, infinity rules and NaN rules
- for (NFRule fractionRule : nonNumericalRules) {
- if (fractionRule != null) {
- tempResult = fractionRule.doParse(text, parsePosition, false, upperBound);
+ for (int nonNumericalRuleIdx = 0; nonNumericalRuleIdx < nonNumericalRules.length; nonNumericalRuleIdx++) {
+ NFRule nonNumericalRule = nonNumericalRules[nonNumericalRuleIdx];
+ if (nonNumericalRule != null && ((nonNumericalExecutedRuleMask >> nonNumericalRuleIdx) & 1) == 0) {
+ // Mark this rule as being executed so that we don't try to execute it again.
+ nonNumericalExecutedRuleMask |= 1 << nonNumericalRuleIdx;
+
+ tempResult = nonNumericalRule.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask);
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
result = tempResult;
highWaterMark.setIndex(parsePosition.getIndex());
continue;
}
- tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound);
+ tempResult = rules[i].doParse(text, parsePosition, isFractionRuleSet, upperBound, nonNumericalExecutedRuleMask);
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
result = tempResult;
highWaterMark.setIndex(parsePosition.getIndex());
* is left unchanged.
*/
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
- double upperBound, boolean lenientParse) {
+ double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
Number tempResult;
// figure out the highest base value a rule can have and match
// on), then also try parsing the text using a default-
// constructed NumberFormat
if (ruleSet != null) {
- tempResult = ruleSet.parse(text, parsePosition, upperBound);
+ tempResult = ruleSet.parse(text, parsePosition, upperBound, nonNumericalExecutedRuleMask);
if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {
tempResult = ruleSet.owner.getDecimalFormat().parse(text, parsePosition);
}
*/
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
- double upperBound, boolean lenientParse) {
+ double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
// if this isn't a >>> substitution, we can just use the
// inherited parse() routine to do the parsing
if (ruleToUse == null) {
- return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse);
+ return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse, nonNumericalExecutedRuleMask);
} else {
// but if it IS a >>> substitution, we have to do it here: we
// use the specific rule's doParse() method, and then we have to
// do some of the other work of NFRuleSet.parse()
- Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound);
+ Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound, nonNumericalExecutedRuleMask);
if (parsePosition.getIndex() != 0) {
double result = tempResult.doubleValue();
*/
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
- double upperBound, boolean lenientParse) {
+ double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
// if we're not in byDigits mode, we can just use the inherited
// doParse()
if (!byDigits) {
- return super.doParse(text, parsePosition, baseValue, 0, lenientParse);
+ return super.doParse(text, parsePosition, baseValue, 0, lenientParse, nonNumericalExecutedRuleMask);
}
else {
// if we ARE in byDigits mode, parse the text one digit at a time
int leadingZeros = 0;
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
- digit = ruleSet.parse(workText, workPos, 10).intValue();
+ digit = ruleSet.parse(workText, workPos, 10, nonNumericalExecutedRuleMask).intValue();
if (lenientParse && workPos.getIndex() == 0) {
Number n = ruleSet.owner.getDecimalFormat().parse(workText, workPos);
if (n != null) {
*/
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
- double upperBound, boolean lenientParse) {
+ double upperBound, boolean lenientParse, int nonNumericalExecutedRuleMask) {
// we don't have to do anything special to do the parsing here,
// but we have to turn lenient parsing off-- if we leave it on,
// it SERIOUSLY messes up the algorithm
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
- /*digit = */ruleSet.parse(workText, workPos, 1).intValue(); // parse zero or nothing at all
+ /*digit = */ruleSet.parse(workText, workPos, 1, nonNumericalExecutedRuleMask).intValue(); // parse zero or nothing at all
if (workPos.getIndex() == 0) {
// we failed, either there were no more zeros, or the number was formatted with digits
// either way, we're done
}
// we've parsed off the zeros, now let's parse the rest from our current position
- Number result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false);
+ Number result = super.doParse(text, parsePosition, withZeros ? 1 : baseValue, upperBound, false, nonNumericalExecutedRuleMask);
if (withZeros) {
// any base value will do in this case. is there a way to
// try parsing the string with the rule set. If it gets past the
// high-water mark, update the high-water mark and the result
- tempResult = ruleSets[i].parse(workingText, workingPos, Double.MAX_VALUE);
+ tempResult = ruleSets[i].parse(workingText, workingPos, Double.MAX_VALUE, 0);
if (workingPos.getIndex() > highWaterMark.getIndex()) {
result = tempResult;
highWaterMark.setIndex(workingPos.getIndex());
*/
package com.ibm.icu.dev.test.format;
+import java.text.ParseException;
import java.util.Locale;
import org.junit.Test;
logln("rbnf_fr:" + rbnf_en.getDefaultRuleSetName());
parseList(rbnf_en, rbnf_fr, lists);
}
+
+ @Test
+ public void TestBadParse() {
+ RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(Locale.JAPAN, RuleBasedNumberFormat.SPELLOUT);
+ String[] testData = {
+ "\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB\u30FB",
+ };
+
+ for (String testString : testData) {
+ try {
+ rbnf.parse(testString);
+ errln("Unexpected success: " + testString);
+ }
+ catch (ParseException e) {
+ // success!
+ }
+ }
+ }
}