MLIR  20.0.0git
Format.cpp
Go to the documentation of this file.
1 //===- Format.cpp - Utilities for String Format ---------------------------===//
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 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.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "mlir/TableGen/Format.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/ADT/Twine.h"
18 #include <cctype>
19 
20 using namespace mlir;
21 using namespace mlir::tblgen;
22 
23 // Marker to indicate an error happened when replacing a placeholder.
24 const char *const kMarkerForNoSubst = "<no-subst-found>";
25 
26 FmtContext::FmtContext(ArrayRef<std::pair<StringRef, StringRef>> subs) {
27  for (auto &sub : subs)
28  addSubst(sub.first, sub.second);
29 }
30 
31 FmtContext &FmtContext::addSubst(StringRef placeholder, const Twine &subst) {
32  customSubstMap[placeholder] = subst.str();
33  return *this;
34 }
35 
37  builtinSubstMap[PHKind::Builder] = subst.str();
38  return *this;
39 }
40 
42  builtinSubstMap[PHKind::Self] = subst.str();
43  return *this;
44 }
45 
46 std::optional<StringRef>
48  if (placeholder == FmtContext::PHKind::None ||
49  placeholder == FmtContext::PHKind::Custom)
50  return {};
51  auto it = builtinSubstMap.find(placeholder);
52  if (it == builtinSubstMap.end())
53  return {};
54  return StringRef(it->second);
55 }
56 
57 std::optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
58  auto it = customSubstMap.find(placeholder);
59  if (it == customSubstMap.end())
60  return {};
61  return StringRef(it->second);
62 }
63 
66  .Case("_builder", FmtContext::PHKind::Builder)
67  .Case("_self", FmtContext::PHKind::Self)
68  .Case("", FmtContext::PHKind::None)
70 }
71 
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
77  // literal string.
78  return {FmtReplacement{fmt}, StringRef()};
79  }
80  if (begin != 0) {
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)};
84  }
85 
86  // The first placeholder is at the beginning
87 
88  if (fmt.size() == 1) {
89  // The whole format string just contains '$': treat as literal.
90  return {FmtReplacement{fmt}, StringRef()};
91  }
92 
93  // Allow escaping dollar with '$$'
94  if (fmt[1] == '$') {
95  return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)};
96  }
97 
98  // First try to see if it's a positional placeholder, and then handle special
99  // placeholders.
100 
101  size_t end =
102  fmt.find_if_not([](char c) { return std::isdigit(c); }, /*From=*/1);
103  if (end != 1) {
104  // We have a positional placeholder. Parse the index.
105  size_t index = 0;
106  if (fmt.substr(1, end - 1).consumeInteger(0, index)) {
107  llvm_unreachable("invalid replacement sequence index");
108  }
109 
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.
113  return {
114  FmtReplacement{fmt.substr(0, end + 3), index, FmtReplacement::kUnset},
115  fmt.substr(end + 3)};
116  }
117 
118  if (end == StringRef::npos) {
119  // All the remaining characters are part of the positional placeholder.
120  return {FmtReplacement{fmt, index}, StringRef()};
121  }
122  return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};
123  }
124 
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()};
130  }
131  return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};
132 }
133 
134 std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
135  std::vector<FmtReplacement> replacements;
136  FmtReplacement repl;
137  while (!fmt.empty()) {
138  std::tie(repl, fmt) = splitFmtSegment(fmt);
139  if (repl.type != FmtReplacement::Type::Empty)
140  replacements.push_back(repl);
141  }
142  return replacements;
143 }
144 
145 void FmtObjectBase::format(raw_ostream &s) const {
146  for (auto &repl : replacements) {
147  if (repl.type == FmtReplacement::Type::Empty)
148  continue;
149 
150  if (repl.type == FmtReplacement::Type::Literal) {
151  s << repl.spec;
152  continue;
153  }
154 
157  s << repl.spec;
158  } else if (!context) {
159  // We need the context to replace special placeholders.
160  s << repl.spec << kMarkerForNoSubst;
161  } else {
162  std::optional<StringRef> subst;
164  // Skip the leading '$' sign for the custom placeholder
165  subst = context->getSubstFor(repl.spec.substr(1));
166  } else {
167  subst = context->getSubstFor(repl.placeholder);
168  }
169  if (subst)
170  s << *subst;
171  else
172  s << repl.spec << kMarkerForNoSubst;
173  }
174  continue;
175  }
176 
178  if (repl.index >= adapters.size()) {
179  s << repl.spec << kMarkerForNoSubst;
180  continue;
181  }
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=*/""); });
188  continue;
189  }
190 
192 
193  if (repl.index >= adapters.size()) {
194  s << repl.spec << kMarkerForNoSubst;
195  continue;
196  }
197  adapters[repl.index]->format(s, /*Options=*/"");
198  }
199 }
200 
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)));
208 
209  adapters.reserve(parameters.size());
210  for (auto &p : parameters)
211  adapters.push_back(&p);
212 }
213 
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);
219 }
const char *const kMarkerForNoSubst
Definition: Format.cpp:24
Format context containing substitutions for special placeholders.
Definition: Format.h:40
FmtContext & withBuilder(Twine subst)
Definition: Format.cpp:36
static PHKind getPlaceHolderKind(StringRef str)
Definition: Format.cpp:64
std::optional< StringRef > getSubstFor(PHKind placeholder) const
Definition: Format.cpp:47
FmtContext & withSelf(Twine subst)
Definition: Format.cpp:41
FmtContext & addSubst(StringRef placeholder, const Twine &subst)
Definition: Format.cpp:31
std::vector< llvm::support::detail::format_adapter * > adapters
Definition: Format.h:144
void format(llvm::raw_ostream &s) const
Definition: Format.cpp:145
const FmtContext * context
Definition: Format.h:143
std::vector< FmtReplacement > replacements
Definition: Format.h:145
FmtStrVecObject(StringRef fmt, const FmtContext *ctx, ArrayRef< std::string > params)
Definition: Format.cpp:201
Include the generated interface declarations.
Struct representing a replacement segment for the formatted string.
Definition: Format.h:93
static constexpr size_t kUnset
Definition: Format.h:118
FmtContext::PHKind placeholder
Definition: Format.h:116