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 24 FmtContext &tblgen::FmtContext::addSubst(StringRef placeholder, Twine subst) { 25 customSubstMap[placeholder] = subst.str(); 26 return *this; 27 } 28 29 FmtContext &tblgen::FmtContext::withBuilder(Twine subst) { 30 builtinSubstMap[PHKind::Builder] = subst.str(); 31 return *this; 32 } 33 34 FmtContext &tblgen::FmtContext::withOp(Twine subst) { 35 builtinSubstMap[PHKind::Op] = subst.str(); 36 return *this; 37 } 38 39 FmtContext &tblgen::FmtContext::withSelf(Twine subst) { 40 builtinSubstMap[PHKind::Self] = subst.str(); 41 return *this; 42 } 43 44 Optional<StringRef> 45 tblgen::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 55 Optional<StringRef> 56 tblgen::FmtContext::getSubstFor(StringRef placeholder) const { 57 auto it = customSubstMap.find(placeholder); 58 if (it == customSubstMap.end()) 59 return {}; 60 return StringRef(it->second); 61 } 62 63 FmtContext::PHKind tblgen::FmtContext::getPlaceHolderKind(StringRef str) { 64 return llvm::StringSwitch<FmtContext::PHKind>(str) 65 .Case("_builder", FmtContext::PHKind::Builder) 66 .Case("_op", FmtContext::PHKind::Op) 67 .Case("_self", FmtContext::PHKind::Self) 68 .Case("", FmtContext::PHKind::None) 69 .Default(FmtContext::PHKind::Custom); 70 } 71 72 std::pair<FmtReplacement, StringRef> 73 tblgen::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 = fmt.find_if_not([](char c) { return std::isdigit(c); }, 1); 102 if (end != 1) { 103 // We have a positional placeholder. Parse the index. 104 size_t index = 0; 105 if (fmt.substr(1, end - 1).consumeInteger(0, index)) { 106 llvm_unreachable("invalid replacement sequence index"); 107 } 108 109 if (end == StringRef::npos) { 110 // All the remaining characters are part of the positional placeholder. 111 return {FmtReplacement{fmt, index}, StringRef()}; 112 } 113 return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)}; 114 } 115 116 end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1); 117 auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1)); 118 if (end == StringRef::npos) { 119 // All the remaining characters are part of the special placeholder. 120 return {FmtReplacement{fmt, placeholder}, StringRef()}; 121 } 122 return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)}; 123 } 124 125 std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) { 126 std::vector<FmtReplacement> replacements; 127 FmtReplacement repl; 128 while (!fmt.empty()) { 129 std::tie(repl, fmt) = splitFmtSegment(fmt); 130 if (repl.type != FmtReplacement::Type::Empty) 131 replacements.push_back(repl); 132 } 133 return replacements; 134 } 135 136 void FmtObjectBase::format(raw_ostream &s) const { 137 for (auto &repl : replacements) { 138 if (repl.type == FmtReplacement::Type::Empty) 139 continue; 140 141 if (repl.type == FmtReplacement::Type::Literal) { 142 s << repl.spec; 143 continue; 144 } 145 146 if (repl.type == FmtReplacement::Type::SpecialPH) { 147 if (repl.placeholder == FmtContext::PHKind::None) { 148 s << repl.spec; 149 } else if (!context) { 150 // We need the context to replace special placeholders. 151 s << repl.spec << kMarkerForNoSubst; 152 } else { 153 Optional<StringRef> subst; 154 if (repl.placeholder == FmtContext::PHKind::Custom) { 155 // Skip the leading '$' sign for the custom placeholder 156 subst = context->getSubstFor(repl.spec.substr(1)); 157 } else { 158 subst = context->getSubstFor(repl.placeholder); 159 } 160 if (subst) 161 s << *subst; 162 else 163 s << repl.spec << kMarkerForNoSubst; 164 } 165 continue; 166 } 167 168 assert(repl.type == FmtReplacement::Type::PositionalPH); 169 170 if (repl.index >= adapters.size()) { 171 s << repl.spec << kMarkerForNoSubst; 172 continue; 173 } 174 adapters[repl.index]->format(s, /*Options=*/""); 175 } 176 } 177