// © 2024 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html #include "unicode/utypes.h" #ifndef U_HIDE_DEPRECATED_API #ifndef MESSAGEFORMAT2_EVALUATION_H #define MESSAGEFORMAT2_EVALUATION_H #if U_SHOW_CPLUSPLUS_API /** * \file * \brief C++ API: Formats messages using the draft MessageFormat 2.0. */ #if !UCONFIG_NO_NORMALIZATION #if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_MF2 #include "unicode/messageformat2_arguments.h" #include "unicode/messageformat2_data_model.h" #include "unicode/messageformat2_function_registry.h" #include "messageformat2_errors.h" // Auxiliary data structures used during formatting a message U_NAMESPACE_BEGIN namespace message2 { using namespace data_model; // PrioritizedVariant // For how this class is used, see the references to (integer, variant) tuples // in https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection class PrioritizedVariant : public UObject { public: PrioritizedVariant() = default; PrioritizedVariant(PrioritizedVariant&&) = default; PrioritizedVariant& operator=(PrioritizedVariant&&) noexcept = default; UBool operator<(const PrioritizedVariant&) const; int32_t priority; /* const */ SelectorKeys keys; /* const */ Pattern pat; PrioritizedVariant(uint32_t p, const SelectorKeys& k, const Pattern& pattern) noexcept : priority(p), keys(k), pat(pattern) {} virtual ~PrioritizedVariant(); }; // class PrioritizedVariant static inline int32_t comparePrioritizedVariants(UElement left, UElement right) { const PrioritizedVariant& tuple1 = *(static_cast(left.pointer)); const PrioritizedVariant& tuple2 = *(static_cast(right.pointer)); if (tuple1 < tuple2) { return -1; } if (tuple1.priority == tuple2.priority) { return 0; } return 1; } // Closures and environments // ------------------------- class Environment; // A closure represents the right-hand side of a variable // declaration, along with an environment giving values // to its free variables class Closure : public UMemory { public: const Expression& getExpr() const { return expr; } const Environment& getEnv() const { return env; } Closure(const Expression& expression, const Environment& environment) : expr(expression), env(environment) {} Closure(Closure&&) = default; virtual ~Closure(); private: // An unevaluated expression const Expression& expr; // The environment mapping names used in this // expression to other expressions const Environment& env; }; // An environment is represented as a linked chain of // non-empty environments, terminating at an empty environment. // It's searched using linear search. class Environment : public UMemory { public: virtual bool has(const VariableName&) const = 0; virtual const Closure& lookup(const VariableName&) const = 0; static Environment* create(UErrorCode&); static Environment* create(const VariableName&, Closure&&, Environment*, UErrorCode&); virtual ~Environment(); }; class NonEmptyEnvironment; class EmptyEnvironment : public Environment { public: EmptyEnvironment() = default; virtual ~EmptyEnvironment(); private: friend class Environment; bool has(const VariableName&) const override; const Closure& lookup(const VariableName&) const override; static EmptyEnvironment* create(UErrorCode&); static NonEmptyEnvironment* create(const VariableName&, Closure&&, Environment*, UErrorCode&); }; class NonEmptyEnvironment : public Environment { private: friend class Environment; bool has(const VariableName&) const override; const Closure& lookup(const VariableName&) const override; static NonEmptyEnvironment* create(const VariableName&, Closure&&, const Environment*, UErrorCode&); virtual ~NonEmptyEnvironment(); private: friend class Environment; NonEmptyEnvironment(const VariableName& v, Closure&& c, Environment* e) : var(v), rhs(std::move(c)), parent(e) {} // Maps VariableName onto Closure* // Chain of linked environments VariableName var; Closure rhs; const LocalPointer parent; }; // The context contains all the information needed to process // an entire message: arguments, formatter cache, and error list class MessageFormatter; class MessageContext : public UMemory { public: MessageContext(const MessageArguments&, const StaticErrors&, UErrorCode&); const Formattable* getGlobal(const MessageFormatter&, const VariableName&, UErrorCode&) const; // If any errors were set, update `status` accordingly void checkErrors(UErrorCode& status) const; DynamicErrors& getErrors() { return errors; } virtual ~MessageContext(); private: const MessageArguments& arguments; // External message arguments // Errors accumulated during parsing/formatting DynamicErrors errors; }; // class MessageContext // InternalValue // ---------------- class InternalValue : public UObject { public: const FunctionName& getFunctionName() const { return name; } bool canSelect() const { return selector != nullptr; } const Selector* getSelector(UErrorCode&) const; FormattedPlaceholder forceFormatting(DynamicErrors& errs, UErrorCode& errorCode); void forceSelection(DynamicErrors& errs, const UnicodeString* keys, int32_t keysLen, UnicodeString* prefs, int32_t& prefsLen, UErrorCode& errorCode); // Needs to be deep-copyable and movable virtual ~InternalValue(); InternalValue(FormattedPlaceholder&&); // Formatter and selector may be null InternalValue(InternalValue*, FunctionOptions&&, const FunctionName&, const Formatter*, const Selector*); const UnicodeString& getFallback() const; bool isFallback() const; bool hasNullOperand() const; // Can't be used anymore after calling this FormattedPlaceholder takeArgument(UErrorCode& errorCode); InternalValue(InternalValue&& other) { *this = std::move(other); } InternalValue& operator=(InternalValue&& other) noexcept; private: // InternalValue is owned (if present) std::variant argument; FunctionOptions options; FunctionName name; const Selector* selector; // May be null const Formatter* formatter; // May be null, but one or the other should be non-null unless argument is a FormattedPlaceholder }; // class InternalValue } // namespace message2 U_NAMESPACE_END #endif /* #if !UCONFIG_NO_MF2 */ #endif /* #if !UCONFIG_NO_FORMATTING */ #endif /* #if !UCONFIG_NO_NORMALIZATION */ #endif /* U_SHOW_CPLUSPLUS_API */ #endif // MESSAGEFORMAT2_EVALUATION_H #endif // U_HIDE_DEPRECATED_API // eof