xref: /llvm-project/mlir/lib/TableGen/Format.cpp (revision b714fc7f869c9627bab38b3a6cac5d5d7720f204)
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"
1675e164f6Sserge-sans-paille #include "llvm/ADT/StringSwitch.h"
1775e164f6Sserge-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 
withSelf(Twine subst)4112d16de5SRahul Joshi FmtContext &FmtContext::withSelf(Twine subst) {
4248a6aa6cSLei Zhang   builtinSubstMap[PHKind::Self] = subst.str();
4348a6aa6cSLei Zhang   return *this;
4448a6aa6cSLei Zhang }
4548a6aa6cSLei Zhang 
463cfe412eSFangrui Song std::optional<StringRef>
getSubstFor(FmtContext::PHKind placeholder) const4712d16de5SRahul Joshi FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {
4848a6aa6cSLei Zhang   if (placeholder == FmtContext::PHKind::None ||
4948a6aa6cSLei Zhang       placeholder == FmtContext::PHKind::Custom)
5048a6aa6cSLei Zhang     return {};
5148a6aa6cSLei Zhang   auto it = builtinSubstMap.find(placeholder);
5248a6aa6cSLei Zhang   if (it == builtinSubstMap.end())
5348a6aa6cSLei Zhang     return {};
5448a6aa6cSLei Zhang   return StringRef(it->second);
5548a6aa6cSLei Zhang }
5648a6aa6cSLei Zhang 
getSubstFor(StringRef placeholder) const573cfe412eSFangrui Song std::optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
5848a6aa6cSLei Zhang   auto it = customSubstMap.find(placeholder);
5948a6aa6cSLei Zhang   if (it == customSubstMap.end())
6048a6aa6cSLei Zhang     return {};
6148a6aa6cSLei Zhang   return StringRef(it->second);
6248a6aa6cSLei Zhang }
6348a6aa6cSLei Zhang 
getPlaceHolderKind(StringRef str)6412d16de5SRahul Joshi FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {
65cc83dc19SChristian Sigg   return StringSwitch<FmtContext::PHKind>(str)
6648a6aa6cSLei Zhang       .Case("_builder", FmtContext::PHKind::Builder)
6748a6aa6cSLei Zhang       .Case("_self", FmtContext::PHKind::Self)
6848a6aa6cSLei Zhang       .Case("", FmtContext::PHKind::None)
6948a6aa6cSLei Zhang       .Default(FmtContext::PHKind::Custom);
7048a6aa6cSLei Zhang }
7148a6aa6cSLei Zhang 
7248a6aa6cSLei Zhang std::pair<FmtReplacement, StringRef>
splitFmtSegment(StringRef fmt)7312d16de5SRahul Joshi FmtObjectBase::splitFmtSegment(StringRef fmt) {
7448a6aa6cSLei Zhang   size_t begin = fmt.find_first_of('$');
7548a6aa6cSLei Zhang   if (begin == StringRef::npos) {
7648a6aa6cSLei Zhang     // No placeholders: the whole format string should be returned as a
7748a6aa6cSLei Zhang     // literal string.
7848a6aa6cSLei Zhang     return {FmtReplacement{fmt}, StringRef()};
7948a6aa6cSLei Zhang   }
8048a6aa6cSLei Zhang   if (begin != 0) {
8148a6aa6cSLei Zhang     // The first placeholder is not at the beginning: we can split the format
8248a6aa6cSLei Zhang     // string into a literal string and the rest.
8348a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, begin)}, fmt.substr(begin)};
8448a6aa6cSLei Zhang   }
8548a6aa6cSLei Zhang 
8648a6aa6cSLei Zhang   // The first placeholder is at the beginning
8748a6aa6cSLei Zhang 
8848a6aa6cSLei Zhang   if (fmt.size() == 1) {
8948a6aa6cSLei Zhang     // The whole format string just contains '$': treat as literal.
9048a6aa6cSLei Zhang     return {FmtReplacement{fmt}, StringRef()};
9148a6aa6cSLei Zhang   }
9248a6aa6cSLei Zhang 
9348a6aa6cSLei Zhang   // Allow escaping dollar with '$$'
9448a6aa6cSLei Zhang   if (fmt[1] == '$') {
9548a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)};
9648a6aa6cSLei Zhang   }
9748a6aa6cSLei Zhang 
9848a6aa6cSLei Zhang   // First try to see if it's a positional placeholder, and then handle special
9948a6aa6cSLei Zhang   // placeholders.
10048a6aa6cSLei Zhang 
1015bc9cc13SJacques Pienaar   size_t end =
1025bc9cc13SJacques Pienaar       fmt.find_if_not([](char c) { return std::isdigit(c); }, /*From=*/1);
10348a6aa6cSLei Zhang   if (end != 1) {
10448a6aa6cSLei Zhang     // We have a positional placeholder. Parse the index.
10548a6aa6cSLei Zhang     size_t index = 0;
10648a6aa6cSLei Zhang     if (fmt.substr(1, end - 1).consumeInteger(0, index)) {
10748a6aa6cSLei Zhang       llvm_unreachable("invalid replacement sequence index");
10848a6aa6cSLei Zhang     }
10948a6aa6cSLei Zhang 
1105bc9cc13SJacques Pienaar     // Check if this is the part of a range specification.
1115bc9cc13SJacques Pienaar     if (fmt.substr(end, 3) == "...") {
1125bc9cc13SJacques Pienaar       // Currently only ranges without upper bound are supported.
1135bc9cc13SJacques Pienaar       return {
1145bc9cc13SJacques Pienaar           FmtReplacement{fmt.substr(0, end + 3), index, FmtReplacement::kUnset},
1155bc9cc13SJacques Pienaar           fmt.substr(end + 3)};
1165bc9cc13SJacques Pienaar     }
1175bc9cc13SJacques Pienaar 
11848a6aa6cSLei Zhang     if (end == StringRef::npos) {
11948a6aa6cSLei Zhang       // All the remaining characters are part of the positional placeholder.
12048a6aa6cSLei Zhang       return {FmtReplacement{fmt, index}, StringRef()};
12148a6aa6cSLei Zhang     }
12248a6aa6cSLei Zhang     return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};
12348a6aa6cSLei Zhang   }
12448a6aa6cSLei Zhang 
12548a6aa6cSLei Zhang   end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1);
12648a6aa6cSLei Zhang   auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1));
12748a6aa6cSLei Zhang   if (end == StringRef::npos) {
12848a6aa6cSLei Zhang     // All the remaining characters are part of the special placeholder.
12948a6aa6cSLei Zhang     return {FmtReplacement{fmt, placeholder}, StringRef()};
13048a6aa6cSLei Zhang   }
13148a6aa6cSLei Zhang   return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};
13248a6aa6cSLei Zhang }
13348a6aa6cSLei Zhang 
parseFormatString(StringRef fmt)13448a6aa6cSLei Zhang std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
13548a6aa6cSLei Zhang   std::vector<FmtReplacement> replacements;
13648a6aa6cSLei Zhang   FmtReplacement repl;
13748a6aa6cSLei Zhang   while (!fmt.empty()) {
13848a6aa6cSLei Zhang     std::tie(repl, fmt) = splitFmtSegment(fmt);
13948a6aa6cSLei Zhang     if (repl.type != FmtReplacement::Type::Empty)
14048a6aa6cSLei Zhang       replacements.push_back(repl);
14148a6aa6cSLei Zhang   }
14248a6aa6cSLei Zhang   return replacements;
14348a6aa6cSLei Zhang }
14448a6aa6cSLei Zhang 
format(raw_ostream & s) const14548a6aa6cSLei Zhang void FmtObjectBase::format(raw_ostream &s) const {
14648a6aa6cSLei Zhang   for (auto &repl : replacements) {
14748a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::Empty)
14848a6aa6cSLei Zhang       continue;
14948a6aa6cSLei Zhang 
15048a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::Literal) {
15148a6aa6cSLei Zhang       s << repl.spec;
15248a6aa6cSLei Zhang       continue;
15348a6aa6cSLei Zhang     }
15448a6aa6cSLei Zhang 
15548a6aa6cSLei Zhang     if (repl.type == FmtReplacement::Type::SpecialPH) {
15648a6aa6cSLei Zhang       if (repl.placeholder == FmtContext::PHKind::None) {
15748a6aa6cSLei Zhang         s << repl.spec;
15848a6aa6cSLei Zhang       } else if (!context) {
15948a6aa6cSLei Zhang         // We need the context to replace special placeholders.
16048a6aa6cSLei Zhang         s << repl.spec << kMarkerForNoSubst;
16148a6aa6cSLei Zhang       } else {
1623cfe412eSFangrui Song         std::optional<StringRef> subst;
16348a6aa6cSLei Zhang         if (repl.placeholder == FmtContext::PHKind::Custom) {
16448a6aa6cSLei Zhang           // Skip the leading '$' sign for the custom placeholder
16548a6aa6cSLei Zhang           subst = context->getSubstFor(repl.spec.substr(1));
16648a6aa6cSLei Zhang         } else {
16748a6aa6cSLei Zhang           subst = context->getSubstFor(repl.placeholder);
16848a6aa6cSLei Zhang         }
16948a6aa6cSLei Zhang         if (subst)
17048a6aa6cSLei Zhang           s << *subst;
17148a6aa6cSLei Zhang         else
17248a6aa6cSLei Zhang           s << repl.spec << kMarkerForNoSubst;
17348a6aa6cSLei Zhang       }
17448a6aa6cSLei Zhang       continue;
17548a6aa6cSLei Zhang     }
17648a6aa6cSLei Zhang 
1775bc9cc13SJacques Pienaar     if (repl.type == FmtReplacement::Type::PositionalRangePH) {
1785bc9cc13SJacques Pienaar       if (repl.index >= adapters.size()) {
1795bc9cc13SJacques Pienaar         s << repl.spec << kMarkerForNoSubst;
1805bc9cc13SJacques Pienaar         continue;
1815bc9cc13SJacques Pienaar       }
182984b800aSserge-sans-paille       auto range = llvm::ArrayRef(adapters);
1835bc9cc13SJacques Pienaar       range = range.drop_front(repl.index);
1845bc9cc13SJacques Pienaar       if (repl.end != FmtReplacement::kUnset)
1855bc9cc13SJacques Pienaar         range = range.drop_back(adapters.size() - repl.end);
1865bc9cc13SJacques Pienaar       llvm::interleaveComma(range, s,
1875bc9cc13SJacques Pienaar                             [&](auto &x) { x->format(s, /*Options=*/""); });
1885bc9cc13SJacques Pienaar       continue;
1895bc9cc13SJacques Pienaar     }
1905bc9cc13SJacques Pienaar 
19148a6aa6cSLei Zhang     assert(repl.type == FmtReplacement::Type::PositionalPH);
19248a6aa6cSLei Zhang 
19348a6aa6cSLei Zhang     if (repl.index >= adapters.size()) {
19448a6aa6cSLei Zhang       s << repl.spec << kMarkerForNoSubst;
19548a6aa6cSLei Zhang       continue;
19648a6aa6cSLei Zhang     }
19748a6aa6cSLei Zhang     adapters[repl.index]->format(s, /*Options=*/"");
19848a6aa6cSLei Zhang   }
19948a6aa6cSLei Zhang }
20002834e1bSVladislav Vinogradov 
FmtStrVecObject(StringRef fmt,const FmtContext * ctx,ArrayRef<std::string> params)20102834e1bSVladislav Vinogradov FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
20202834e1bSVladislav Vinogradov                                  ArrayRef<std::string> params)
20302834e1bSVladislav Vinogradov     : FmtObjectBase(fmt, ctx, params.size()) {
20402834e1bSVladislav Vinogradov   parameters.reserve(params.size());
20502834e1bSVladislav Vinogradov   for (std::string p : params)
206*b714fc7fSChenguang Wang     parameters.push_back(
207*b714fc7fSChenguang Wang         llvm::support::detail::build_format_adapter(std::move(p)));
20802834e1bSVladislav Vinogradov 
20902834e1bSVladislav Vinogradov   adapters.reserve(parameters.size());
21002834e1bSVladislav Vinogradov   for (auto &p : parameters)
21102834e1bSVladislav Vinogradov     adapters.push_back(&p);
21202834e1bSVladislav Vinogradov }
21302834e1bSVladislav Vinogradov 
FmtStrVecObject(FmtStrVecObject && that)21402834e1bSVladislav Vinogradov FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)
21502834e1bSVladislav Vinogradov     : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
21602834e1bSVladislav Vinogradov   adapters.reserve(parameters.size());
21702834e1bSVladislav Vinogradov   for (auto &p : parameters)
21802834e1bSVladislav Vinogradov     adapters.push_back(&p);
21902834e1bSVladislav Vinogradov }
220