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
20using namespace mlir;
21using namespace mlir::tblgen;
22
23// Marker to indicate an error happened when replacing a placeholder.
24const char *const kMarkerForNoSubst = "<no-subst-found>";
25
26FmtContext::FmtContext(ArrayRef<std::pair<StringRef, StringRef>> subs) {
27 for (auto &sub : subs)
28 addSubst(placeholder: sub.first, subst: sub.second);
29}
30
31FmtContext &FmtContext::addSubst(StringRef placeholder, const Twine &subst) {
32 customSubstMap[placeholder] = subst.str();
33 return *this;
34}
35
36FmtContext &FmtContext::withBuilder(Twine subst) {
37 builtinSubstMap[PHKind::Builder] = subst.str();
38 return *this;
39}
40
41FmtContext &FmtContext::withSelf(Twine subst) {
42 builtinSubstMap[PHKind::Self] = subst.str();
43 return *this;
44}
45
46std::optional<StringRef>
47FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {
48 if (placeholder == FmtContext::PHKind::None ||
49 placeholder == FmtContext::PHKind::Custom)
50 return {};
51 auto it = builtinSubstMap.find(Val: placeholder);
52 if (it == builtinSubstMap.end())
53 return {};
54 return StringRef(it->second);
55}
56
57std::optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
58 auto it = customSubstMap.find(Key: placeholder);
59 if (it == customSubstMap.end())
60 return {};
61 return StringRef(it->second);
62}
63
64FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {
65 return StringSwitch<FmtContext::PHKind>(str)
66 .Case(S: "_builder", Value: FmtContext::PHKind::Builder)
67 .Case(S: "_self", Value: FmtContext::PHKind::Self)
68 .Case(S: "", Value: FmtContext::PHKind::None)
69 .Default(Value: FmtContext::PHKind::Custom);
70}
71
72std::pair<FmtReplacement, StringRef>
73FmtObjectBase::splitFmtSegment(StringRef fmt) {
74 size_t begin = fmt.find_first_of(C: '$');
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(Start: 0, N: begin)}, fmt.substr(Start: 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(Start: 0, N: 1)}, fmt.substr(Start: 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(F: [](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(Start: 1, N: end - 1).consumeInteger(Radix: 0, Result&: 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(Start: end, N: 3) == "...") {
112 // Currently only ranges without upper bound are supported.
113 return {
114 FmtReplacement{fmt.substr(Start: 0, N: end + 3), index, FmtReplacement::kUnset},
115 fmt.substr(Start: 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(Start: 0, N: end), index}, fmt.substr(Start: end)};
123 }
124
125 end = fmt.find_if_not(F: [](char c) { return std::isalnum(c) || c == '_'; }, From: 1);
126 auto placeholder = FmtContext::getPlaceHolderKind(str: fmt.substr(Start: 1, N: 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(Start: 0, N: end), placeholder}, fmt.substr(Start: end)};
132}
133
134std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
135 std::vector<FmtReplacement> replacements;
136 FmtReplacement repl;
137 while (!fmt.empty()) {
138 std::tie(args&: repl, args&: fmt) = splitFmtSegment(fmt);
139 if (repl.type != FmtReplacement::Type::Empty)
140 replacements.push_back(x: repl);
141 }
142 return replacements;
143}
144
145void 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
155 if (repl.type == FmtReplacement::Type::SpecialPH) {
156 if (repl.placeholder == FmtContext::PHKind::None) {
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;
163 if (repl.placeholder == FmtContext::PHKind::Custom) {
164 // Skip the leading '$' sign for the custom placeholder
165 subst = context->getSubstFor(placeholder: repl.spec.substr(Start: 1));
166 } else {
167 subst = context->getSubstFor(placeholder: repl.placeholder);
168 }
169 if (subst)
170 s << *subst;
171 else
172 s << repl.spec << kMarkerForNoSubst;
173 }
174 continue;
175 }
176
177 if (repl.type == FmtReplacement::Type::PositionalRangePH) {
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(N: repl.index);
184 if (repl.end != FmtReplacement::kUnset)
185 range = range.drop_back(N: adapters.size() - repl.end);
186 llvm::interleaveComma(c: range, os&: s,
187 each_fn: [&](auto &x) { x->format(s, /*Options=*/""); });
188 continue;
189 }
190
191 assert(repl.type == FmtReplacement::Type::PositionalPH);
192
193 if (repl.index >= adapters.size()) {
194 s << repl.spec << kMarkerForNoSubst;
195 continue;
196 }
197 adapters[repl.index]->format(S&: s, /*Options=*/"");
198 }
199}
200
201FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
202 ArrayRef<std::string> params)
203 : FmtObjectBase(fmt, ctx, params.size()) {
204 parameters.reserve(N: params.size());
205 for (std::string p : params)
206 parameters.push_back(
207 Elt: llvm::support::detail::build_format_adapter(Item: std::move(p)));
208
209 adapters.reserve(n: parameters.size());
210 for (auto &p : parameters)
211 adapters.push_back(x: &p);
212}
213
214FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)
215 : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
216 adapters.reserve(n: parameters.size());
217 for (auto &p : parameters)
218 adapters.push_back(x: &p);
219}
220

source code of mlir/lib/TableGen/Format.cpp