//===- FormatProviders.h - Formatters for common LLVM types -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements format providers for many common LLVM types, for example // allowing precision and width specifiers for scalar and string types. // //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_FORMATPROVIDERS_H #define LLVM_SUPPORT_FORMATPROVIDERS_H #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/FormatVariadicDetails.h" #include "llvm/Support/NativeFormatting.h" #include #include namespace llvm { namespace detail { template struct use_integral_formatter : public std::integral_constant< bool, is_one_of::value> {}; template struct use_char_formatter : public std::integral_constant::value> {}; template struct is_cstring : public std::integral_constant::value> { }; template struct use_string_formatter : public std::integral_constant::value> {}; template struct use_pointer_formatter : public std::integral_constant::value && !is_cstring::value> {}; template struct use_double_formatter : public std::integral_constant::value> {}; class HelperFunctions { protected: static Optional parseNumericPrecision(StringRef Str) { size_t Prec; Optional Result; if (Str.empty()) Result = None; else if (Str.getAsInteger(10, Prec)) { assert(false && "Invalid precision specifier"); Result = None; } else { assert(Prec < 100 && "Precision out of range"); Result = std::min(99u, Prec); } return Result; } static bool consumeHexStyle(StringRef &Str, HexPrintStyle &Style) { if (!Str.startswith_lower("x")) return false; if (Str.consume_front("x-")) Style = HexPrintStyle::Lower; else if (Str.consume_front("X-")) Style = HexPrintStyle::Upper; else if (Str.consume_front("x+") || Str.consume_front("x")) Style = HexPrintStyle::PrefixLower; else if (Str.consume_front("X+") || Str.consume_front("X")) Style = HexPrintStyle::PrefixUpper; return true; } static size_t consumeNumHexDigits(StringRef &Str, HexPrintStyle Style, size_t Default) { Str.consumeInteger(10, Default); if (isPrefixedHexStyle(Style)) Default += 2; return Default; } }; } /// Implementation of format_provider for integral arithmetic types. /// /// The options string of an integral type has the grammar: /// /// integer_options :: [style][digits] /// style :: /// digits :: 0-99 /// /// ========================================================================== /// | style | Meaning | Example | Digits Meaning | /// -------------------------------------------------------------------------- /// | | | Input | Output | | /// ========================================================================== /// | x- | Hex no prefix, lower | 42 | 2a | Minimum # digits | /// | X- | Hex no prefix, upper | 42 | 2A | Minimum # digits | /// | x+ / x | Hex + prefix, lower | 42 | 0x2a | Minimum # digits | /// | X+ / X | Hex + prefix, upper | 42 | 0x2A | Minimum # digits | /// | N / n | Digit grouped number | 123456 | 123,456 | Ignored | /// | D / d | Integer | 100000 | 100000 | Ignored | /// | (empty) | Same as D / d | | | | /// ========================================================================== /// template struct format_provider< T, typename std::enable_if::value>::type> : public detail::HelperFunctions { private: public: static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { HexPrintStyle HS; size_t Digits = 0; if (consumeHexStyle(Style, HS)) { Digits = consumeNumHexDigits(Style, HS, 0); write_hex(Stream, V, HS, Digits); return; } IntegerStyle IS = IntegerStyle::Integer; if (Style.consume_front("N") || Style.consume_front("n")) IS = IntegerStyle::Number; else if (Style.consume_front("D") || Style.consume_front("d")) IS = IntegerStyle::Integer; Style.consumeInteger(10, Digits); assert(Style.empty() && "Invalid integral format style!"); write_integer(Stream, V, Digits, IS); } }; /// Implementation of format_provider for integral pointer types. /// /// The options string of a pointer type has the grammar: /// /// pointer_options :: [style][precision] /// style :: /// digits :: 0-sizeof(void*) /// /// ========================================================================== /// | S | Meaning | Example | /// -------------------------------------------------------------------------- /// | | | Input | Output | /// ========================================================================== /// | x- | Hex no prefix, lower | 0xDEADBEEF | deadbeef | /// | X- | Hex no prefix, upper | 0xDEADBEEF | DEADBEEF | /// | x+ / x | Hex + prefix, lower | 0xDEADBEEF | 0xdeadbeef | /// | X+ / X | Hex + prefix, upper | 0xDEADBEEF | 0xDEADBEEF | /// | (empty) | Same as X+ / X | | | /// ========================================================================== /// /// The default precision is the number of nibbles in a machine word, and in all /// cases indicates the minimum number of nibbles to print. template struct format_provider< T, typename std::enable_if::value>::type> : public detail::HelperFunctions { private: public: static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { HexPrintStyle HS = HexPrintStyle::PrefixUpper; consumeHexStyle(Style, HS); size_t Digits = consumeNumHexDigits(Style, HS, sizeof(void *) * 2); write_hex(Stream, reinterpret_cast(V), HS, Digits); } }; /// Implementation of format_provider for c-style strings and string /// objects such as std::string and llvm::StringRef. /// /// The options string of a string type has the grammar: /// /// string_options :: [length] /// /// where `length` is an optional integer specifying the maximum number of /// characters in the string to print. If `length` is omitted, the string is /// printed up to the null terminator. template struct format_provider< T, typename std::enable_if::value>::type> { static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { size_t N = StringRef::npos; if (!Style.empty() && Style.getAsInteger(10, N)) { assert(false && "Style is not a valid integer"); } llvm::StringRef S = V; Stream << S.substr(0, N); } }; /// Implementation of format_provider for llvm::Twine. /// /// This follows the same rules as the string formatter. template <> struct format_provider { static void format(const Twine &V, llvm::raw_ostream &Stream, StringRef Style) { format_provider::format(V.str(), Stream, Style); } }; /// Implementation of format_provider for characters. /// /// The options string of a character type has the grammar: /// /// char_options :: (empty) | [integer_options] /// /// If `char_options` is empty, the character is displayed as an ASCII /// character. Otherwise, it is treated as an integer options string. /// template struct format_provider< T, typename std::enable_if::value>::type> { static void format(const char &V, llvm::raw_ostream &Stream, StringRef Style) { if (Style.empty()) Stream << V; else { int X = static_cast(V); format_provider::format(X, Stream, Style); } } }; /// Implementation of format_provider for type `bool` /// /// The options string of a boolean type has the grammar: /// /// bool_options :: "" | "Y" | "y" | "D" | "d" | "T" | "t" /// /// ================================== /// | C | Meaning | /// ================================== /// | Y | YES / NO | /// | y | yes / no | /// | D / d | Integer 0 or 1 | /// | T | TRUE / FALSE | /// | t | true / false | /// | (empty) | Equivalent to 't' | /// ================================== template <> struct format_provider { static void format(const bool &B, llvm::raw_ostream &Stream, StringRef Style) { Stream << StringSwitch(Style) .Case("Y", B ? "YES" : "NO") .Case("y", B ? "yes" : "no") .CaseLower("D", B ? "1" : "0") .Case("T", B ? "TRUE" : "FALSE") .Cases("t", "", B ? "true" : "false") .Default(B ? "1" : "0"); } }; /// Implementation of format_provider for floating point types. /// /// The options string of a floating point type has the format: /// /// float_options :: [style][precision] /// style :: /// precision :: 0-99 /// /// ===================================================== /// | style | Meaning | Example | /// ----------------------------------------------------- /// | | | Input | Output | /// ===================================================== /// | P / p | Percentage | 0.05 | 5.00% | /// | F / f | Fixed point | 1.0 | 1.00 | /// | E | Exponential with E | 100000 | 1.0E+05 | /// | e | Exponential with e | 100000 | 1.0e+05 | /// | (empty) | Same as F / f | | | /// ===================================================== /// /// The default precision is 6 for exponential (E / e) and 2 for everything /// else. template struct format_provider< T, typename std::enable_if::value>::type> : public detail::HelperFunctions { static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { FloatStyle S; if (Style.consume_front("P") || Style.consume_front("p")) S = FloatStyle::Percent; else if (Style.consume_front("F") || Style.consume_front("f")) S = FloatStyle::Fixed; else if (Style.consume_front("E")) S = FloatStyle::ExponentUpper; else if (Style.consume_front("e")) S = FloatStyle::Exponent; else S = FloatStyle::Fixed; Optional Precision = parseNumericPrecision(Style); if (!Precision.hasValue()) Precision = getDefaultPrecision(S); write_double(Stream, static_cast(V), S, Precision); } }; namespace detail { template using IterValue = typename std::iterator_traits::value_type; template struct range_item_has_provider : public std::integral_constant< bool, !uses_missing_provider>::value> {}; } /// Implementation of format_provider for ranges. /// /// This will print an arbitrary range as a delimited sequence of items. /// /// The options string of a range type has the grammar: /// /// range_style ::= [separator] [element_style] /// separator ::= "$" delimeted_expr /// element_style ::= "@" delimeted_expr /// delimeted_expr ::= "[" expr "]" | "(" expr ")" | "<" expr ">" /// expr ::= /// /// where the separator expression is the string to insert between consecutive /// items in the range and the argument expression is the Style specification to /// be used when formatting the underlying type. The default separator if /// unspecified is ' ' (space). The syntax of the argument expression follows /// whatever grammar is dictated by the format provider or format adapter used /// to format the value type. /// /// Note that attempting to format an `iterator_range` where no format /// provider can be found for T will result in a compile error. /// template class format_provider> { using value = typename std::iterator_traits::value_type; using reference = typename std::iterator_traits::reference; static StringRef consumeOneOption(StringRef &Style, char Indicator, StringRef Default) { if (Style.empty()) return Default; if (Style.front() != Indicator) return Default; Style = Style.drop_front(); if (Style.empty()) { assert(false && "Invalid range style"); return Default; } for (const char *D : {"[]", "<>", "()"}) { if (Style.front() != D[0]) continue; size_t End = Style.find_first_of(D[1]); if (End == StringRef::npos) { assert(false && "Missing range option end delimeter!"); return Default; } StringRef Result = Style.slice(1, End); Style = Style.drop_front(End + 1); return Result; } assert(false && "Invalid range style!"); return Default; } static std::pair parseOptions(StringRef Style) { StringRef Sep = consumeOneOption(Style, '$', ", "); StringRef Args = consumeOneOption(Style, '@', ""); assert(Style.empty() && "Unexpected text in range option string!"); return std::make_pair(Sep, Args); } public: static_assert(detail::range_item_has_provider::value, "Range value_type does not have a format provider!"); static void format(const llvm::iterator_range &V, llvm::raw_ostream &Stream, StringRef Style) { StringRef Sep; StringRef ArgStyle; std::tie(Sep, ArgStyle) = parseOptions(Style); auto Begin = V.begin(); auto End = V.end(); if (Begin != End) { auto Adapter = detail::build_format_adapter(std::forward(*Begin)); Adapter.format(Stream, ArgStyle); ++Begin; } while (Begin != End) { Stream << Sep; auto Adapter = detail::build_format_adapter(std::forward(*Begin)); Adapter.format(Stream, ArgStyle); ++Begin; } } }; } #endif