/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 |