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 <cctype>
17
18 using namespace mlir;
19 using namespace mlir::tblgen;
20
21 // Marker to indicate an error happened when replacing a placeholder.
22 const char *const kMarkerForNoSubst = "<no-subst-found>";
23
addSubst(StringRef placeholder,Twine subst)24 FmtContext &FmtContext::addSubst(StringRef placeholder, Twine subst) {
25 customSubstMap[placeholder] = subst.str();
26 return *this;
27 }
28
withBuilder(Twine subst)29 FmtContext &FmtContext::withBuilder(Twine subst) {
30 builtinSubstMap[PHKind::Builder] = subst.str();
31 return *this;
32 }
33
withOp(Twine subst)34 FmtContext &FmtContext::withOp(Twine subst) {
35 builtinSubstMap[PHKind::Op] = subst.str();
36 return *this;
37 }
38
withSelf(Twine subst)39 FmtContext &FmtContext::withSelf(Twine subst) {
40 builtinSubstMap[PHKind::Self] = subst.str();
41 return *this;
42 }
43
44 Optional<StringRef>
getSubstFor(FmtContext::PHKind placeholder) const45 FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {
46 if (placeholder == FmtContext::PHKind::None ||
47 placeholder == FmtContext::PHKind::Custom)
48 return {};
49 auto it = builtinSubstMap.find(placeholder);
50 if (it == builtinSubstMap.end())
51 return {};
52 return StringRef(it->second);
53 }
54
getSubstFor(StringRef placeholder) const55 Optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
56 auto it = customSubstMap.find(placeholder);
57 if (it == customSubstMap.end())
58 return {};
59 return StringRef(it->second);
60 }
61
getPlaceHolderKind(StringRef str)62 FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {
63 return StringSwitch<FmtContext::PHKind>(str)
64 .Case("_builder", FmtContext::PHKind::Builder)
65 .Case("_op", FmtContext::PHKind::Op)
66 .Case("_self", FmtContext::PHKind::Self)
67 .Case("", FmtContext::PHKind::None)
68 .Default(FmtContext::PHKind::Custom);
69 }
70
71 std::pair<FmtReplacement, StringRef>
splitFmtSegment(StringRef fmt)72 FmtObjectBase::splitFmtSegment(StringRef fmt) {
73 size_t begin = fmt.find_first_of('$');
74 if (begin == StringRef::npos) {
75 // No placeholders: the whole format string should be returned as a
76 // literal string.
77 return {FmtReplacement{fmt}, StringRef()};
78 }
79 if (begin != 0) {
80 // The first placeholder is not at the beginning: we can split the format
81 // string into a literal string and the rest.
82 return {FmtReplacement{fmt.substr(0, begin)}, fmt.substr(begin)};
83 }
84
85 // The first placeholder is at the beginning
86
87 if (fmt.size() == 1) {
88 // The whole format string just contains '$': treat as literal.
89 return {FmtReplacement{fmt}, StringRef()};
90 }
91
92 // Allow escaping dollar with '$$'
93 if (fmt[1] == '$') {
94 return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)};
95 }
96
97 // First try to see if it's a positional placeholder, and then handle special
98 // placeholders.
99
100 size_t end = fmt.find_if_not([](char c) { return std::isdigit(c); }, 1);
101 if (end != 1) {
102 // We have a positional placeholder. Parse the index.
103 size_t index = 0;
104 if (fmt.substr(1, end - 1).consumeInteger(0, index)) {
105 llvm_unreachable("invalid replacement sequence index");
106 }
107
108 if (end == StringRef::npos) {
109 // All the remaining characters are part of the positional placeholder.
110 return {FmtReplacement{fmt, index}, StringRef()};
111 }
112 return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};
113 }
114
115 end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1);
116 auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1));
117 if (end == StringRef::npos) {
118 // All the remaining characters are part of the special placeholder.
119 return {FmtReplacement{fmt, placeholder}, StringRef()};
120 }
121 return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};
122 }
123
parseFormatString(StringRef fmt)124 std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
125 std::vector<FmtReplacement> replacements;
126 FmtReplacement repl;
127 while (!fmt.empty()) {
128 std::tie(repl, fmt) = splitFmtSegment(fmt);
129 if (repl.type != FmtReplacement::Type::Empty)
130 replacements.push_back(repl);
131 }
132 return replacements;
133 }
134
format(raw_ostream & s) const135 void FmtObjectBase::format(raw_ostream &s) const {
136 for (auto &repl : replacements) {
137 if (repl.type == FmtReplacement::Type::Empty)
138 continue;
139
140 if (repl.type == FmtReplacement::Type::Literal) {
141 s << repl.spec;
142 continue;
143 }
144
145 if (repl.type == FmtReplacement::Type::SpecialPH) {
146 if (repl.placeholder == FmtContext::PHKind::None) {
147 s << repl.spec;
148 } else if (!context) {
149 // We need the context to replace special placeholders.
150 s << repl.spec << kMarkerForNoSubst;
151 } else {
152 Optional<StringRef> subst;
153 if (repl.placeholder == FmtContext::PHKind::Custom) {
154 // Skip the leading '$' sign for the custom placeholder
155 subst = context->getSubstFor(repl.spec.substr(1));
156 } else {
157 subst = context->getSubstFor(repl.placeholder);
158 }
159 if (subst)
160 s << *subst;
161 else
162 s << repl.spec << kMarkerForNoSubst;
163 }
164 continue;
165 }
166
167 assert(repl.type == FmtReplacement::Type::PositionalPH);
168
169 if (repl.index >= adapters.size()) {
170 s << repl.spec << kMarkerForNoSubst;
171 continue;
172 }
173 adapters[repl.index]->format(s, /*Options=*/"");
174 }
175 }
176