1 //===- Format.cpp - Utilities for String Format ---------------------------===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 // 18 // This file defines utilities for formatting strings. They are specially 19 // tailored to the needs of TableGen'ing op definitions and rewrite rules, 20 // so they are not expected to be used as widely applicable utilities. 21 // 22 //===----------------------------------------------------------------------===// 23 24 #include "mlir/TableGen/Format.h" 25 26 using namespace mlir; 27 using namespace mlir::tblgen; 28 29 // Marker to indicate an error happened when replacing a placeholder. 30 const char *const kMarkerForNoSubst = "<no-subst-found>"; 31 32 FmtContext &tblgen::FmtContext::addSubst(StringRef placeholder, Twine subst) { 33 customSubstMap[placeholder] = subst.str(); 34 return *this; 35 } 36 37 FmtContext &tblgen::FmtContext::withBuilder(Twine subst) { 38 builtinSubstMap[PHKind::Builder] = subst.str(); 39 return *this; 40 } 41 42 FmtContext &tblgen::FmtContext::withOp(Twine subst) { 43 builtinSubstMap[PHKind::Op] = subst.str(); 44 return *this; 45 } 46 47 FmtContext &tblgen::FmtContext::withSelf(Twine subst) { 48 builtinSubstMap[PHKind::Self] = subst.str(); 49 return *this; 50 } 51 52 Optional<StringRef> 53 tblgen::FmtContext::getSubstFor(FmtContext::PHKind placeholder) const { 54 if (placeholder == FmtContext::PHKind::None || 55 placeholder == FmtContext::PHKind::Custom) 56 return {}; 57 auto it = builtinSubstMap.find(placeholder); 58 if (it == builtinSubstMap.end()) 59 return {}; 60 return StringRef(it->second); 61 } 62 63 Optional<StringRef> 64 tblgen::FmtContext::getSubstFor(StringRef placeholder) const { 65 auto it = customSubstMap.find(placeholder); 66 if (it == customSubstMap.end()) 67 return {}; 68 return StringRef(it->second); 69 } 70 71 FmtContext::PHKind tblgen::FmtContext::getPlaceHolderKind(StringRef str) { 72 return llvm::StringSwitch<FmtContext::PHKind>(str) 73 .Case("_builder", FmtContext::PHKind::Builder) 74 .Case("_op", FmtContext::PHKind::Op) 75 .Case("_self", FmtContext::PHKind::Self) 76 .Case("", FmtContext::PHKind::None) 77 .Default(FmtContext::PHKind::Custom); 78 } 79 80 std::pair<FmtReplacement, StringRef> 81 tblgen::FmtObjectBase::splitFmtSegment(StringRef fmt) { 82 size_t begin = fmt.find_first_of('$'); 83 if (begin == StringRef::npos) { 84 // No placeholders: the whole format string should be returned as a 85 // literal string. 86 return {FmtReplacement{fmt}, StringRef()}; 87 } 88 if (begin != 0) { 89 // The first placeholder is not at the beginning: we can split the format 90 // string into a literal string and the rest. 91 return {FmtReplacement{fmt.substr(0, begin)}, fmt.substr(begin)}; 92 } 93 94 // The first placeholder is at the beginning 95 96 if (fmt.size() == 1) { 97 // The whole format string just contains '$': treat as literal. 98 return {FmtReplacement{fmt}, StringRef()}; 99 } 100 101 // Allow escaping dollar with '$$' 102 if (fmt[1] == '$') { 103 return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)}; 104 } 105 106 // First try to see if it's a positional placeholder, and then handle special 107 // placeholders. 108 109 size_t end = fmt.find_if_not([](char c) { return std::isdigit(c); }, 1); 110 if (end != 1) { 111 // We have a positional placeholder. Parse the index. 112 size_t index = 0; 113 if (fmt.substr(1, end - 1).consumeInteger(0, index)) { 114 llvm_unreachable("invalid replacement sequence index"); 115 } 116 117 if (end == StringRef::npos) { 118 // All the remaining characters are part of the positional placeholder. 119 return {FmtReplacement{fmt, index}, StringRef()}; 120 } 121 return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)}; 122 } 123 124 end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1); 125 auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1)); 126 if (end == StringRef::npos) { 127 // All the remaining characters are part of the special placeholder. 128 return {FmtReplacement{fmt, placeholder}, StringRef()}; 129 } 130 return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)}; 131 } 132 133 std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) { 134 std::vector<FmtReplacement> replacements; 135 FmtReplacement repl; 136 while (!fmt.empty()) { 137 std::tie(repl, fmt) = splitFmtSegment(fmt); 138 if (repl.type != FmtReplacement::Type::Empty) 139 replacements.push_back(repl); 140 } 141 return replacements; 142 } 143 144 void FmtObjectBase::format(raw_ostream &s) const { 145 for (auto &repl : replacements) { 146 if (repl.type == FmtReplacement::Type::Empty) 147 continue; 148 149 if (repl.type == FmtReplacement::Type::Literal) { 150 s << repl.spec; 151 continue; 152 } 153 154 if (repl.type == FmtReplacement::Type::SpecialPH) { 155 if (repl.placeholder == FmtContext::PHKind::None) { 156 s << repl.spec; 157 } else if (!context) { 158 // We need the context to replace special placeholders. 159 s << repl.spec << kMarkerForNoSubst; 160 } else { 161 Optional<StringRef> subst; 162 if (repl.placeholder == FmtContext::PHKind::Custom) { 163 // Skip the leading '$' sign for the custom placeholder 164 subst = context->getSubstFor(repl.spec.substr(1)); 165 } else { 166 subst = context->getSubstFor(repl.placeholder); 167 } 168 if (subst) 169 s << *subst; 170 else 171 s << repl.spec << kMarkerForNoSubst; 172 } 173 continue; 174 } 175 176 assert(repl.type == FmtReplacement::Type::PositionalPH); 177 178 if (repl.index >= adapters.size()) { 179 s << repl.spec << kMarkerForNoSubst; 180 continue; 181 } 182 adapters[repl.index]->format(s, /*Options=*/""); 183 } 184 } 185