1 //===- Format.cpp - Utilities for String Format ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file defines 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.
13 //===----------------------------------------------------------------------===//
15 #include "mlir/TableGen/Format.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/ADT/Twine.h"
21 using namespace mlir::tblgen
;
23 // Marker to indicate an error happened when replacing a placeholder.
24 const char *const kMarkerForNoSubst
= "<no-subst-found>";
26 FmtContext::FmtContext(ArrayRef
<std::pair
<StringRef
, StringRef
>> subs
) {
27 for (auto &sub
: subs
)
28 addSubst(sub
.first
, sub
.second
);
31 FmtContext
&FmtContext::addSubst(StringRef placeholder
, const Twine
&subst
) {
32 customSubstMap
[placeholder
] = subst
.str();
36 FmtContext
&FmtContext::withBuilder(Twine subst
) {
37 builtinSubstMap
[PHKind::Builder
] = subst
.str();
41 FmtContext
&FmtContext::withSelf(Twine subst
) {
42 builtinSubstMap
[PHKind::Self
] = subst
.str();
46 std::optional
<StringRef
>
47 FmtContext::getSubstFor(FmtContext::PHKind placeholder
) const {
48 if (placeholder
== FmtContext::PHKind::None
||
49 placeholder
== FmtContext::PHKind::Custom
)
51 auto it
= builtinSubstMap
.find(placeholder
);
52 if (it
== builtinSubstMap
.end())
54 return StringRef(it
->second
);
57 std::optional
<StringRef
> FmtContext::getSubstFor(StringRef placeholder
) const {
58 auto it
= customSubstMap
.find(placeholder
);
59 if (it
== customSubstMap
.end())
61 return StringRef(it
->second
);
64 FmtContext::PHKind
FmtContext::getPlaceHolderKind(StringRef str
) {
65 return StringSwitch
<FmtContext::PHKind
>(str
)
66 .Case("_builder", FmtContext::PHKind::Builder
)
67 .Case("_self", FmtContext::PHKind::Self
)
68 .Case("", FmtContext::PHKind::None
)
69 .Default(FmtContext::PHKind::Custom
);
72 std::pair
<FmtReplacement
, StringRef
>
73 FmtObjectBase::splitFmtSegment(StringRef fmt
) {
74 size_t begin
= fmt
.find_first_of('$');
75 if (begin
== StringRef::npos
) {
76 // No placeholders: the whole format string should be returned as a
78 return {FmtReplacement
{fmt
}, StringRef()};
81 // The first placeholder is not at the beginning: we can split the format
82 // string into a literal string and the rest.
83 return {FmtReplacement
{fmt
.substr(0, begin
)}, fmt
.substr(begin
)};
86 // The first placeholder is at the beginning
88 if (fmt
.size() == 1) {
89 // The whole format string just contains '$': treat as literal.
90 return {FmtReplacement
{fmt
}, StringRef()};
93 // Allow escaping dollar with '$$'
95 return {FmtReplacement
{fmt
.substr(0, 1)}, fmt
.substr(2)};
98 // First try to see if it's a positional placeholder, and then handle special
102 fmt
.find_if_not([](char c
) { return std::isdigit(c
); }, /*From=*/1);
104 // We have a positional placeholder. Parse the index.
106 if (fmt
.substr(1, end
- 1).consumeInteger(0, index
)) {
107 llvm_unreachable("invalid replacement sequence index");
110 // Check if this is the part of a range specification.
111 if (fmt
.substr(end
, 3) == "...") {
112 // Currently only ranges without upper bound are supported.
114 FmtReplacement
{fmt
.substr(0, end
+ 3), index
, FmtReplacement::kUnset
},
115 fmt
.substr(end
+ 3)};
118 if (end
== StringRef::npos
) {
119 // All the remaining characters are part of the positional placeholder.
120 return {FmtReplacement
{fmt
, index
}, StringRef()};
122 return {FmtReplacement
{fmt
.substr(0, end
), index
}, fmt
.substr(end
)};
125 end
= fmt
.find_if_not([](char c
) { return std::isalnum(c
) || c
== '_'; }, 1);
126 auto placeholder
= FmtContext::getPlaceHolderKind(fmt
.substr(1, end
- 1));
127 if (end
== StringRef::npos
) {
128 // All the remaining characters are part of the special placeholder.
129 return {FmtReplacement
{fmt
, placeholder
}, StringRef()};
131 return {FmtReplacement
{fmt
.substr(0, end
), placeholder
}, fmt
.substr(end
)};
134 std::vector
<FmtReplacement
> FmtObjectBase::parseFormatString(StringRef fmt
) {
135 std::vector
<FmtReplacement
> replacements
;
137 while (!fmt
.empty()) {
138 std::tie(repl
, fmt
) = splitFmtSegment(fmt
);
139 if (repl
.type
!= FmtReplacement::Type::Empty
)
140 replacements
.push_back(repl
);
145 void FmtObjectBase::format(raw_ostream
&s
) const {
146 for (auto &repl
: replacements
) {
147 if (repl
.type
== FmtReplacement::Type::Empty
)
150 if (repl
.type
== FmtReplacement::Type::Literal
) {
155 if (repl
.type
== FmtReplacement::Type::SpecialPH
) {
156 if (repl
.placeholder
== FmtContext::PHKind::None
) {
158 } else if (!context
) {
159 // We need the context to replace special placeholders.
160 s
<< repl
.spec
<< kMarkerForNoSubst
;
162 std::optional
<StringRef
> subst
;
163 if (repl
.placeholder
== FmtContext::PHKind::Custom
) {
164 // Skip the leading '$' sign for the custom placeholder
165 subst
= context
->getSubstFor(repl
.spec
.substr(1));
167 subst
= context
->getSubstFor(repl
.placeholder
);
172 s
<< repl
.spec
<< kMarkerForNoSubst
;
177 if (repl
.type
== FmtReplacement::Type::PositionalRangePH
) {
178 if (repl
.index
>= adapters
.size()) {
179 s
<< repl
.spec
<< kMarkerForNoSubst
;
182 auto range
= llvm::ArrayRef(adapters
);
183 range
= range
.drop_front(repl
.index
);
184 if (repl
.end
!= FmtReplacement::kUnset
)
185 range
= range
.drop_back(adapters
.size() - repl
.end
);
186 llvm::interleaveComma(range
, s
,
187 [&](auto &x
) { x
->format(s
, /*Options=*/""); });
191 assert(repl
.type
== FmtReplacement::Type::PositionalPH
);
193 if (repl
.index
>= adapters
.size()) {
194 s
<< repl
.spec
<< kMarkerForNoSubst
;
197 adapters
[repl
.index
]->format(s
, /*Options=*/"");
201 FmtStrVecObject::FmtStrVecObject(StringRef fmt
, const FmtContext
*ctx
,
202 ArrayRef
<std::string
> params
)
203 : FmtObjectBase(fmt
, ctx
, params
.size()) {
204 parameters
.reserve(params
.size());
205 for (std::string p
: params
)
206 parameters
.push_back(
207 llvm::support::detail::build_format_adapter(std::move(p
)));
209 adapters
.reserve(parameters
.size());
210 for (auto &p
: parameters
)
211 adapters
.push_back(&p
);
214 FmtStrVecObject::FmtStrVecObject(FmtStrVecObject
&&that
)
215 : FmtObjectBase(std::move(that
)), parameters(std::move(that
.parameters
)) {
216 adapters
.reserve(parameters
.size());
217 for (auto &p
: parameters
)
218 adapters
.push_back(&p
);