Coverage Report

Created: 2026-10-31 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/messageformat2_evaluation.h
Line
Count
Source
1
// © 2024 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
4
#include "unicode/utypes.h"
5
6
#ifndef U_HIDE_DEPRECATED_API
7
8
#ifndef MESSAGEFORMAT2_EVALUATION_H
9
#define MESSAGEFORMAT2_EVALUATION_H
10
11
#if U_SHOW_CPLUSPLUS_API
12
13
/**
14
 * \file
15
 * \brief C++ API: Formats messages using the draft MessageFormat 2.0.
16
 */
17
#if !UCONFIG_NO_NORMALIZATION
18
19
#if !UCONFIG_NO_FORMATTING
20
21
#if !UCONFIG_NO_MF2
22
23
#include "unicode/messageformat2_arguments.h"
24
#include "unicode/messageformat2_data_model.h"
25
#include "unicode/messageformat2_function_registry.h"
26
#include "messageformat2_errors.h"
27
28
// Auxiliary data structures used during formatting a message
29
30
U_NAMESPACE_BEGIN
31
32
namespace message2 {
33
34
    namespace functions {
35
    static constexpr std::u16string_view CURRENCY = u"currency";
36
    static constexpr std::u16string_view DATETIME = u"datetime";
37
    static constexpr std::u16string_view DATE = u"date";
38
    static constexpr std::u16string_view TIME = u"time";
39
    static constexpr std::u16string_view NUMBER = u"number";
40
    static constexpr std::u16string_view INTEGER = u"integer";
41
    static constexpr std::u16string_view OFFSET = u"offset";
42
    static constexpr std::u16string_view PER_CENT = u"percent";
43
    static constexpr std::u16string_view TEST_FUNCTION = u"test:function";
44
    static constexpr std::u16string_view TEST_FORMAT = u"test:format";
45
    static constexpr std::u16string_view TEST_SELECT = u"test:select";
46
    static constexpr std::u16string_view STRING = u"string";
47
    }
48
49
    using namespace data_model;
50
51
    class Closure;
52
    class Environment;
53
54
    // InternalValue represents an intermediate value in the message
55
    // formatter.
56
    // It has four possible states:
57
    // 1. Fallback Value. A fallback value
58
    // is a string that serves as a replacement for expressions whose evaluation
59
    // caused an error. Fallback values are not passed to functions.
60
    // 2. Closure, representing the unevaluated right-hand side of a declaration.
61
    // 3. Evaluated Value (FunctionValue), representing an evaluated declaration.
62
    // 4. Indirection (const InternalValue*), representing a shared reference to another
63
    //    InternalValue. Note that all InternalValues are owned by the global
64
    //    environment.
65
    /*
66
      Example:
67
68
      .local $x = {$y}
69
      .local $z = {1 :number}
70
      .local $a = {$z}
71
      {{ {$x} {$z} {$a} }}
72
73
      If this message is formatted with no arguments,
74
      initially, x, z and a are all bound to Closures.
75
      When the value of x is demanded by the pattern, the contents of x's value
76
      are updated to a Fallback Value (because its RHS contains an unbound variable).
77
      When the value of z is demanded, the contents of z's value are updated to
78
      an Evaluated Value representing the result of :number on the operand.
79
      When the value of a is demanded, the contents of a's value are updated to
80
      an Indirection, pointing to z's value.
81
82
      Indirections are used so that a FunctionValue can be uniquely owned by an
83
      InternalValue. Since all InternalValues are owned by the global Environment,
84
      it's safe to use these non-owned pointers.
85
     */
86
    class InternalValue : public UObject {
87
    public:
88
0
        bool isFallback() const { return std::holds_alternative<UnicodeString>(val); }
89
        bool isNullOperand() const;
90
        bool isEvaluated() const;
91
        bool isClosure() const;
92
        virtual bool isSelectable() const;
93
94
0
        Closure& asClosure() {
95
0
            U_ASSERT(isClosure());
96
0
            return **std::get_if<LocalPointer<Closure>>(&val);
97
0
        }
98
        const FunctionValue* getValue(UErrorCode& status) const;
99
        FunctionValue* wrapInVariableValue(UErrorCode& status) const;
100
101
0
        UnicodeString asFallback() const { return fallbackString; }
102
103
        static LocalPointer<InternalValue> null(UErrorCode& status);
104
        static LocalPointer<InternalValue> fallback(const UnicodeString& s, UErrorCode& status);
105
        // Adopts `c`
106
        static InternalValue closure(Closure* c, const UnicodeString& s);
107
108
        // Updates the mutable contents of this InternalValue
109
        void update(InternalValue&);
110
        void update(LocalPointer<FunctionValue>);
111
        void update(const UnicodeString&);
112
113
0
        InternalValue() : val(UnicodeString()) {}
114
        explicit InternalValue(FunctionValue* v, const UnicodeString& fb);
115
        InternalValue& operator=(InternalValue&&);
116
        InternalValue(InternalValue&&);
117
        virtual ~InternalValue();
118
    private:
119
        UnicodeString fallbackString;
120
        std::variant<UnicodeString, // Fallback value
121
                     LocalPointer<Closure>, // Unevaluated thunk
122
                     LocalPointer<FunctionValue>, // Evaluated value
123
                     const InternalValue*> val; // Indirection to another value -- Not owned
124
        // Null operand constructor
125
        explicit InternalValue(UErrorCode& status);
126
        // Fallback constructor
127
        explicit InternalValue(const UnicodeString& fb)
128
0
            : fallbackString(fb), val(fb) {}
129
        // Closure (unevaluated) constructor
130
        explicit InternalValue(Closure* c, UnicodeString fallbackStr)
131
0
            : fallbackString(fallbackStr), val(LocalPointer<Closure>(c)) {}
132
        bool isIndirection() const;
133
    }; // class InternalValue
134
135
136
    // A BaseValue wraps a literal value or argument value so it can be used
137
    // in a context that expects a FunctionValue.
138
    class BaseValue : public FunctionValue {
139
        public:
140
            static BaseValue* create(const Locale&, const UnicodeString&, const Formattable&, bool, UErrorCode&);
141
            // Apply default formatters to the argument value
142
            UnicodeString formatToString(UErrorCode&) const override;
143
0
            UBool isSelectable() const override { return true; }
144
0
            UBool wasCreatedFromLiteral() const override { return fromLiteral; }
145
0
            BaseValue() {}
146
            BaseValue(BaseValue&&);
147
            BaseValue& operator=(BaseValue&&) noexcept;
148
       private:
149
            Locale locale;
150
            bool fromLiteral = false;
151
152
            BaseValue(const Locale&, const UnicodeString&, const Formattable&, bool);
153
    }; // class BaseValue
154
155
    // A NullValue represents the absence of an argument.
156
    class NullValue : public FunctionValue {
157
        public:
158
0
            virtual UBool isNullOperand() const { return true; }
159
    }; // class NullValue
160
161
    // A VariableValue wraps another FunctionValue and its sole purpose
162
    // is to override the wasCreatedFromLiteral() method to always return false.
163
    // This makes it easy to implement .local $foo = {exact}: the RHS returns a BaseValue
164
    // such that wasCreatedFromLiteral() is true, but then we can wrap it in a VariableValue,
165
    // which will always return false for this method.
166
    class VariableValue : public FunctionValue {
167
        public:
168
            static VariableValue* create(const FunctionValue*, UErrorCode&);
169
0
            UBool wasCreatedFromLiteral() const override { return false; }
170
0
            UnicodeString formatToString(UErrorCode& status) const override { return underlyingValue->formatToString(status); }
171
0
            const Formattable& unwrap() const override { return underlyingValue->unwrap(); }
172
0
            const FunctionOptions& getResolvedOptions() const override { return underlyingValue->getResolvedOptions(); }
173
0
            UMFDirectionality getDirection() const override { return underlyingValue->getDirection(); }
174
0
            UMFBidiOption getDirectionAnnotation() const override { return underlyingValue->getDirectionAnnotation(); }
175
0
            UBool isSelectable() const override { return underlyingValue->isSelectable(); }
176
0
            UBool isNullOperand() const override { return underlyingValue->isNullOperand(); }
177
            void selectKeys(const UnicodeString* keys,
178
                            int32_t keysLen,
179
                            int32_t* prefs,
180
                            int32_t& prefsLen,
181
0
                            UErrorCode& status) const override { return underlyingValue->selectKeys(keys, keysLen, prefs, prefsLen, status); }
182
0
            const UnicodeString& getFunctionName() const override { return underlyingValue->getFunctionName(); }
183
0
            const UnicodeString& getFallback() const override { return underlyingValue->getFallback(); }
184
0
            VariableValue() {}
185
            virtual ~VariableValue();
186
            VariableValue(VariableValue&&);
187
            VariableValue& operator=(VariableValue&&) noexcept;
188
        private:
189
            const FunctionValue* underlyingValue;
190
191
            VariableValue(const FunctionValue*);
192
    }; // class VariableValue
193
194
    // PrioritizedVariant
195
196
    // For how this class is used, see the references to (integer, variant) tuples
197
    // in https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection
198
    class PrioritizedVariant : public UObject {
199
    public:
200
        PrioritizedVariant() = default;
201
        PrioritizedVariant(PrioritizedVariant&&) = default;
202
        PrioritizedVariant& operator=(PrioritizedVariant&&) noexcept = default;
203
        UBool operator<(const PrioritizedVariant&) const;
204
        int32_t priority;
205
        /* const */ SelectorKeys keys;
206
        /* const */ Pattern pat;
207
        PrioritizedVariant(uint32_t p,
208
                           const SelectorKeys& k,
209
0
                           const Pattern& pattern) noexcept : priority(p), keys(k), pat(pattern) {}
210
        virtual ~PrioritizedVariant();
211
    }; // class PrioritizedVariant
212
213
0
    static inline int32_t comparePrioritizedVariants(UElement left, UElement right) {
214
0
        const PrioritizedVariant& tuple1 = *(static_cast<const PrioritizedVariant*>(left.pointer));
215
0
        const PrioritizedVariant& tuple2 = *(static_cast<const PrioritizedVariant*>(right.pointer));
216
0
        if (tuple1 < tuple2) {
217
0
            return -1;
218
0
        }
219
0
        if (tuple1.priority == tuple2.priority) {
220
0
            return 0;
221
0
        }
222
0
        return 1;
223
0
    }
Unexecuted instantiation: messageformat2_formatter.cpp:icu_80::message2::comparePrioritizedVariants(UElement, UElement)
Unexecuted instantiation: messageformat2_function_registry.cpp:icu_80::message2::comparePrioritizedVariants(UElement, UElement)
Unexecuted instantiation: messageformat2_checker.cpp:icu_80::message2::comparePrioritizedVariants(UElement, UElement)
Unexecuted instantiation: messageformat2_evaluation.cpp:icu_80::message2::comparePrioritizedVariants(UElement, UElement)
Unexecuted instantiation: messageformat2_arguments.cpp:icu_80::message2::comparePrioritizedVariants(UElement, UElement)
224
225
    // Closures and environments
226
    // -------------------------
227
228
    class Environment;
229
230
    // A closure represents the right-hand side of a variable
231
    // declaration, along with an environment giving values
232
    // to its free variables
233
    class Closure : public UMemory {
234
    public:
235
0
        const Expression& getExpr() const {
236
0
            return expr;
237
0
        }
238
0
        Environment& getEnv() const {
239
0
            return env;
240
0
        }
241
        Closure(Closure&&) = default;
242
        static Closure* create(const Expression&, Environment&, UErrorCode&);
243
244
        virtual ~Closure();
245
    private:
246
247
0
        Closure(const Expression& expression, Environment& environment) : expr(expression), env(environment) {}
248
249
        // An unevaluated expression
250
        const Expression& expr;
251
        // The environment mapping names used in this
252
        // expression to other expressions
253
        Environment& env;
254
    };
255
256
    class NonEmptyEnvironment;
257
258
    // An environment is represented as a linked chain of
259
    // non-empty environments, terminating at an empty environment.
260
    // It's searched using linear search.
261
    class Environment : public UMemory {
262
        public:
263
            virtual bool has(const VariableName&) const = 0;
264
            virtual InternalValue& lookup(const VariableName&) = 0;
265
            virtual InternalValue& bogus() = 0;
266
            // For convenience so that InternalValue::getValue() can return a reference
267
            // in error cases
268
0
            FunctionValue& bogusFunctionValue() { return bogusFunctionVal; }
269
            virtual InternalValue& createFallback(const UnicodeString&, UErrorCode&) = 0;
270
            virtual InternalValue& createNull(UErrorCode&) = 0;
271
            virtual InternalValue& createUnnamed(InternalValue&&, UErrorCode&) = 0;
272
            static Environment* create(UErrorCode&);
273
            static Environment* create(const VariableName&, Closure*, const UnicodeString&,
274
                                       Environment*, UErrorCode&);
275
            virtual ~Environment();
276
277
        private:
278
            FunctionValue bogusFunctionVal;
279
    };
280
281
    // The empty environment includes a "bogus" value to use when an
282
    // InternalValue& is needed (e.g. error conditions),
283
    // and a vector of "unnamed" values, so that the environment can
284
    // own all InternalValues (even those arising from expressions
285
    // that appear directly in a pattern and are not named).
286
    class EmptyEnvironment : public Environment {
287
    public:
288
        EmptyEnvironment(UErrorCode& status);
289
        virtual ~EmptyEnvironment();
290
291
    private:
292
        friend class Environment;
293
294
        bool has(const VariableName&) const override;
295
        InternalValue& lookup(const VariableName&) override;
296
0
        InternalValue& bogus() override { return bogusValue; }
297
        static EmptyEnvironment* create(UErrorCode&);
298
        static NonEmptyEnvironment* create(const VariableName&, InternalValue,
299
                                           Environment*, UErrorCode&);
300
301
        // Creates a fallback value owned by this Environment
302
        InternalValue& createFallback(const UnicodeString&, UErrorCode&) override;
303
        // Creates a null operand owned by this Environment
304
        InternalValue& createNull(UErrorCode&) override;
305
        // Creates an arbitrary value owned by this Environment
306
        InternalValue& createUnnamed(InternalValue&&, UErrorCode&) override;
307
308
        InternalValue& addUnnamedValue(LocalPointer<InternalValue>, UErrorCode&);
309
310
        InternalValue bogusValue; // Used in place of `nullptr` in error conditions
311
        UVector unnamedValues;
312
    };
313
314
    class NonEmptyEnvironment : public Environment {
315
    public:
316
        InternalValue* update(const VariableName&, InternalValue&&);
317
    private:
318
        friend class Environment;
319
320
        bool has(const VariableName&) const override;
321
        InternalValue& lookup(const VariableName&) override;
322
0
        InternalValue& bogus() override { return parent->bogus(); }
323
        static NonEmptyEnvironment* create(const VariableName&, Closure&&, const Environment*, UErrorCode&);
324
        virtual ~NonEmptyEnvironment();
325
    private:
326
        friend class Environment;
327
328
0
        NonEmptyEnvironment(const VariableName& v, InternalValue c, Environment* e) : var(v), rhs(std::move(c)), parent(e) {}
329
330
        InternalValue& createFallback(const UnicodeString&, UErrorCode&) override;
331
        InternalValue& createNull(UErrorCode&) override;
332
        InternalValue& createUnnamed(InternalValue&&, UErrorCode&) override;
333
334
        // Maps VariableName onto Closure*
335
        // Chain of linked environments
336
        VariableName var;
337
        InternalValue rhs;
338
        const LocalPointer<Environment> parent;
339
    };
340
341
    // The context contains all the information needed to process
342
    // an entire message: arguments, formatter cache, and error list
343
344
    class MessageFormatter;
345
346
    class MessageContext : public UMemory {
347
    public:
348
        MessageContext(const MessageArguments&, const StaticErrors&, UErrorCode&);
349
350
        const Formattable* getGlobal(const VariableName&, UErrorCode&) const;
351
352
        // If any errors were set, update `status` accordingly
353
        void checkErrors(UErrorCode& status) const;
354
0
        DynamicErrors& getErrors() { return errors; }
355
356
        virtual ~MessageContext();
357
358
    private:
359
360
        const MessageArguments& arguments; // External message arguments
361
        // Errors accumulated during parsing/formatting
362
        DynamicErrors errors;
363
364
    }; // class MessageContext
365
366
} // namespace message2
367
368
U_NAMESPACE_END
369
370
#endif /* #if !UCONFIG_NO_MF2 */
371
372
#endif /* #if !UCONFIG_NO_FORMATTING */
373
374
#endif /* #if !UCONFIG_NO_NORMALIZATION */
375
376
#endif /* U_SHOW_CPLUSPLUS_API */
377
378
#endif // MESSAGEFORMAT2_EVALUATION_H
379
380
#endif // U_HIDE_DEPRECATED_API
381
// eof