148a6aa6cSLei Zhang //===- Format.cpp - Utilities for String Format ---------------------------===//
248a6aa6cSLei Zhang //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
648a6aa6cSLei Zhang //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
848a6aa6cSLei Zhang //
948a6aa6cSLei Zhang // This file defines utilities for formatting strings. They are specially
1048a6aa6cSLei Zhang // tailored to the needs of TableGen'ing op definitions and rewrite rules,
1148a6aa6cSLei Zhang // so they are not expected to be used as widely applicable utilities.
1248a6aa6cSLei Zhang //
1348a6aa6cSLei Zhang //===----------------------------------------------------------------------===//
1448a6aa6cSLei Zhang 
1548a6aa6cSLei Zhang #include "mlir/TableGen/Format.h"
16*75e164f6Sserge-sans-paille #include "llvm/ADT/StringSwitch.h"
17*75e164f6Sserge-sans-paille #include "llvm/ADT/Twine.h"
18be9b20ffSRiver Riddle #include <cctype>
1948a6aa6cSLei Zhang 
2048a6aa6cSLei Zhang using namespace mlir;
2148a6aa6cSLei Zhang using namespace mlir::tblgen;
2248a6aa6cSLei Zhang 
2348a6aa6cSLei Zhang // Marker to indicate an error happened when replacing a placeholder.
2448a6aa6cSLei Zhang const char *const kMarkerForNoSubst = "<no-subst-found>";
2548a6aa6cSLei Zhang 
FmtContext(ArrayRef<std::pair<StringRef,StringRef>> subs)26ca6bd9cdSMogball FmtContext::FmtContext(ArrayRef<std::pair<StringRef, StringRef>> subs) {
27ca6bd9cdSMogball   for (auto &sub : subs)
28ca6bd9cdSMogball     addSubst(sub.first, sub.second);
29ca6bd9cdSMogball }
30ca6bd9cdSMogball 
addSubst(StringRef placeholder,const Twine & subst)31ca6bd9cdSMogball FmtContext &FmtContext::addSubst(StringRef placeholder, const Twine &subst) {
3248a6aa6cSLei Zhang   customSubstMap[placeholder] = subst.str();
3348a6aa6cSLei Zhang   return *this;
3448a6aa6cSLei Zhang }
3548a6aa6cSLei Zhang 
withBuilder(Twine subst)3612d16de5SRahul Joshi FmtContext &FmtContext::withBuilder(Twine subst) {
3748a6aa6cSLei Zhang   builtinSubstMap[PHKind::Builder] = subst.str();
3848a6aa6cSLei Zhang   return *this;
3948a6aa6cSLei Zhang }
4048a6aa6cSLei Zhang 
withOp(Twine subst)4112d16de5SRahul Joshi FmtContext &FmtContext::withOp(Twine subst) {
4248a6aa6cSLei Zhang   builtinSubstMap[PHKind::Op] = subst.str();
4348a6aa6cSLei Zhang   return *this;
4448a6aa6cSLei Zhang }
4548a6aa6cSLei Zhang 
withSelf(Twine subst)4612d16de5SRahul Joshi FmtContext &FmtContext::withSelf(Twine subst) {
4748a6aa6cSLei Zhang   builtinSubstMap[PHKind::Self] = subst.str();
4848a6aa6cSLei Zhang   return *this;
4948a6aa6cSLei Zhang }
5048a6aa6cSLei Zhang 
5148a6aa6cSLei Zhang Optional<StringRef>
getSubstFor(FmtContext::PHKind placeholder) const5212d16de5SRahul Joshi FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {
5348a6aa6cSLei Zhang   if (placeholder == FmtContext::PHKind::None ||
5448a6aa6cSLei Zhang       placeholder == FmtContext::PHKind::Custom)
5548a6aa6cSLei Zhang     return {};
5648a6aa6cSLei Zhang   auto it = builtinSubstMap.find(placeholder);
5748a6aa6cSLei Zhang   if (it == builtinSubstMap.end())
5848a6aa6cSLei Zhang     return {};
5948a6aa6cSLei Zhang   return StringRef(it->second);
6048a6aa6cSLei Zhang }
6148a6aa6cSLei Zhang 
getSubstFor(StringRef placeholder) const6212d16de5SRahul Joshi Optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
6348a6aa6cSLei Zhang   auto it = customSubstMap.find(placeholder);
6448a6aa6cSLei Zhang   if (it == customSubstMap.end())
6548a6aa6cSLei Zhang     return {};
6648a6aa6cSLei Zhang   return StringRef(it->second);
6748a6aa6cSLei Zhang }
6848a6aa6cSLei Zhang 
getPlaceHolderKind(StringRef str)6912d16de5SRahul Joshi FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {
70cc83dc19SChristian Sigg   return StringSwitch<FmtContext::PHKind>(str)
7148a6aa6cSLei Zhang       .Case("_builder", FmtContext::PHKind::Builder)
7248a6aa6cSLei Zhang       .Case("_op", FmtContext::PHKind::Op)
7348a6aa6cSLei Zhang       .Case("_self", FmtContext::PHKind::Self)
7448a6aa6cSLei Zhang       .Case("", FmtContext::PHKind::None)
7548a6aa6cSLei Zhang       .Default(FmtContext::PHKind::Custom);
7648a6aa6cSLei Zhang }
7748a6aa6cSLei Zhang 
7848a6aa6cSLei Zhang std::pair<FmtReplacement, StringRef>
splitFmtSegment(StringRef fmt)7912d16de5SRahul Joshi FmtObjectBase::splitFmtSegment(StringRef fmt) {
8048a6aa6cSLei Zhang   size_t begin = fmt.find_first_of('$');
8148a6aa6cSLei Zhang   if (begin == StringRef::npos) {
8248a6aa6cSLei Zhang     // No placeholders: the whole format string should be returned as a
8348a6aa6cSLei Zhang     // literal string.
8448a6aa6cSLei Zhang     return {FmtReplacement{fmt}, StringRef()};
8548a6aa6cSLei Zhang   }
8648a6aa6cSLei Zhang   if (begin != 0) {
8748a6aa6cSLei Zhang     // The first placeholder is not at the beginning: we can split the format
8848a6aa6cSLei Zhang     // string into a literal string and the rest.
8948a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, begin)}, fmt.substr(begin)};
9048a6aa6cSLei Zhang   }
9148a6aa6cSLei Zhang 
9248a6aa6cSLei Zhang   // The first placeholder is at the beginning
9348a6aa6cSLei Zhang 
9448a6aa6cSLei Zhang   if (fmt.size() == 1) {
9548a6aa6cSLei Zhang     // The whole format string just contains '$': treat as literal.
9648a6aa6cSLei Zhang     return {FmtReplacement{fmt}, StringRef()};
9748a6aa6cSLei Zhang   }
9848a6aa6cSLei Zhang 
9948a6aa6cSLei Zhang   // Allow escaping dollar with '$$'
10048a6aa6cSLei Zhang   if (fmt[1] == '$') {
10148a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)};
10248a6aa6cSLei Zhang   }
10348a6aa6cSLei Zhang 
10448a6aa6cSLei Zhang   // First try to see if it's a positional placeholder, and then handle special
10548a6aa6cSLei Zhang   // placeholders.
10648a6aa6cSLei Zhang 
1075bc9cc13SJacques Pienaar   size_t end =
1085bc9cc13SJacques Pienaar       fmt.find_if_not([](char c) { return std::isdigit(c); }, /*From=*/1);
10948a6aa6cSLei Zhang   if (end != 1) {
11048a6aa6cSLei Zhang     // We have a positional placeholder. Parse the index.
11148a6aa6cSLei Zhang     size_t index = 0;
11248a6aa6cSLei Zhang     if (fmt.substr(1, end - 1).consumeInteger(0, index)) {
11348a6aa6cSLei Zhang       llvm_unreachable("invalid replacement sequence index");
11448a6aa6cSLei Zhang     }
11548a6aa6cSLei Zhang 
1165bc9cc13SJacques Pienaar     // Check if this is the part of a range specification.
1175bc9cc13SJacques Pienaar     if (fmt.substr(end, 3) == "...") {
1185bc9cc13SJacques Pienaar       // Currently only ranges without upper bound are supported.
1195bc9cc13SJacques Pienaar       return {
1205bc9cc13SJacques Pienaar           FmtReplacement{fmt.substr(0, end + 3), index, FmtReplacement::kUnset},
1215bc9cc13SJacques Pienaar           fmt.substr(end + 3)};
1225bc9cc13SJacques Pienaar     }
1235bc9cc13SJacques Pienaar 
12448a6aa6cSLei Zhang     if (end == StringRef::npos) {
12548a6aa6cSLei Zhang       // All the remaining characters are part of the positional placeholder.
12648a6aa6cSLei Zhang       return {FmtReplacement{fmt, index}, StringRef()};
12748a6aa6cSLei Zhang     }
12848a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};
12948a6aa6cSLei Zhang   }
13048a6aa6cSLei Zhang 
13148a6aa6cSLei Zhang   end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1);
13248a6aa6cSLei Zhang   auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1));
13348a6aa6cSLei Zhang   if (end == StringRef::npos) {
13448a6aa6cSLei Zhang     // All the remaining characters are part of the special placeholder.
13548a6aa6cSLei Zhang     return {FmtReplacement{fmt, placeholder}, StringRef()};
13648a6aa6cSLei Zhang   }
13748a6aa6cSLei Zhang   return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};
13848a6aa6cSLei Zhang }
13948a6aa6cSLei Zhang 
parseFormatString(StringRef fmt)14048a6aa6cSLei Zhang std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
14148a6aa6cSLei Zhang   std::vector<FmtReplacement> replacements;
14248a6aa6cSLei Zhang   FmtReplacement repl;
14348a6aa6cSLei Zhang   while (!fmt.empty()) {
14448a6aa6cSLei Zhang     std::tie(repl, fmt) = splitFmtSegment(fmt);
14548a6aa6cSLei Zhang     if (repl.type != FmtReplacement::Type::Empty)
14648a6aa6cSLei Zhang       replacements.push_back(repl);
14748a6aa6cSLei Zhang   }
14848a6aa6cSLei Zhang   return replacements;
14948a6aa6cSLei Zhang }
15048a6aa6cSLei Zhang 
format(raw_ostream & s) const15148a6aa6cSLei Zhang void FmtObjectBase::format(raw_ostream &s) const {
15248a6aa6cSLei Zhang   for (auto &repl : replacements) {
15348a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::Empty)
15448a6aa6cSLei Zhang       continue;
15548a6aa6cSLei Zhang 
15648a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::Literal) {
15748a6aa6cSLei Zhang       s << repl.spec;
15848a6aa6cSLei Zhang       continue;
15948a6aa6cSLei Zhang     }
16048a6aa6cSLei Zhang 
16148a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::SpecialPH) {
16248a6aa6cSLei Zhang       if (repl.placeholder == FmtContext::PHKind::None) {
16348a6aa6cSLei Zhang         s << repl.spec;
16448a6aa6cSLei Zhang       } else if (!context) {
16548a6aa6cSLei Zhang         // We need the context to replace special placeholders.
16648a6aa6cSLei Zhang         s << repl.spec << kMarkerForNoSubst;
16748a6aa6cSLei Zhang       } else {
16848a6aa6cSLei Zhang         Optional<StringRef> subst;
16948a6aa6cSLei Zhang         if (repl.placeholder == FmtContext::PHKind::Custom) {
17048a6aa6cSLei Zhang           // Skip the leading '$' sign for the custom placeholder
17148a6aa6cSLei Zhang           subst = context->getSubstFor(repl.spec.substr(1));
17248a6aa6cSLei Zhang         } else {
17348a6aa6cSLei Zhang           subst = context->getSubstFor(repl.placeholder);
17448a6aa6cSLei Zhang         }
17548a6aa6cSLei Zhang         if (subst)
17648a6aa6cSLei Zhang           s << *subst;
17748a6aa6cSLei Zhang         else
17848a6aa6cSLei Zhang           s << repl.spec << kMarkerForNoSubst;
17948a6aa6cSLei Zhang       }
18048a6aa6cSLei Zhang       continue;
18148a6aa6cSLei Zhang     }
18248a6aa6cSLei Zhang 
1835bc9cc13SJacques Pienaar     if (repl.type == FmtReplacement::Type::PositionalRangePH) {
1845bc9cc13SJacques Pienaar       if (repl.index >= adapters.size()) {
1855bc9cc13SJacques Pienaar         s << repl.spec << kMarkerForNoSubst;
1865bc9cc13SJacques Pienaar         continue;
1875bc9cc13SJacques Pienaar       }
1885bc9cc13SJacques Pienaar       auto range = llvm::makeArrayRef(adapters);
1895bc9cc13SJacques Pienaar       range = range.drop_front(repl.index);
1905bc9cc13SJacques Pienaar       if (repl.end != FmtReplacement::kUnset)
1915bc9cc13SJacques Pienaar         range = range.drop_back(adapters.size() - repl.end);
1925bc9cc13SJacques Pienaar       llvm::interleaveComma(range, s,
1935bc9cc13SJacques Pienaar                             [&](auto &x) { x->format(s, /*Options=*/""); });
1945bc9cc13SJacques Pienaar       continue;
1955bc9cc13SJacques Pienaar     }
1965bc9cc13SJacques Pienaar 
19748a6aa6cSLei Zhang     assert(repl.type == FmtReplacement::Type::PositionalPH);
19848a6aa6cSLei Zhang 
19948a6aa6cSLei Zhang     if (repl.index >= adapters.size()) {
20048a6aa6cSLei Zhang       s << repl.spec << kMarkerForNoSubst;
20148a6aa6cSLei Zhang       continue;
20248a6aa6cSLei Zhang     }
20348a6aa6cSLei Zhang     adapters[repl.index]->format(s, /*Options=*/"");
20448a6aa6cSLei Zhang   }
20548a6aa6cSLei Zhang }
20602834e1bSVladislav Vinogradov 
FmtStrVecObject(StringRef fmt,const FmtContext * ctx,ArrayRef<std::string> params)20702834e1bSVladislav Vinogradov FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
20802834e1bSVladislav Vinogradov                                  ArrayRef<std::string> params)
20902834e1bSVladislav Vinogradov     : FmtObjectBase(fmt, ctx, params.size()) {
21002834e1bSVladislav Vinogradov   parameters.reserve(params.size());
21102834e1bSVladislav Vinogradov   for (std::string p : params)
21202834e1bSVladislav Vinogradov     parameters.push_back(llvm::detail::build_format_adapter(std::move(p)));
21302834e1bSVladislav Vinogradov 
21402834e1bSVladislav Vinogradov   adapters.reserve(parameters.size());
21502834e1bSVladislav Vinogradov   for (auto &p : parameters)
21602834e1bSVladislav Vinogradov     adapters.push_back(&p);
21702834e1bSVladislav Vinogradov }
21802834e1bSVladislav Vinogradov 
FmtStrVecObject(FmtStrVecObject && that)21902834e1bSVladislav Vinogradov FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)
22002834e1bSVladislav Vinogradov     : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
22102834e1bSVladislav Vinogradov   adapters.reserve(parameters.size());
22202834e1bSVladislav Vinogradov   for (auto &p : parameters)
22302834e1bSVladislav Vinogradov     adapters.push_back(&p);
22402834e1bSVladislav Vinogradov }
225