diff --git a/icu4c/source/i18n/messageformat2.cpp b/icu4c/source/i18n/messageformat2.cpp index 73f7fa45e69f..ee68a4173681 100644 --- a/icu4c/source/i18n/messageformat2.cpp +++ b/icu4c/source/i18n/messageformat2.cpp @@ -328,13 +328,13 @@ void MessageFormatter::resolveSelectors(MessageContext& context, const Environme CHECK_ERROR(status); U_ASSERT(!dataModel.hasPattern()); - const Expression* selectors = dataModel.getSelectorsInternal(); + const VariableName* selectors = dataModel.getSelectorsInternal(); // 1. Let res be a new empty list of resolved values that support selection. // (Implicit, since `res` is an out-parameter) // 2. For each expression exp of the message's selectors for (int32_t i = 0; i < dataModel.numSelectors(); i++) { // 2i. Let rv be the resolved value of exp. - ResolvedSelector rv = formatSelectorExpression(env, selectors[i], context, status); + ResolvedSelector rv = formatSelector(env, selectors[i], context, status); if (rv.hasSelector()) { // 2ii. If selection is supported for rv: // (True if this code has been reached) @@ -606,7 +606,10 @@ void MessageFormatter::sortVariants(const UVector& pref, UVector& vars, UErrorCo // Evaluate the operand -ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, const Operand& rand, MessageContext& context, UErrorCode &status) const { +ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, + const Operand& rand, + MessageContext& context, + UErrorCode &status) const { if (U_FAILURE(status)) { return {}; } @@ -620,7 +623,13 @@ ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, cons } // Must be variable - const VariableName& var = rand.asVariable(); + return resolveVariables(env, rand.asVariable(), context, status); +} + +ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, + const VariableName& var, + MessageContext& context, + UErrorCode &status) const { // Resolve the variable if (env.has(var)) { const Closure& referent = env.lookup(var); @@ -683,13 +692,16 @@ ResolvedSelector MessageFormatter::resolveVariables(const Environment& env, } } -ResolvedSelector MessageFormatter::formatSelectorExpression(const Environment& globalEnv, const Expression& expr, MessageContext& context, UErrorCode &status) const { +ResolvedSelector MessageFormatter::formatSelector(const Environment& globalEnv, + const VariableName& var, + MessageContext& context, + UErrorCode &status) const { if (U_FAILURE(status)) { return {}; } // Resolve expression to determine if it's a function call - ResolvedSelector exprResult = resolveVariables(globalEnv, expr, context, status); + ResolvedSelector exprResult = resolveVariables(globalEnv, var, context, status); DynamicErrors& err = context.getErrors(); diff --git a/icu4c/source/i18n/messageformat2_checker.cpp b/icu4c/source/i18n/messageformat2_checker.cpp index bdc5c383b6e8..33c796f1b4ff 100644 --- a/icu4c/source/i18n/messageformat2_checker.cpp +++ b/icu4c/source/i18n/messageformat2_checker.cpp @@ -205,18 +205,14 @@ void Checker::checkVariants(UErrorCode& status) { } } -void Checker::requireAnnotated(const TypeEnvironment& t, const Expression& selectorExpr, UErrorCode& status) { +void Checker::requireAnnotated(const TypeEnvironment& t, + const VariableName& selectorVar, + UErrorCode& status) { CHECK_ERROR(status); - if (selectorExpr.isFunctionCall()) { + if (t.get(selectorVar) == TypeEnvironment::Type::Annotated) { return; // No error } - const Operand& rand = selectorExpr.getOperand(); - if (rand.isVariable()) { - if (t.get(rand.asVariable()) == TypeEnvironment::Type::Annotated) { - return; // No error - } - } // If this code is reached, an error was detected errors.addError(StaticErrorType::MissingSelectorAnnotation, status); } @@ -226,7 +222,7 @@ void Checker::checkSelectors(const TypeEnvironment& t, UErrorCode& status) { // Check each selector; if it's not annotated, emit a // "missing selector annotation" error - const Expression* selectors = dataModel.getSelectorsInternal(); + const VariableName* selectors = dataModel.getSelectorsInternal(); for (int32_t i = 0; i < dataModel.numSelectors(); i++) { requireAnnotated(t, selectors[i], status); } diff --git a/icu4c/source/i18n/messageformat2_checker.h b/icu4c/source/i18n/messageformat2_checker.h index 4bb0498efb99..34a58f23f0ee 100644 --- a/icu4c/source/i18n/messageformat2_checker.h +++ b/icu4c/source/i18n/messageformat2_checker.h @@ -64,7 +64,7 @@ namespace message2 { Checker(const MFDataModel& m, StaticErrors& e) : dataModel(m), errors(e) {} private: - void requireAnnotated(const TypeEnvironment&, const Expression&, UErrorCode&); + void requireAnnotated(const TypeEnvironment&, const VariableName&, UErrorCode&); void addFreeVars(TypeEnvironment& t, const Operand&, UErrorCode&); void addFreeVars(TypeEnvironment& t, const Operator&, UErrorCode&); void addFreeVars(TypeEnvironment& t, const OptionMap&, UErrorCode&); diff --git a/icu4c/source/i18n/messageformat2_data_model.cpp b/icu4c/source/i18n/messageformat2_data_model.cpp index 3fe5f65b5323..22fe19d3d61a 100644 --- a/icu4c/source/i18n/messageformat2_data_model.cpp +++ b/icu4c/source/i18n/messageformat2_data_model.cpp @@ -691,9 +691,9 @@ Matcher::Matcher(const Matcher& other) { numSelectors = other.numSelectors; numVariants = other.numVariants; UErrorCode localErrorCode = U_ZERO_ERROR; - selectors.adoptInstead(copyArray(other.selectors.getAlias(), - numSelectors, - localErrorCode)); + selectors.adoptInstead(copyArray(other.selectors.getAlias(), + numSelectors, + localErrorCode)); variants.adoptInstead(copyArray(other.variants.getAlias(), numVariants, localErrorCode)); @@ -702,7 +702,7 @@ Matcher::Matcher(const Matcher& other) { } } -Matcher::Matcher(Expression* ss, int32_t ns, Variant* vs, int32_t nv) +Matcher::Matcher(VariableName* ss, int32_t ns, Variant* vs, int32_t nv) : selectors(ss), numSelectors(ns), variants(vs), numVariants(nv) {} Matcher::~Matcher() {} @@ -724,7 +724,7 @@ const Binding* MFDataModel::getLocalVariablesInternal() const { return bindings.getAlias(); } -const Expression* MFDataModel::getSelectorsInternal() const { +const VariableName* MFDataModel::getSelectorsInternal() const { U_ASSERT(!bogus); U_ASSERT(!hasPattern()); return std::get_if(&body)->selectors.getAlias(); @@ -786,15 +786,13 @@ MFDataModel::Builder& MFDataModel::Builder::addBinding(Binding&& b, UErrorCode& return *this; } -/* - selector must be non-null -*/ -MFDataModel::Builder& MFDataModel::Builder::addSelector(Expression&& selector, UErrorCode& status) noexcept { +MFDataModel::Builder& MFDataModel::Builder::addSelector(VariableName&& selector, + UErrorCode& status) { THIS_ON_ERROR(status); buildSelectorsMessage(status); U_ASSERT(selectors != nullptr); - selectors->adoptElement(create(std::move(selector), status), status); + selectors->adoptElement(create(std::move(selector), status), status); return *this; } @@ -830,11 +828,11 @@ MFDataModel::MFDataModel(const MFDataModel& other) : body(Pattern()) { if (other.hasPattern()) { body = *std::get_if(&other.body); } else { - const Expression* otherSelectors = other.getSelectorsInternal(); + const VariableName* otherSelectors = other.getSelectorsInternal(); const Variant* otherVariants = other.getVariantsInternal(); int32_t numSelectors = other.numSelectors(); int32_t numVariants = other.numVariants(); - Expression* copiedSelectors = copyArray(otherSelectors, numSelectors, localErrorCode); + VariableName* copiedSelectors = copyArray(otherSelectors, numSelectors, localErrorCode); Variant* copiedVariants = copyArray(otherVariants, numVariants, localErrorCode); if (U_FAILURE(localErrorCode)) { bogus = true; @@ -863,7 +861,9 @@ MFDataModel::MFDataModel(const MFDataModel::Builder& builder, UErrorCode& errorC int32_t numVariants = builder.variants->size(); int32_t numSelectors = builder.selectors->size(); LocalArray variants(copyVectorToArray(*builder.variants, errorCode), errorCode); - LocalArray selectors(copyVectorToArray(*builder.selectors, errorCode), errorCode); + LocalArray selectors(copyVectorToArray(*builder.selectors, + errorCode), + errorCode); if (U_FAILURE(errorCode)) { bogus = true; return; diff --git a/icu4c/source/i18n/messageformat2_parser.cpp b/icu4c/source/i18n/messageformat2_parser.cpp index b4768756c5ea..bb157792593c 100644 --- a/icu4c/source/i18n/messageformat2_parser.cpp +++ b/icu4c/source/i18n/messageformat2_parser.cpp @@ -510,21 +510,13 @@ VariableName Parser::parseVariableName(UErrorCode& errorCode) { VariableName result; U_ASSERT(inBounds()); - // If the '$' is missing, we don't want a binding - // for this variable to be created. - bool valid = peek() == DOLLAR; + parseToken(DOLLAR, errorCode); if (!inBounds()) { ERROR(errorCode); return result; } - UnicodeString varName = parseName(errorCode); - // Set the name to "" if the variable wasn't - // declared correctly - if (!valid) { - varName.remove(); - } - return VariableName(varName); + return VariableName(parseName(errorCode)); } /* @@ -861,19 +853,9 @@ void Parser::parseAttribute(AttributeAdder& attrAdder, UErrorCode& errorCode) parseTokenWithWhitespace(EQUALS, errorCode); UnicodeString rhsStr; - // Parse RHS, which is either a literal or variable - switch (peek()) { - case DOLLAR: { - rand = Operand(parseVariableName(errorCode)); - break; - } - default: { - // Must be a literal - rand = Operand(parseLiteral(errorCode)); - break; - } - } - U_ASSERT(!rand.isNull()); + // Parse RHS, which must be a literal + // attribute = "@" identifier [o "=" o literal] + rand = Operand(parseLiteral(errorCode)); } else { // attribute -> "@" identifier [[s] "=" [s]] // Use null operand, which `rand` is already set to @@ -881,7 +863,7 @@ void Parser::parseAttribute(AttributeAdder& attrAdder, UErrorCode& errorCode) index = savedIndex; } - attrAdder.addAttribute(lhs, std::move(rand), errorCode); + attrAdder.addAttribute(lhs, std::move(Operand(rand)), errorCode); } /* @@ -1720,6 +1702,22 @@ Pattern Parser::parseSimpleMessage(UErrorCode& status) { return result.build(status); } +void Parser::parseVariant(UErrorCode& status) { + CHECK_ERROR(status); + + // At least one key is required + SelectorKeys keyList(parseNonEmptyKeys(status)); + + // parseNonEmptyKeys() consumes any trailing whitespace, + // so the pattern can be consumed next. + + // Restore precondition before calling parsePattern() + // (which must return a non-null value) + CHECK_BOUNDS(status); + Pattern rhs = parseQuotedPattern(status); + + dataModel.addVariant(std::move(keyList), std::move(rhs), status); +} /* Consume a `selectors` (matching the nonterminal in the grammar), @@ -1739,22 +1737,25 @@ void Parser::parseSelectors(UErrorCode& status) { // Parse selectors // "Backtracking" is required here. It's not clear if whitespace is // (`[s]` selector) or (`[s]` variant) - while (isWhitespace(peek()) || peek() == LEFT_CURLY_BRACE) { - parseOptionalWhitespace(status); + while (isWhitespace(peek()) || peek() == DOLLAR) { + int32_t whitespaceStart = index; + parseRequiredWhitespace(status); // Restore precondition CHECK_BOUNDS(status); - if (peek() != LEFT_CURLY_BRACE) { + if (peek() != DOLLAR) { // This is not necessarily an error, but rather, // means the whitespace we parsed was the optional // whitespace preceding the first variant, not the - // optional whitespace preceding a subsequent expression. + // required whitespace preceding a subsequent variable. + // In that case, "push back" the whitespace. + normalizedInput.truncate(normalizedInput.length() - 1); + index = whitespaceStart; break; } - Expression expression; - expression = parseExpression(status); + VariableName var = parseVariableName(status); empty = false; - dataModel.addSelector(std::move(expression), status); + dataModel.addSelector(std::move(var), status); CHECK_ERROR(status); } @@ -1770,27 +1771,29 @@ void Parser::parseSelectors(UErrorCode& status) { } \ // Parse variants + // matcher = match-statement s variant *(o variant) + + // Parse first variant + parseRequiredWhitespace(status); + if (!inBounds()) { + ERROR(status); + return; + } + parseVariant(status); + if (!inBounds()) { + // Not an error; there might be only one variant + return; + } + while (isWhitespace(peek()) || isKeyStart(peek())) { - // Trailing whitespace is allowed parseOptionalWhitespace(status); + // Restore the precondition. + // Trailing whitespace is allowed. if (!inBounds()) { return; } - // At least one key is required - SelectorKeys keyList(parseNonEmptyKeys(status)); - - CHECK_ERROR(status); - - // parseNonEmptyKeys() consumes any trailing whitespace, - // so the pattern can be consumed next. - - // Restore precondition before calling parsePattern() - // (which must return a non-null value) - CHECK_BOUNDS(status); - Pattern rhs = parseQuotedPattern(status); - - dataModel.addVariant(std::move(keyList), std::move(rhs), status); + parseVariant(status); // Restore the precondition, *without* erroring out if we've // reached the end of input. That's because it's valid for the diff --git a/icu4c/source/i18n/messageformat2_parser.h b/icu4c/source/i18n/messageformat2_parser.h index b62cbe9200b9..b7c4bd478686 100644 --- a/icu4c/source/i18n/messageformat2_parser.h +++ b/icu4c/source/i18n/messageformat2_parser.h @@ -100,7 +100,8 @@ namespace message2 { void parseUnsupportedStatement(UErrorCode&); void parseLocalDeclaration(UErrorCode&); void parseInputDeclaration(UErrorCode&); - void parseSelectors(UErrorCode&); + void parseSelectors(UErrorCode&); + void parseVariant(UErrorCode&); void parseWhitespaceMaybeRequired(bool, UErrorCode&); void parseRequiredWhitespace(UErrorCode&); diff --git a/icu4c/source/i18n/messageformat2_serializer.cpp b/icu4c/source/i18n/messageformat2_serializer.cpp index b2765f5acf43..47dce426f3ce 100644 --- a/icu4c/source/i18n/messageformat2_serializer.cpp +++ b/icu4c/source/i18n/messageformat2_serializer.cpp @@ -244,11 +244,12 @@ void Serializer::serializeDeclarations() { void Serializer::serializeSelectors() { U_ASSERT(!dataModel.hasPattern()); - const Expression* selectors = dataModel.getSelectorsInternal(); + const VariableName* selectors = dataModel.getSelectorsInternal(); emit(ID_MATCH); for (int32_t i = 0; i < dataModel.numSelectors(); i++) { - // No whitespace needed here -- see `selectors` in the grammar + whitespace(); + emit(DOLLAR); emit(selectors[i]); } } @@ -256,6 +257,7 @@ void Serializer::serializeSelectors() { void Serializer::serializeVariants() { U_ASSERT(!dataModel.hasPattern()); const Variant* variants = dataModel.getVariantsInternal(); + whitespace(); for (int32_t i = 0; i < dataModel.numVariants(); i++) { const Variant& v = variants[i]; emit(v.getKeys()); diff --git a/icu4c/source/i18n/unicode/messageformat2.h b/icu4c/source/i18n/unicode/messageformat2.h index c5459f042f40..603463eb9a0f 100644 --- a/icu4c/source/i18n/unicode/messageformat2.h +++ b/icu4c/source/i18n/unicode/messageformat2.h @@ -334,8 +334,18 @@ namespace message2 { // Do not define default assignment operator const MessageFormatter &operator=(const MessageFormatter &) = delete; - ResolvedSelector resolveVariables(const Environment& env, const data_model::Operand&, MessageContext&, UErrorCode &) const; - ResolvedSelector resolveVariables(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode &) const; + ResolvedSelector resolveVariables(const Environment& env, + const data_model::VariableName&, + MessageContext&, + UErrorCode &) const; + ResolvedSelector resolveVariables(const Environment& env, + const data_model::Operand&, + MessageContext&, + UErrorCode &) const; + ResolvedSelector resolveVariables(const Environment& env, + const data_model::Expression&, + MessageContext&, + UErrorCode &) const; // Selection methods @@ -365,8 +375,11 @@ namespace message2 { FunctionOptions&& options, MessageContext& context, UErrorCode& status) const; - // Formats an expression that appears as a selector - ResolvedSelector formatSelectorExpression(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode&) const; + // Formats a variableName that appears as a selector + ResolvedSelector formatSelector(const Environment& env, + const data_model::VariableName&, + MessageContext&, + UErrorCode&) const; // Formats an expression that appears in a pattern or as the definition of a local variable [[nodiscard]] FormattedPlaceholder formatExpression(const Environment&, const data_model::Expression&, MessageContext&, UErrorCode&) const; [[nodiscard]] FunctionOptions resolveOptions(const Environment& env, const OptionMap&, MessageContext&, UErrorCode&) const; diff --git a/icu4c/source/i18n/unicode/messageformat2_data_model.h b/icu4c/source/i18n/unicode/messageformat2_data_model.h index 0c836af1bdfd..43c2793b2a72 100644 --- a/icu4c/source/i18n/unicode/messageformat2_data_model.h +++ b/icu4c/source/i18n/unicode/messageformat2_data_model.h @@ -2211,7 +2211,7 @@ namespace message2 { friend class MFDataModel; - Matcher(Expression* ss, int32_t ns, Variant* vs, int32_t nv); + Matcher(VariableName* ss, int32_t ns, Variant* vs, int32_t nv); Matcher() {} // A Matcher may have numSelectors=0 and numVariants=0 @@ -2219,8 +2219,8 @@ namespace message2 { // So we have to keep a separate flag to track failed copies. bool bogus = false; - // The expressions that are being matched on. - LocalArray selectors; + // The variables that are being matched on. + LocalArray selectors; // The number of selectors int32_t numSelectors = 0; // The list of `when` clauses (case arms). @@ -2328,13 +2328,13 @@ namespace message2 { * @internal ICU 75 technology preview * @deprecated This API is for technology preview only. */ - const std::vector getSelectors() const { + const std::vector getSelectors() const { if (std::holds_alternative(body)) { return {}; } const Matcher* match = std::get_if(&body); // match must be non-null, given the previous check - return toStdVector(match->selectors.getAlias(), match->numSelectors); + return toStdVector(match->selectors.getAlias(), match->numSelectors); } /** * Accesses the variants. Returns an empty vector if this is a pattern message. @@ -2462,17 +2462,17 @@ namespace message2 { */ Builder& addBinding(Binding&& b, UErrorCode& status); /** - * Adds a selector expression. Copies `expression`. + * Adds a selector variable. * If a pattern was previously set, clears the pattern. * - * @param selector Expression to add as a selector. Passed by move. + * @param selector Variable to add as a selector. Passed by move. * @param errorCode Input/output error code * @return A reference to the builder. * * @internal ICU 75 technology preview * @deprecated This API is for technology preview only. */ - Builder& addSelector(Expression&& selector, UErrorCode& errorCode) noexcept; + Builder& addSelector(VariableName&& selector, UErrorCode& errorCode); /** * Adds a single variant. * If a pattern was previously set using `setPattern()`, clears the pattern. @@ -2564,7 +2564,7 @@ namespace message2 { int32_t bindingsLen = 0; const Binding* getLocalVariablesInternal() const; - const Expression* getSelectorsInternal() const; + const VariableName* getSelectorsInternal() const; const Variant* getVariantsInternal() const; int32_t numSelectors() const { diff --git a/icu4c/source/test/intltest/messageformat2test.cpp b/icu4c/source/test/intltest/messageformat2test.cpp index 353082ef5c91..2e969128d424 100644 --- a/icu4c/source/test/intltest/messageformat2test.cpp +++ b/icu4c/source/test/intltest/messageformat2test.cpp @@ -182,7 +182,8 @@ void TestMessageFormat2::testAPISimple() { argsBuilder["userName"] = message2::Formattable("Maria"); args = MessageArguments(argsBuilder, errorCode); - mf = builder.setPattern(".match {$photoCount :number} {$userGender :string}\n\ + mf = builder.setPattern(".input {$photoCount :number} .input {$userGender :string}\n\ + .match $photoCount $userGender\n \ 1 masculine {{{$userName} added a new photo to his album.}}\n \ 1 feminine {{{$userName} added a new photo to her album.}}\n \ 1 * {{{$userName} added a new photo to their album.}}\n \ @@ -226,7 +227,8 @@ void TestMessageFormat2::testAPI() { TestUtils::runTestCase(*this, test, errorCode); // Pattern matching - plural - UnicodeString pattern = ".match {$photoCount :string} {$userGender :string}\n\ + UnicodeString pattern = ".input {$photoCount :number} .input {$userGender :string}\n\ + .match $photoCount $userGender\n\ 1 masculine {{{$userName} added a new photo to his album.}}\n \ 1 feminine {{{$userName} added a new photo to her album.}}\n \ 1 * {{{$userName} added a new photo to their album.}}\n \ @@ -247,7 +249,8 @@ void TestMessageFormat2::testAPI() { TestUtils::runTestCase(*this, test, errorCode); // Built-in functions - pattern = ".match {$photoCount :number} {$userGender :string}\n\ + pattern = ".input {$photoCount :number} .input {$userGender :string}\n\ + .match $photoCount $userGender\n \ 1 masculine {{{$userName} added a new photo to his album.}}\n \ 1 feminine {{{$userName} added a new photo to her album.}}\n \ 1 * {{{$userName} added a new photo to their album.}}\n \ diff --git a/icu4c/source/test/intltest/messageformat2test_custom.cpp b/icu4c/source/test/intltest/messageformat2test_custom.cpp index b498be791ca9..f712a885b208 100644 --- a/icu4c/source/test/intltest/messageformat2test_custom.cpp +++ b/icu4c/source/test/intltest/messageformat2test_custom.cpp @@ -115,7 +115,8 @@ void TestMessageFormat2::testCustomFunctionsComplexMessage(IcuTestErrorCode& err UnicodeString message = ".local $hostName = {$host :person length=long}\n\ .local $guestName = {$guest :person length=long}\n\ .input {$guestCount :number}\n\ - .match {$hostGender :string} {$guestCount :number}\n\ + .input {$hostGender :string}\n\ + .match $hostGender $guestCount\n\ female 0 {{{$hostName} does not give a party.}}\n\ female 1 {{{$hostName} invites {$guestName} to her party.}}\n\ female 2 {{{$hostName} invites {$guestName} and one other person to her party.}}\n\ @@ -538,9 +539,9 @@ void TestMessageFormat2::testListFormatter(IcuTestErrorCode& errorCode) { /* static */ Hashtable* message2::ResourceManager::properties(UErrorCode& errorCode) { NULL_ON_ERROR(errorCode); - UnicodeString* firefox = new UnicodeString(".match {$gcase :string} genitive {{Firefoxin}} * {{Firefox}}"); - UnicodeString* chrome = new UnicodeString(".match {$gcase :string} genitive {{Chromen}} * {{Chrome}}"); - UnicodeString* safari = new UnicodeString(".match {$gcase :string} genitive {{Safarin}} * {{Safari}}"); + UnicodeString* firefox = new UnicodeString(".input {$gcase :string} .match $gcase genitive {{Firefoxin}} * {{Firefox}}"); + UnicodeString* chrome = new UnicodeString(".input {$gcase :string} .match $gcase genitive {{Chromen}} * {{Chrome}}"); + UnicodeString* safari = new UnicodeString(".input {$gcase :string} .match $gcase genitive {{Safarin}} * {{Safari}}"); if (firefox != nullptr && chrome != nullptr && safari != nullptr) { Hashtable* result = new Hashtable(uhash_compareUnicodeString, nullptr, errorCode); diff --git a/icu4c/source/test/intltest/messageformat2test_read_json.cpp b/icu4c/source/test/intltest/messageformat2test_read_json.cpp index ddf93da632ce..98020e15306b 100644 --- a/icu4c/source/test/intltest/messageformat2test_read_json.cpp +++ b/icu4c/source/test/intltest/messageformat2test_read_json.cpp @@ -300,6 +300,10 @@ void TestMessageFormat2::jsonTestsFromFiles(IcuTestErrorCode& errorCode) { // Do valid spec tests runTestsFromJsonFile(*this, "spec/syntax.json", errorCode); + // Uncomment when test functions are implemented in the registry + // See https://unicode-org.atlassian.net/browse/ICU-22907 + // runTestsFromJsonFile(*this, "spec/pattern-selection.json", errorCode); + // Do valid function tests runTestsFromJsonFile(*this, "spec/functions/date.json", errorCode); runTestsFromJsonFile(*this, "spec/functions/datetime.json", errorCode); diff --git a/icu4c/source/test/intltest/messageformat2test_utils.h b/icu4c/source/test/intltest/messageformat2test_utils.h index c4ad251c7f48..6b3458c9c6bd 100644 --- a/icu4c/source/test/intltest/messageformat2test_utils.h +++ b/icu4c/source/test/intltest/messageformat2test_utils.h @@ -252,7 +252,8 @@ class TestUtils { if (!roundTrip(in, mf.getDataModel(), out) // For now, don't round-trip messages with these errors, // since duplicate options are dropped - && testCase.expectedErrorCode() != U_MF_DUPLICATE_OPTION_NAME_ERROR) { + && (testCase.expectSuccess() || + (testCase.expectedErrorCode() != U_MF_DUPLICATE_OPTION_NAME_ERROR))) { failRoundTrip(tmsg, testCase, in, out); } diff --git a/testdata/message2/alias-selector-annotations.json b/testdata/message2/alias-selector-annotations.json index c064d40d4c82..d5307c5b1192 100644 --- a/testdata/message2/alias-selector-annotations.json +++ b/testdata/message2/alias-selector-annotations.json @@ -6,11 +6,11 @@ }, "tests": [ { - "src": ".local $one = {|The one| :string}\n .match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "src": ".local $one = {|The one| :string}\n .match $one\n 1 {{Value is one}}\n * {{Value is not one}}", "exp": "Value is not one" }, { - "src": ".local $one = {|The one| :string}\n .local $two = {$one}\n .match {$two}\n 1 {{Value is one}}\n * {{Value is not one}}", + "src": ".local $one = {|The one| :string}\n .local $two = {$one}\n .match $two\n 1 {{Value is one}}\n * {{Value is not one}}", "exp": "Value is not one" } ] diff --git a/testdata/message2/icu-test-previous-release.json b/testdata/message2/icu-test-previous-release.json index 0a1e27dff6e2..74fc41fdfcea 100644 --- a/testdata/message2/icu-test-previous-release.json +++ b/testdata/message2/icu-test-previous-release.json @@ -31,50 +31,50 @@ "exp": "bar foo" }, { - "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "src": ".input {$foo :number} .match $foo 1 {{one}} * {{other}}", "params": [{ "name": "foo", "value": "1" }], "exp": "one", "ignoreJava": "See ICU-22809" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "params": [{ "name": "foo", "value": "1" }], "exp": "one" }, { - "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "src": ".input {$foo :number} .match $foo 1 {{one}} * {{other}}", "params": [{ "name": "foo", "value": 1 }], "exp": "one" }, { "ignoreJava": "Can't pass null in a map", "ignoreCpp": "Same as Java", - "src": ".match {$foo} 1 {{one}} * {{other}}", + "src": ".match $foo 1 {{one}} * {{other}}", "params": [{ "name": "foo", "value": null }], "exp": "other" }, { - "src": ".match {$foo :number} 1 {{one}} * {{other}}", + "src": ".input {$foo :number} .match $foo 1 {{one}} * {{other}}", "exp": "other", "expErrors": [{ "type": "unresolved-variable" }] }, { - "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", + "src": ".local $foo = {$bar :number} .match $foo one {{one}} * {{other}}", "params": [{ "name": "bar", "value": 1 }], "exp": "one" }, { - "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", + "src": ".local $foo = {$bar :number} .match $foo one {{one}} * {{other}}", "params": [{ "name": "bar", "value": 2 }], "exp": "other" }, { - "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", + "src": ".local $bar = {$none} .input {$foo :number} .match $foo one {{one}} * {{{$bar}}}", "params": [{ "name": "foo", "value": 1 }, {"name": "none", "value": "" }], "exp": "one" }, { - "src": ".local $bar = {$none :number} .match {$foo :string} one {{one}} * {{{$bar}}}", + "src": ".local $bar = {$none :number} .input {$foo :string} .match $foo one {{one}} * {{{$bar}}}", "params": [{ "name": "foo", "value": 2 }], "exp": "{$none}", "expErrors": [{ "type": "unresolved-variable" }], @@ -120,17 +120,17 @@ "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/703" }, { - "src": ".match {|foo| :string} *{{foo}}", + "src": ".local $f = {|foo| :string} .match $f *{{foo}}", "exp": "foo" }, { - "src": ".match {$foo :string} * * {{foo}}", + "src": ".input {$foo :string} .match $foo * * {{foo}}", "exp": "foo", "expErrors": [{ "type": "variant-key-mismatch" }, { "type": "unresolved-variable" }], "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/735" }, { - "src": ".match {$foo :string} {$bar :string} * {{foo}}", + "src": ".input {$foo :string} .input {$bar :string} .match $foo $bar * {{foo}}", "exp": "foo", "expErrors": [{ "type": "variant-key-mismatch" }, { "type": "unresolved-variable" }], "ignoreCpp": "Fallback is unclear. See https://github.com/unicode-org/message-format-wg/issues/735" diff --git a/testdata/message2/icu-test-selectors.json b/testdata/message2/icu-test-selectors.json index 102bdfd88f50..7998f1359fb2 100644 --- a/testdata/message2/icu-test-selectors.json +++ b/testdata/message2/icu-test-selectors.json @@ -8,7 +8,8 @@ { "comment": "Testing simple plural", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -18,7 +19,8 @@ { "comment": "Testing simple plural", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -28,7 +30,8 @@ { "comment": "Testing simple plural", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -39,7 +42,8 @@ "comment": "Testing simple plural", "locale": "fr", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -50,7 +54,8 @@ "comment": "Testing simple plural", "locale": "fr", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -61,7 +66,8 @@ "comment": "Testing simple plural", "locale": "fr", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", "one {{{$count} file}}\n", " * {{{$count} files}}" ], @@ -71,7 +77,8 @@ { "comment": "Testing simple plural, but swap variant order", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", " * {{You deleted {$count} files}}\n", "one {{You deleted {$count} file}}" ], @@ -81,7 +88,8 @@ { "comment": "Testing simple plural, but swap variant order", "src": [ - ".match {$count :number}\n", + ".input {$count :number}\n", + ".match $count\n", " * {{You deleted {$count} files}}\n", "one {{You deleted {$count} file}}" ], @@ -91,7 +99,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -106,7 +115,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -121,7 +131,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -136,7 +147,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -151,7 +163,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -166,7 +179,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -181,7 +195,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -196,7 +211,8 @@ { "comment": "Ordinal, with mixed order and exact matches", "src": [ - ".match {$place :number select=ordinal}\n", + ".input {$place :number select=ordinal}\n", + ".match $place\n", "* {{You finished in the {$place}th place}}\n", "two {{You finished in the {$place}nd place}}\n", "one {{You finished in the {$place}st place}}\n", @@ -211,7 +227,8 @@ { "comment": "Plural combinations, mixed order", "src": [ - ".match {$fileCount :number} {$folderCount :number}\n", + ".input {$fileCount :number} .input {$folderCount :number}\n", + ".match $fileCount $folderCount\n", " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", @@ -224,7 +241,8 @@ { "comment": "Plural combinations, mixed order", "src": [ - ".match {$fileCount :number} {$folderCount :number}\n", + ".input {$fileCount :number} .input {$folderCount :number}\n", + ".match $fileCount $folderCount\n", " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", @@ -237,7 +255,8 @@ { "comment": "Plural combinations, mixed order", "src": [ - ".match {$fileCount :number} {$folderCount :number}\n", + ".input {$fileCount :number} .input {$folderCount :number}\n", + ".match $fileCount $folderCount\n", " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", @@ -250,7 +269,8 @@ { "comment": "Plural combinations, mixed order", "src": [ - ".match {$fileCount :number} {$folderCount :number}\n", + ".input {$fileCount :number} .input {$folderCount :number}\n", + ".match $fileCount $folderCount\n", " * * {{You found {$fileCount} files in {$folderCount} folders}}\n", " one one {{You found {$fileCount} file in {$folderCount} folder}}\n", " one * {{You found {$fileCount} file in {$folderCount} folders}}\n", @@ -264,7 +284,7 @@ "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", "src": [ ".local $c = {$price :number minimumFractionDigits=$minF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -276,7 +296,7 @@ "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", "src": [ ".local $c = {$price :number minimumFractionDigits=$minF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -288,7 +308,7 @@ "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", "src": [ ".local $c = {$price :number maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -300,7 +320,7 @@ "comment": "Test that the selection honors the formatting option (`1.00 dollars`)", "src": [ ".local $c = {$price :number maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -312,7 +332,7 @@ "comment": "Test that the selection honors the `:integer` over options", "src": [ ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -324,7 +344,7 @@ "comment": "Test that the selection honors the `:integer` over options", "src": [ ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -336,7 +356,7 @@ "comment": "Test that the selection honors the `:integer` over options", "src": [ ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -348,7 +368,7 @@ "comment": "Test that the selection honors the `:integer` over options", "src": [ ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], @@ -360,7 +380,7 @@ "comment": "Test that the selection honors the `:integer` over options", "src": [ ".local $c = {$price :integer maximumFractionDigits=$maxF}\n", - ".match {$c}\n", + ".match $c\n", " one {{{$c} dollar}}\n", " * {{{$c} dollars}}" ], diff --git a/testdata/message2/matches-whitespace.json b/testdata/message2/matches-whitespace.json index a0af4c4d143e..2255a0203bb7 100644 --- a/testdata/message2/matches-whitespace.json +++ b/testdata/message2/matches-whitespace.json @@ -5,29 +5,14 @@ "locale": "en-US" }, "tests": [ - { "src": ".match {one :string} {bar :string} one * {{one}} * * {{other}}", + { "src": ".local $one = {1 :string} .local $bar = {bar :string} .match $one $bar one * {{one}} * * {{other}}", "exp": "one" }, - { "src": ".match {foo :string} {bar :string}one * {{one}} * * {{other}}", - "exp": "other" - }, - { "src": ".match {foo :string}{bar :string} one * {{one}} * * {{other}}", - "exp": "other" - }, - { "src": ".match {one :string}{bar :string}one * {{one}} * * {{other}}", - "exp": "one" - }, - { "src": ".match{foo :string} {bar :string} one * {{one}} * * {{other}}", - "exp": "other" - }, - { "src": ".match {foo :string} {bar :string} one * {{one}}* * {{other}}", + { "src": ".local $foo = {foo :string} .local $bar = {bar :string} .match $foo $bar one * {{one}}* * {{other}}", "exp": "other" }, - { "src": ".match {foo :string} {bar :string}one * {{one}}* * {{other}}", - "exp": "other" - }, - { "src": ".match {foo :string} {bar :string} one *{{one}} * * {{foo}}", + { "src": ".local $foo = {foo :string} .local $bar = {bar :string} .match $foo $bar one *{{one}} * * {{foo}}", "exp": "foo" }, - { "src": ".match {foo :string} {bar :string} one * {{one}} * * {{foo}}", + { "src": ".local $foo = {foo :string} .local $bar = {bar :string} .match $foo $bar one * {{one}} * * {{foo}}", "exp": "foo" } ] } diff --git a/testdata/message2/more-data-model-errors.json b/testdata/message2/more-data-model-errors.json index 32744083af1c..36b528bc2715 100644 --- a/testdata/message2/more-data-model-errors.json +++ b/testdata/message2/more-data-model-errors.json @@ -5,7 +5,7 @@ }, "tests": [ { - "src": ".match {$foo :number} {$bar :number} one{{one}}", + "src": ".input {$foo :number} .input {$bar :number} .match $foo $bar one{{one}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -14,7 +14,7 @@ }, { - "src": ".match {$foo :number} {$bar :number} one {{one}}", + "src": ".input {$foo :number} .input {$bar :number} .match $foo $bar one {{one}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -23,7 +23,7 @@ }, { - "src": ".match {$foo :number} {$bar :number} one {{one}}", + "src": ".input {$foo :number} .input {$bar :number} .match $foo $bar one {{one}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -32,7 +32,7 @@ }, { - "src": ".match {$foo :number} * * {{foo}}", + "src": ".input {$foo :number} .match $foo * * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -41,7 +41,7 @@ }, { - "src": ".match {$one :number}\n 1 2 {{Too many}}\n * {{Otherwise}}", + "src": ".input {$one :number} .match $one\n 1 2 {{Too many}}\n * {{Otherwise}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -50,7 +50,7 @@ }, { - "src": ".match {$one :number} {$two :number}\n 1 2 {{Two keys}}\n * {{Missing a key}}\n * * {{Otherwise}}", + "src": ".input {$one :number} .input {$two :number} .match $one $two \n 1 2 {{Two keys}}\n * {{Missing a key}}\n * * {{Otherwise}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -59,7 +59,7 @@ }, { - "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "src": ".input {$foo :x} .input {$bar :X} .match $foo $bar * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -68,7 +68,7 @@ }, { - "src": ".match {$one :number}\n 1 {{Value is one}}\n 2 {{Value is two}}", + "src": ".input {$one :number} .match $one\n 1 {{Value is one}}\n 2 {{Value is two}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -77,7 +77,7 @@ }, { - "src": ".match {$one :number} {$two :number}\n 1 * {{First is one}}\n * 1 {{Second is one}}", + "src": ".input {$one :number} .input {$two :number} .match $one $two\n 1 * {{First is one}}\n * 1 {{Second is one}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -86,7 +86,7 @@ }, { - "src": ".match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "src": ".match $one\n 1 {{Value is one}}\n * {{Value is not one}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -95,7 +95,7 @@ }, { - "src": ".local $one = {|The one|}\n .match {$one}\n 1 {{Value is one}}\n * {{Value is not one}}", + "src": ".local $one = {|The one|}\n .match $one\n 1 {{Value is one}}\n * {{Value is not one}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -103,7 +103,7 @@ ] }, { - "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "src": ".input {$foo} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -112,7 +112,7 @@ }, { - "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "src": ".local $foo = {$bar} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" diff --git a/testdata/message2/resolution-errors.json b/testdata/message2/resolution-errors.json index 8f1ac2fb9364..736e1cb64f05 100644 --- a/testdata/message2/resolution-errors.json +++ b/testdata/message2/resolution-errors.json @@ -7,8 +7,8 @@ "tests": [ { "src": "{$oops}", "exp": "{$oops}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "ICU4J doesn't signal unresolved variable errors?"}, { "src": ".input {$x :number} {{{$x}}}", "exp": "{$x}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, - { "src": ".local $foo = {$bar} .match {$foo :number} one {{one}} * {{other}}", "exp": "other", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, - { "src": ".local $bar = {$none :number} .match {$foo :string} one {{one}} * {{{$bar}}}", "exp": "{$none}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, + { "src": ".local $foo = {$bar} .local $f = {$foo :number} .match $f one {{one}} * {{other}}", "exp": "other", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, + { "src": ".local $bar = {$none :number} .local $f = {$foo :string} .match $f one {{one}} * {{{$bar}}}", "exp": "{$none}", "expErrors": [{ "type": "unresolved-variable" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"}, { "src": "The value is {horse :func}.", "exp": "The value is {|horse|}.", "expErrors": [{ "type": "unknown-function" }], "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782"} ] } diff --git a/testdata/message2/runtime-errors.json b/testdata/message2/runtime-errors.json index b1bb0cd491a0..ffeb081fab0d 100644 --- a/testdata/message2/runtime-errors.json +++ b/testdata/message2/runtime-errors.json @@ -6,19 +6,19 @@ }, "tests": [ { - "src": ".match {|horse| :date}\n 1 {{The value is one.}}\n * {{Formatter used as selector.}}", + "src": ".local $h = {|horse| :date} .match $h\n 1 {{The value is one.}}\n * {{Formatter used as selector.}}", "exp": "Formatter used as selector.", "expErrors": [{"type": "bad-selector"}], "ignoreJava": "ICU4J doesn't signal runtime errors?" }, { - "src": ".match {|horse| :number}\n 1 {{The value is one.}}\n * {{horse is not a number.}}", + "src": ".local $h = {|horse| :number} .match $h\n 1 {{The value is one.}}\n * {{horse is not a number.}}", "exp": "horse is not a number.", "expErrors": [{"type": "bad-selector"}], "ignoreJava": "ICU4J doesn't signal runtime errors?" }, { - "src": ".local $sel = {|horse| :number}\n .match {$sel}\n 1 {{The value is one.}}\n * {{horse is not a number.}}", + "src": ".local $sel = {|horse| :number}\n .match $sel\n 1 {{The value is one.}}\n * {{horse is not a number.}}", "exp": "horse is not a number.", "expErrors": [{"type": "bad-selector"}], "ignoreJava": "ICU4J doesn't signal runtime errors?" diff --git a/testdata/message2/spec/data-model-errors.json b/testdata/message2/spec/data-model-errors.json index 86a674c43961..f1f54cabe7c2 100644 --- a/testdata/message2/spec/data-model-errors.json +++ b/testdata/message2/spec/data-model-errors.json @@ -6,7 +6,7 @@ }, "tests": [ { - "src": ".match {$foo :x} * * {{foo}}", + "src": ".input {$foo :x} .match $foo * * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -14,7 +14,7 @@ ] }, { - "src": ".match {$foo :x} {$bar :x} * {{foo}}", + "src": ".input {$foo :x} .input {$bar :x} .match $foo $bar * {{foo}}", "expErrors": [ { "type": "variant-key-mismatch" @@ -22,7 +22,7 @@ ] }, { - "src": ".match {:foo} 1 {{_}}", + "src": ".input {$foo :x} .match $foo 1 {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -30,7 +30,7 @@ ] }, { - "src": ".match {:foo} other {{_}}", + "src": ".input {$foo :x} .match $foo other {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -38,7 +38,7 @@ ] }, { - "src": ".match {:foo} {:bar} * 1 {{_}} 1 * {{_}}", + "src": ".input {$foo :x} .input {$bar :x} .match $foo $bar * 1 {{_}} 1 * {{_}}", "expErrors": [ { "type": "missing-fallback-variant" @@ -46,7 +46,7 @@ ] }, { - "src": ".match {$foo} one {{one}} * {{other}}", + "src": ".input {$foo} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -54,7 +54,7 @@ ] }, { - "src": ".input {$foo} .match {$foo} one {{one}} * {{other}}", + "src": ".local $foo = {$bar} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -62,7 +62,7 @@ ] }, { - "src": ".local $foo = {$bar} .match {$foo} one {{one}} * {{other}}", + "src": ".input {$bar} .local $foo = {$bar} .match $foo one {{one}} * {{other}}", "expErrors": [ { "type": "missing-selector-annotation" @@ -166,7 +166,7 @@ ] }, { - "src": ".match {$var :string} * {{The first default}} * {{The second default}}", + "src": ".input {$var :string} .match $var * {{The first default}} * {{The second default}}", "expErrors": [ { "type": "duplicate-variant" @@ -174,12 +174,16 @@ ] }, { - "src": ".match {$x :string} {$y :string} * foo {{The first foo variant}} bar * {{The bar variant}} * |foo| {{The second foo variant}} * * {{The default variant}}", + "src": ".input {$x :string} .input {$y :string} .match $x $y * foo {{The first foo variant}} bar * {{The bar variant}} * |foo| {{The second foo variant}} * * {{The default variant}}", "expErrors": [ { "type": "duplicate-variant" } ] + }, + { + "src": ".local $star = {star :string} .match $star |*| {{Literal star}} * {{The default}}", + "exp": "The default" } ] } diff --git a/testdata/message2/spec/functions/date.json b/testdata/message2/spec/functions/date.json index dd14e6785fb6..494ca8d23458 100644 --- a/testdata/message2/spec/functions/date.json +++ b/testdata/message2/spec/functions/date.json @@ -1,9 +1,10 @@ { + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", "scenario": "Date function", "description": "The built-in formatter for dates.", "defaultTestProperties": { "locale": "en-US", - "expErrors": [] + "expErrors": false }, "tests": [ { @@ -13,8 +14,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{horse :date}", @@ -23,8 +23,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{|2006-01-02| :date}" @@ -39,8 +38,7 @@ "src": ".local $d = {|2006-01-02| :date style=long} {{{$d :date}}}" }, { - "src": ".local $t = {|2006-01-02T15:04:06| :time} {{{$t :date}}}", - "ignoreJava": "ICU4J doesn't support this kind of composition" + "src": ".local $t = {|2006-01-02T15:04:06| :time} {{{$t :date}}}" } ] } diff --git a/testdata/message2/spec/functions/datetime.json b/testdata/message2/spec/functions/datetime.json index bdfea3096cda..758a8bbaa004 100644 --- a/testdata/message2/spec/functions/datetime.json +++ b/testdata/message2/spec/functions/datetime.json @@ -1,9 +1,10 @@ { + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", "scenario": "Datetime function", "description": "The built-in formatter for datetimes.", "defaultTestProperties": { "locale": "en-US", - "expErrors": [] + "expErrors": false }, "tests": [ { @@ -13,8 +14,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{$x :datetime}", @@ -29,8 +29,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{horse :datetime}", @@ -39,8 +38,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{|2006-01-02T15:04:06| :datetime}" diff --git a/testdata/message2/spec/functions/integer.json b/testdata/message2/spec/functions/integer.json index c8e75077a221..4ea96941e179 100644 --- a/testdata/message2/spec/functions/integer.json +++ b/testdata/message2/spec/functions/integer.json @@ -19,7 +19,7 @@ "exp": "hello 4" }, { - "src": ".match {$foo :integer} one {{one}} * {{other}}", + "src": ".input {$foo :integer} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", diff --git a/testdata/message2/spec/functions/number.json b/testdata/message2/spec/functions/number.json index 1b81c705622b..2b00d83e4957 100644 --- a/testdata/message2/spec/functions/number.json +++ b/testdata/message2/spec/functions/number.json @@ -1,4 +1,5 @@ { + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", "scenario": "Number function", "description": "The built-in formatter for numbers.", "defaultTestProperties": { @@ -24,8 +25,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "invalid number literal {|.1| :number}", @@ -34,8 +34,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "invalid number literal {|1.| :number}", @@ -44,8 +43,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "invalid number literal {|01| :number}", @@ -54,8 +52,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "invalid number literal {|+1| :number}", @@ -64,8 +61,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "invalid number literal {|0x1| :number}", @@ -74,8 +70,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "hello {:number}", @@ -84,8 +79,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "hello {4.2 :number minimumFractionDigits=2}", @@ -148,8 +142,7 @@ { "type": "bad-option" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": ".local $foo = {$bar :number} {{bar {$foo}}}", @@ -164,8 +157,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": ".input {$foo :number} {{bar {$foo}}}", @@ -200,8 +192,7 @@ { "type": "bad-option" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": ".input {$foo :number} {{bar {$foo}}}", @@ -216,176 +207,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" - }, - { - "src": ".match {$foo :number} one {{one}} * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".match {$foo :number} 1 {{=1}} one {{one}} * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "=1" - }, - { - "src": ".match {$foo :number} one {{one}} 1 {{=1}} * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "=1" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - }, - { - "name": "bar", - "value": 1 - } - ], - "exp": "one one" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - }, - { - "name": "bar", - "value": 2 - } - ], - "exp": "one other" - }, - { - "src": ".match {$foo :number} {$bar :number} one one {{one one}} one * {{one other}} * * {{other}}", - "params": [ - { - "name": "foo", - "value": 2 - }, - { - "name": "bar", - "value": 2 - } - ], - "exp": "other" - }, - { - "src": ".input {$foo :number} .match {$foo} one {{one}} * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".local $foo = {$bar :number} .match {$foo} one {{one}} * {{other}}", - "params": [ - { - "name": "bar", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".input {$foo :number} .local $bar = {$foo} .match {$bar} one {{one}} * {{other}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".input {$bar :number} .match {$bar} one {{one}} * {{other}}", - "params": [ - { - "name": "bar", - "value": 2 - } - ], - "exp": "other" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": [ - { - "name": "bar", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".input {$bar} .match {$bar :number} one {{one}} * {{other}}", - "params": [ - { - "name": "bar", - "value": 2 - } - ], - "exp": "other" - }, - { - "src": ".input {$none} .match {$foo :number} one {{one}} * {{{$none}}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", - "params": [ - { - "name": "foo", - "value": 1 - } - ], - "exp": "one" - }, - { - "src": ".local $bar = {$none} .match {$foo :number} one {{one}} * {{{$bar}}}", - "params": [ - { - "name": "foo", - "value": 2 - } - ], - "exp": "{$none}", - "expErrors": [ - { - "type": "unresolved-variable" - } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{42 :number @foo @bar=13}", diff --git a/testdata/message2/spec/functions/string.json b/testdata/message2/spec/functions/string.json index 5858079b99a6..1deb2ec66ce7 100644 --- a/testdata/message2/spec/functions/string.json +++ b/testdata/message2/spec/functions/string.json @@ -1,4 +1,5 @@ { + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", "scenario": "String function", "description": "The built-in formatter for strings.", "defaultTestProperties": { @@ -6,7 +7,7 @@ }, "tests": [ { - "src": ".match {$foo :string} |1| {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo |1| {{one}} * {{other}}", "params": [ { "name": "foo", @@ -16,18 +17,17 @@ "exp": "one" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", "value": 1 } ], - "exp": "one", - "ignoreJava": ":string doesn't stringify numbers?" + "exp": "one" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "params": [ { "name": "foo", @@ -35,17 +35,16 @@ } ], "exp": "other", - "ignoreCpp": "Can't handle null value for input variable" + "ignoreCpp": "Explicit null doesn't work" }, { - "src": ".match {$foo :string} 1 {{one}} * {{other}}", + "src": ".input {$foo :string} .match $foo 1 {{one}} * {{other}}", "exp": "other", "expErrors": [ { "type": "unresolved-variable" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] } ] } diff --git a/testdata/message2/spec/functions/time.json b/testdata/message2/spec/functions/time.json index 845934a5e16a..416d18a3efef 100644 --- a/testdata/message2/spec/functions/time.json +++ b/testdata/message2/spec/functions/time.json @@ -1,9 +1,10 @@ { + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", "scenario": "Time function", "description": "The built-in formatter for times.", "defaultTestProperties": { "locale": "en-US", - "expErrors": [] + "expErrors": false }, "tests": [ { @@ -13,8 +14,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{horse :time}", @@ -23,8 +23,7 @@ { "type": "bad-operand" } - ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + ] }, { "src": "{|2006-01-02T15:04:06| :time}" @@ -36,8 +35,7 @@ "src": ".local $t = {|2006-01-02T15:04:06| :time style=medium} {{{$t :time}}}" }, { - "src": ".local $d = {|2006-01-02T15:04:06| :date} {{{$d :time}}}", - "ignoreJava": "ICU4J doesn't support this kind of composition" + "src": ".local $d = {|2006-01-02T15:04:06| :date} {{{$d :time}}}" } ] } diff --git a/testdata/message2/spec/pattern-selection.json b/testdata/message2/spec/pattern-selection.json new file mode 100644 index 000000000000..29dc146c1907 --- /dev/null +++ b/testdata/message2/spec/pattern-selection.json @@ -0,0 +1,120 @@ +{ + "$schema": "https://raw.githubusercontent.com/unicode-org/message-format-wg/main/test/schemas/v0/tests.schema.json", + "scenario": "Pattern selection", + "description": "Tests for pattern selection", + "defaultTestProperties": { + "locale": "und" + }, + "tests": [ + { + "src": ".local $x = {1 :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1" + }, + { + "src": ".local $x = {0 :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other" + }, + { + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".input {$x :test:select} .local $y = {$x} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1" + }, + { + "src": ".input {$x :test:select} .local $y = {$x} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 2 }], + "exp": "other" + }, + { + "src": ".local $x = {1 :test:select decimalPlaces=1} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1.0" + }, + { + "src": ".local $x = {1 :test:select decimalPlaces=1} .match $x 1 {{1}} 1.0 {{1.0}} * {{other}}", + "exp": "1.0" + }, + { + "src": ".local $x = {1 :test:select decimalPlaces=9} .match $x 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "exp": "bad-option-value", + "expErrors": [{ "type": "bad-option" }, { "type": "bad-selector" }] + }, + { + "src": ".input {$x :test:select} .local $y = {$x :test:select decimalPlaces=1} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select decimalPlaces=1} .local $y = {$x :test:select} .match $y 1.0 {{1.0}} 1 {{1}} * {{other}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "1.0" + }, + { + "src": ".input {$x :test:select decimalPlaces=9} .local $y = {$x :test:select decimalPlaces=1} .match $y 1.0 {{1.0}} 1 {{1}} * {{bad-option-value}}", + "params": [{ "name": "x", "value": 1 }], + "exp": "bad-option-value", + "expErrors": [ + { "type": "bad-option" }, + { "type": "bad-operand" }, + { "type": "bad-selector" } + ] + }, + { + "src": ".local $x = {1 :test:select fails=select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".local $x = {1 :test:select fails=format} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "1" + }, + { + "src": ".local $x = {1 :test:format} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".input {$x :test:select} .match $x 1.0 {{1.0}} 1 {{1}} * {{other}}", + "exp": "other", + "expErrors": [ + { "type": "unresolved-variable" }, + { "type": "bad-operand" }, + { "type": "bad-selector" } + ] + }, + { + "src": ".local $x = {1 :test:select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,1" + }, + { + "src": ".local $x = {1 :test:select} .local $y = {0 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,*" + }, + { + "src": ".local $x = {0 :test:select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,1" + }, + { + "src": ".local $x = {0 :test:select} .local $y = {0 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,*" + }, + { + "src": ".local $x = {1 :test:select fails=select} .local $y = {1 :test:select} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "*,1", + "expErrors": [{ "type": "bad-selector" }] + }, + { + "src": ".local $x = {1 :test:select} .local $y = {1 :test:format} .match $x $y 1 1 {{1,1}} 1 * {{1,*}} * 1 {{*,1}} * * {{*,*}}", + "exp": "1,*", + "expErrors": [{ "type": "bad-selector" }] + } + ] +} diff --git a/testdata/message2/spec/syntax-errors.json b/testdata/message2/spec/syntax-errors.json index 34d9aa484572..00d0420f46f7 100644 --- a/testdata/message2/spec/syntax-errors.json +++ b/testdata/message2/spec/syntax-errors.json @@ -122,6 +122,9 @@ { "src": "bad {:placeholder @attribute=@foo}" }, + { + "src": "bad {:placeholder @attribute=$foo}" + }, { "src": "{ @misplaced = attribute }" }, @@ -155,26 +158,90 @@ { "src": ".local $bar = |foo| {{_}}" }, - { - "src": ".match {#foo} * {{foo}}" - }, - { - "src": ".match {} * {{foo}}" - }, - { - "src": ".match {|foo| :x} {|bar| :x} ** {{foo}}" - }, - { - "src": ".match * {{foo}}" - }, - { - "src": ".match {|x| :x} * foo" - }, - { - "src": ".match {|x| :x} * {{foo}} extra" - }, - { - "src": ".match |x| * {{foo}}" - } + { "src": ".match {{foo}}" }, + { "src": ".match * {{foo}}" }, + { "src": ".match x * {{foo}}" }, + { "src": ".match |x| * {{foo}}" }, + { "src": ".match :x * {{foo}}" }, + { "src": ".match {$foo} * {{foo}}" }, + { "src": ".match {#foo} * {{foo}}" }, + { "src": ".input {$x :x} .match {$x} * {{foo}}" }, + { "src": ".input {$x :x} .match$x * {{foo}}" }, + { "src": ".input {$x :x} .match $x* {{foo}}" }, + { "src": ".input {$x :x} .match $x|x| {{foo}} * {{foo}}" }, + { "src": ".input {$x :x} .local $y = {y :y} .match $x$y * * {{foo}}" }, + { "src": ".input {$x :x} .local $y = {y :y} .match $x $y ** {{foo}}" }, + { "src": ".input {$x :x} .match $x" }, + { "src": ".input {$x :x} .match $x *" }, + { "src": ".input {$x :x} .match $x * foo" }, + { "src": ".input {$x :x} .match $x * {{foo}} extra" }, + { "src": ".n{a}{{}}" }, + { "src": "{^}" }, + { "src": "{!}" }, + { "src": ".n .{a}{{}}" }, + { "src": ".n. {a}{{}}" }, + { "src": ".n.{a}{b}{{}}" }, + { "src": "{!.}" }, + { "src": "{! .}" }, + { "src": "{%}" }, + { "src": "{*}" }, + { "src": "{+}" }, + { "src": "{<}" }, + { "src": "{>}" }, + { "src": "{?}" }, + { "src": "{~}" }, + { "src": "{^.}" }, + { "src": "{^ .}" }, + { "src": "{&}" }, + { "src": "{!.\\{}" }, + { "src": "{!. \\{}" }, + { "src": "{!|a|}" }, + { "src": "foo {+reserved}" }, + { "src": "foo {&private}" }, + { "src": "foo {?reserved @a @b=c}" }, + { "src": ".foo {42} {{bar}}" }, + { "src": ".foo{42}{{bar}}" }, + { "src": ".foo |}lit{| {42}{{bar}}" }, + { "src": ".i {1} {{}}" }, + { "src": ".l $y = {|bar|} {{}}" }, + { "src": ".l $x.y = {|bar|} {{}}" }, + { "src": "hello {|4.2| %number}" }, + { "src": "hello {|4.2| %n|um|ber}" }, + { "src": "{+42}" }, + { "src": "hello {|4.2| &num|be|r}" }, + { "src": "hello {|4.2| ^num|be|r}" }, + { "src": "hello {|4.2| +num|be|r}" }, + { "src": "hello {|4.2| ?num|be||r|s}" }, + { "src": "hello {|foo| !number}" }, + { "src": "hello {|foo| *number}" }, + { "src": "hello {?number}" }, + { "src": "{xyzz }" }, + { "src": "hello {$foo ~xyzz }" }, + { "src": "hello {$x xyzz }" }, + { "src": "{ !xyzz }" }, + { "src": "{~xyzz }" }, + { "src": "{ num x \\\\ abcde |aaa||3.14||42| r }" }, + { "src": "hello {$foo >num x \\\\ abcde |aaa||3.14| |42| r }" }, + { "src" : ".input{ $n ~ }{{{$n}}}" } ] } diff --git a/testdata/message2/spec/syntax.json b/testdata/message2/spec/syntax.json index 558fc64062b4..27b74b2f302c 100644 --- a/testdata/message2/spec/syntax.json +++ b/testdata/message2/spec/syntax.json @@ -26,6 +26,11 @@ "src": "\\\\", "exp": "\\" }, + { + "description": "message -> simple-message -> simple-start pattern -> 1*escaped-char", + "src": "\\\\\\{\\|\\}", + "exp": "\\{|}" + }, { "description": "message -> simple-message -> simple-start pattern -> simple-start-char pattern -> ... -> simple-start-char *text-char placeholder", "src": "hello {world}", @@ -68,8 +73,7 @@ "type": "unresolved-variable" } ], - "exp": "hello {$place}", - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "exp": "hello {$place}" }, { "description": "message -> simple-message -> simple-start pattern -> placeholder -> expression -> literal-expression -> \"{\" literal \"}\"", @@ -80,50 +84,43 @@ "description": "... -> literal-expression -> \"{\" literal s annotation \"}\" -> \"{\" literal s function \"}\" -> \"{\" literal s \":\" identifier \"}\" -> \"{\" literal s \":\" name \"}\"", "src": "{a :f}", "exp": "{|a|}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... -> \"{\" literal s \":\" namespace \":\" name \"}\"", "src": "{a :u:f}", "exp": "{|a|}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "message -> simple-message -> simple-start pattern -> placeholder -> expression -> variable-expression -> \"{\" variable \"}\"", "src": "{$x}", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }] }, { "description": "... -> variable-expression -> \"{\" variable s annotation \"}\" -> \"{\" variable s function \"}\" -> \"{\" variable s \":\" identifier \"}\" -> \"{\" variable s \":\" name \"}\"", "src": "{$x :f}", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }] }, { "description": "... -> \"{\" variable s \":\" namespace \":\" name \"}\"", "src": "{$x :u:f}", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }, { "type": "unknown-function" }] }, { "description": "... -> annotation-expression -> function -> \"{\" \":\" namespace \":\" name \"}\"", "src": "{:u:f}", "exp": "{:u:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... -> annotation-expression -> function -> \"{\" \":\" name \"}\"", "src": "{:f}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "message -> complex-message -> complex-body -> quoted-pattern -> \"{{\" pattern \"}}\" -> \"{{\"\"}}\"", @@ -173,11 +170,10 @@ "exp": "" }, { - "description": "message -> complex-message -> complex-body -> matcher -> match-statement variant -> match selector key quoted-pattern -> \".match\" expression literal quoted-pattern", - "src": ".match{a :f}a{{}}*{{}}", + "description": "message -> complex-message -> complex-body -> ... -> matcher -> match-statement variant -> match selector key quoted-pattern -> \".match\" variable literal quoted-pattern", + "src": ".local $a={a :f}.match $a a{{}}*{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "... input-declaration -> input s variable-expression ...", @@ -199,40 +195,57 @@ "src": ".local $x = {a}{{}}", "exp": "" }, + { + "description": "input-declaration-like content in complex-message", + "src": "{{.input {$x}}}", + "params": [{ "name": "x", "value": "X" }], + "exp": ".input X" + }, + { + "description": "local-declaration-like content in complex-message with leading whitespace", + "src": "{{ .local $x = {$y}}}", + "params": [{ "name": "y", "value": "Y" }], + "exp": " .local $x = Y" + }, { "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector selector variant -> match selector selector variant key s key quoted-pattern", - "src": ".match{a :f}{b :f}a b{{}}* *{{}}", + "src": ".local $a={a :f}.local $b={b :f}.match $a $b a b{{}}* *{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [ + { "type": "unknown-function" }, + { "type": "bad-selector" }, + { "type": "unknown-function" }, + { "type": "bad-selector" } + ] }, { "description": "... matcher -> match-statement [s] variant -> match 1*([s] selector) variant -> match selector variant variant ...", - "src": ".match{a :f}a{{}}b{{}}*{{}}", + "src": ".local $a={a :f}.match $a a{{}}b{{}}*{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "... variant -> key s quoted-pattern -> ...", - "src": ".match{a :f}a {{}}*{{}}", + "src": ".local $a={a :f}.match $a a {{}}*{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "... variant -> key s key s quoted-pattern -> ...", - "src": ".match{a :f}{b :f}a b {{}}* *{{}}", + "src": ".local $a={a :f}.local $b={b :f}.match $a $b a b {{}}* *{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [ + { "type": "unknown-function" }, + { "type": "bad-selector" }, + { "type": "unknown-function" }, + { "type": "bad-selector" } + ] }, { "description": "... key -> \"*\" ...", - "src": ".match{a :f}*{{}}", + "src": ".local $a={a :f}.match $a *{{}}", "exp": "", - "expErrors": [ { "type": "unknown-function" } ], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }, { "type": "bad-selector" }] }, { "description": "simple-message -> simple-start pattern -> placeholder -> expression -> literal-expression -> \"{\" s literal \"}\"", @@ -253,43 +266,37 @@ "description": "simple-message -> simple-start pattern -> placeholder -> expression -> variable-expression -> \"{\" s variable \"}\"", "src": "{ $x}", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }] }, { "description": "... variable-expression -> \"{\" variable s attribute \"}\" -> \"{\" variable s \"@\" identifier \"}\"", "src": "{$x @c}", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }] }, { "description": "... -> variable-expression -> \"{\" variable s \"}\"", "src": "{$x }", "exp": "{$x}", - "expErrors": [{ "type": "unresolved-variable" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unresolved-variable" }] }, { "description": "simple-message -> simple-start pattern -> placeholder -> expression -> annotation-expression -> \"{\" s annotation \"}\"", "src": "{ :f}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... annotation-expression -> \"{\" annotation s attribute \"}\" -> \"{\" annotation s \"@\" identifier \"}\"", "src": "{:f @c}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... -> annotation-expression -> \"{\" annotation s \"}\"", "src": "{:f }", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "message -> simple-message -> simple-start pattern -> placeholder -> markup -> \"{\" s \"#\" identifier \"}\"", @@ -360,29 +367,25 @@ "description": "... annotation-expression -> function -> \":\" identifier option", "src": "{:f k=v}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... option -> identifier s \"=\" literal", "src": "{:f k =v}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... option -> identifier \"=\" s literal", "src": "{:f k= v}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... option -> identifier s \"=\" s literal", "src": "{:f k = v}", "exp": "{:f}", - "expErrors": [{ "type": "unknown-function" }], - "ignoreJava": "See https://github.com/unicode-org/message-format-wg/issues/782" + "expErrors": [{ "type": "unknown-function" }] }, { "description": "... attribute -> \"@\" identifier \"=\" literal ...", @@ -405,8 +408,8 @@ "exp": "a" }, { - "description": "... attribute -> \"@\" identifier s \"=\" s variable ...", - "src": "{42 @foo=$bar}", + "description": "... attribute -> \"@\" identifier s \"=\" s quoted-literal ...", + "src": "{42 @foo=|bar|}", "exp": "42", "expParts": [ { @@ -432,9 +435,9 @@ "exp": "\\" }, { - "description": "... quoted-literal -> \"|\" quoted-char escaped-char \"|\"", - "src": "{|a\\\\|}", - "exp": "a\\" + "description": "... quoted-literal -> \"|\" quoted-char 1*escaped-char \"|\"", + "src": "{|a\\\\\\{\\|\\}|}", + "exp": "a\\{|}" }, { "description": "... unquoted-literal -> number-literal -> %x30", diff --git a/testdata/message2/valid-tests.json b/testdata/message2/valid-tests.json index 4b1ce8d67fb6..0f062116b733 100644 --- a/testdata/message2/valid-tests.json +++ b/testdata/message2/valid-tests.json @@ -141,7 +141,7 @@ }, { "comment": "Trailing whitespace after match is valid", - "src": ".match {1 :string} * {{}} ", + "src": ".local $x = {1 :string} .match $x * {{}} ", "exp": "" }, {