1 //===- Format.h - Utilities for String Format -------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file declares utilities for formatting strings. They are specially
10 // tailored to the needs of TableGen'ing op definitions and rewrite rules,
11 // so they are not expected to be used as widely applicable utilities.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #ifndef MLIR_TABLEGEN_FORMAT_H_
16 #define MLIR_TABLEGEN_FORMAT_H_
17
18 #include "mlir/Support/LLVM.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/StringMap.h"
21 #include "llvm/Support/FormatVariadic.h"
22
23 namespace mlir {
24 namespace tblgen {
25
26 /// Format context containing substitutions for special placeholders.
27 ///
28 /// This context divides special placeholders into two categories: builtin ones
29 /// and custom ones.
30 ///
31 /// Builtin placeholders are baked into `FmtContext` and each one of them has a
32 /// dedicated setter. They can be used in all dialects. Their names follow the
33 /// convention of `$_<name>`. The rationale of the leading underscore is to
34 /// avoid confusion and name collision: op arguments/attributes/results are
35 /// named as $<name>, and we can potentially support referencing those entities
36 /// directly in the format template in the future.
37 //
38 /// Custom ones are registered by dialect-specific TableGen backends and use the
39 /// same unified setter.
40 class FmtContext {
41 public:
42 // Placeholder kinds
43 enum class PHKind : char {
44 None,
45 Custom, // For custom placeholders
46 Builder, // For the $_builder placeholder
47 Op, // For the $_op placeholder
48 Self, // For the $_self placeholder
49 };
50
51 FmtContext() = default;
52
53 // Setter for custom placeholders
54 FmtContext &addSubst(StringRef placeholder, Twine subst);
55
56 // Setters for builtin placeholders
57 FmtContext &withBuilder(Twine subst);
58 FmtContext &withOp(Twine subst);
59 FmtContext &withSelf(Twine subst);
60
61 Optional<StringRef> getSubstFor(PHKind placeholder) const;
62 Optional<StringRef> getSubstFor(StringRef placeholder) const;
63
64 static PHKind getPlaceHolderKind(StringRef str);
65
66 private:
67 struct PHKindInfo : DenseMapInfo<PHKind> {
68 using CharInfo = DenseMapInfo<char>;
69
getEmptyKeyPHKindInfo70 static inline PHKind getEmptyKey() {
71 return static_cast<PHKind>(CharInfo::getEmptyKey());
72 }
getTombstoneKeyPHKindInfo73 static inline PHKind getTombstoneKey() {
74 return static_cast<PHKind>(CharInfo::getTombstoneKey());
75 }
getHashValuePHKindInfo76 static unsigned getHashValue(const PHKind &val) {
77 return CharInfo::getHashValue(static_cast<char>(val));
78 }
79
isEqualPHKindInfo80 static bool isEqual(const PHKind &lhs, const PHKind &rhs) {
81 return lhs == rhs;
82 }
83 };
84
85 llvm::SmallDenseMap<PHKind, std::string, 4, PHKindInfo> builtinSubstMap;
86 llvm::StringMap<std::string> customSubstMap;
87 };
88
89 /// Struct representing a replacement segment for the formatted string. It can
90 /// be a segment of the formatting template (for `Literal`) or a replacement
91 /// parameter (for `PositionalPH`, `PositionalRangePH` and `SpecialPH`).
92 struct FmtReplacement {
93 enum class Type {
94 Empty,
95 Literal,
96 PositionalPH,
97 PositionalRangePH,
98 SpecialPH
99 };
100
101 FmtReplacement() = default;
FmtReplacementFmtReplacement102 explicit FmtReplacement(StringRef literal)
103 : type(Type::Literal), spec(literal) {}
FmtReplacementFmtReplacement104 FmtReplacement(StringRef spec, size_t index)
105 : type(Type::PositionalPH), spec(spec), index(index) {}
FmtReplacementFmtReplacement106 FmtReplacement(StringRef spec, size_t index, size_t end)
107 : type(Type::PositionalRangePH), spec(spec), index(index), end(end) {}
FmtReplacementFmtReplacement108 FmtReplacement(StringRef spec, FmtContext::PHKind placeholder)
109 : type(Type::SpecialPH), spec(spec), placeholder(placeholder) {}
110
111 Type type = Type::Empty;
112 StringRef spec;
113 size_t index = 0;
114 size_t end = kUnset;
115 FmtContext::PHKind placeholder = FmtContext::PHKind::None;
116
117 static constexpr size_t kUnset = -1;
118 };
119
120 class FmtObjectBase {
121 private:
122 static std::pair<FmtReplacement, StringRef> splitFmtSegment(StringRef fmt);
123 static std::vector<FmtReplacement> parseFormatString(StringRef fmt);
124
125 protected:
126 // The parameters are stored in a std::tuple, which does not provide runtime
127 // indexing capabilities. In order to enable runtime indexing, we use this
128 // structure to put the parameters into a std::vector. Since the parameters
129 // are not all the same type, we use some type-erasure by wrapping the
130 // parameters in a template class that derives from a non-template superclass.
131 // Essentially, we are converting a std::tuple<Derived<Ts...>> to a
132 // std::vector<Base*>.
133 struct CreateAdapters {
134 template <typename... Ts>
operatorCreateAdapters135 std::vector<llvm::detail::format_adapter *> operator()(Ts &...items) {
136 return std::vector<llvm::detail::format_adapter *>{&items...};
137 }
138 };
139
140 StringRef fmt;
141 const FmtContext *context;
142 std::vector<llvm::detail::format_adapter *> adapters;
143 std::vector<FmtReplacement> replacements;
144
145 public:
FmtObjectBase(StringRef fmt,const FmtContext * ctx,size_t numParams)146 FmtObjectBase(StringRef fmt, const FmtContext *ctx, size_t numParams)
147 : fmt(fmt), context(ctx), replacements(parseFormatString(fmt)) {}
148
149 FmtObjectBase(const FmtObjectBase &that) = delete;
150
FmtObjectBase(FmtObjectBase && that)151 FmtObjectBase(FmtObjectBase &&that)
152 : fmt(std::move(that.fmt)), context(that.context),
153 adapters(), // adapters are initialized by FmtObject
154 replacements(std::move(that.replacements)) {}
155
156 void format(llvm::raw_ostream &s) const;
157
str()158 std::string str() const {
159 std::string result;
160 llvm::raw_string_ostream s(result);
161 format(s);
162 return s.str();
163 }
164
sstr()165 template <unsigned N> SmallString<N> sstr() const {
166 SmallString<N> result;
167 llvm::raw_svector_ostream s(result);
168 format(s);
169 return result;
170 }
171
172 template <unsigned N> operator SmallString<N>() const { return sstr<N>(); }
173
string()174 operator std::string() const { return str(); }
175 };
176
177 template <typename Tuple> class FmtObject : public FmtObjectBase {
178 // Storage for the parameter adapters. Since the base class erases the type
179 // of the parameters, we have to own the storage for the parameters here, and
180 // have the base class store type-erased pointers into this tuple.
181 Tuple parameters;
182
183 public:
FmtObject(StringRef fmt,const FmtContext * ctx,Tuple && params)184 FmtObject(StringRef fmt, const FmtContext *ctx, Tuple &¶ms)
185 : FmtObjectBase(fmt, ctx, std::tuple_size<Tuple>::value),
186 parameters(std::move(params)) {
187 adapters.reserve(std::tuple_size<Tuple>::value);
188 adapters = llvm::apply_tuple(CreateAdapters(), parameters);
189 }
190
191 FmtObject(FmtObject const &that) = delete;
192
FmtObject(FmtObject && that)193 FmtObject(FmtObject &&that)
194 : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
195 adapters.reserve(that.adapters.size());
196 adapters = llvm::apply_tuple(CreateAdapters(), parameters);
197 }
198 };
199
200 class FmtStrVecObject : public FmtObjectBase {
201 public:
202 using StrFormatAdapter =
203 decltype(llvm::detail::build_format_adapter(std::declval<std::string>()));
204
205 FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
206 ArrayRef<std::string> params);
207 FmtStrVecObject(FmtStrVecObject const &that) = delete;
208 FmtStrVecObject(FmtStrVecObject &&that);
209
210 private:
211 SmallVector<StrFormatAdapter, 16> parameters;
212 };
213
214 /// Formats text by substituting placeholders in format string with replacement
215 /// parameters.
216 ///
217 /// There are two categories of placeholders accepted, both led by a '$' sign:
218 ///
219 /// 1.a Positional placeholder: $[0-9]+
220 /// 1.b Positional range placeholder: $[0-9]+...
221 /// 2. Special placeholder: $[a-zA-Z_][a-zA-Z0-9_]*
222 ///
223 /// Replacement parameters for positional placeholders are supplied as the
224 /// `vals` parameter pack with 1:1 mapping. That is, $0 will be replaced by the
225 /// first parameter in `vals`, $1 by the second one, and so on. Note that you
226 /// can use the positional placeholders in any order and repeat any times, for
227 /// example, "$2 $1 $1 $0" is accepted.
228 ///
229 /// Replace parameters for positional range placeholders are supplied as if
230 /// positional placeholders were specified with commas separating them.
231 ///
232 /// Replacement parameters for special placeholders are supplied using the `ctx`
233 /// format context.
234 ///
235 /// The `fmt` is recorded as a `StringRef` inside the returned `FmtObject`.
236 /// The caller needs to make sure the underlying data is available when the
237 /// `FmtObject` is used.
238 ///
239 /// `ctx` accepts a nullptr if there is no special placeholder is used.
240 ///
241 /// If no substitution is provided for a placeholder or any error happens during
242 /// format string parsing or replacement, the placeholder will be outputted
243 /// as-is with an additional marker '<no-subst-found>', to aid debugging.
244 ///
245 /// To print a '$' literally, escape it with '$$'.
246 ///
247 /// This utility function is inspired by LLVM formatv(), with modifications
248 /// specially tailored for TableGen C++ generation usage:
249 ///
250 /// 1. This utility use '$' instead of '{' and '}' for denoting the placeholder
251 /// because '{' and '}' are frequently used in C++ code.
252 /// 2. This utility does not support format layout because it is rarely needed
253 /// in C++ code generation.
254 template <typename... Ts>
255 inline auto tgfmt(StringRef fmt, const FmtContext *ctx, Ts &&...vals)
256 -> FmtObject<decltype(std::make_tuple(
257 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...))> {
258 using ParamTuple = decltype(std::make_tuple(
259 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...));
260 return FmtObject<ParamTuple>(
261 fmt, ctx,
262 std::make_tuple(
263 llvm::detail::build_format_adapter(std::forward<Ts>(vals))...));
264 }
265
tgfmt(StringRef fmt,const FmtContext * ctx,ArrayRef<std::string> params)266 inline FmtStrVecObject tgfmt(StringRef fmt, const FmtContext *ctx,
267 ArrayRef<std::string> params) {
268 return FmtStrVecObject(fmt, ctx, params);
269 }
270
271 } // end namespace tblgen
272 } // end namespace mlir
273
274 #endif // MLIR_TABLEGEN_FORMAT_H_
275