brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.1 KiB · 65f4ad5 Raw
220 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines utilities for formatting strings. They are specially10// 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 "llvm/ADT/StringSwitch.h"17#include "llvm/ADT/Twine.h"18#include <cctype>19 20using namespace mlir;21using namespace mlir::tblgen;22 23// Marker to indicate an error happened when replacing a placeholder.24const char *const kMarkerForNoSubst = "<no-subst-found>";25 26FmtContext::FmtContext(ArrayRef<std::pair<StringRef, StringRef>> subs) {27  for (auto &sub : subs)28    addSubst(sub.first, sub.second);29}30 31FmtContext &FmtContext::addSubst(StringRef placeholder, const Twine &subst) {32  customSubstMap[placeholder] = subst.str();33  return *this;34}35 36FmtContext &FmtContext::withBuilder(Twine subst) {37  builtinSubstMap[PHKind::Builder] = subst.str();38  return *this;39}40 41FmtContext &FmtContext::withSelf(Twine subst) {42  builtinSubstMap[PHKind::Self] = subst.str();43  return *this;44}45 46std::optional<StringRef>47FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {48  if (placeholder == FmtContext::PHKind::None ||49      placeholder == FmtContext::PHKind::Custom)50    return {};51  auto it = builtinSubstMap.find(placeholder);52  if (it == builtinSubstMap.end())53    return {};54  return StringRef(it->second);55}56 57std::optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {58  auto it = customSubstMap.find(placeholder);59  if (it == customSubstMap.end())60    return {};61  return StringRef(it->second);62}63 64FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {65  return StringSwitch<FmtContext::PHKind>(str)66      .Case("_builder", FmtContext::PHKind::Builder)67      .Case("_self", FmtContext::PHKind::Self)68      .Case("", FmtContext::PHKind::None)69      .Default(FmtContext::PHKind::Custom);70}71 72std::pair<FmtReplacement, StringRef>73FmtObjectBase::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 a77    // 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 format82    // 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 beginning87 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 special99  // placeholders.100 101  size_t end =102      fmt.find_if_not([](char c) { return std::isdigit(c); }, /*From=*/1);103  if (end != 1) {104    // We have a positional placeholder. Parse the index.105    size_t index = 0;106    if (fmt.substr(1, end - 1).consumeInteger(0, index)) {107      llvm_unreachable("invalid replacement sequence index");108    }109 110    // Check if this is the part of a range specification.111    if (fmt.substr(end, 3) == "...") {112      // Currently only ranges without upper bound are supported.113      return {114          FmtReplacement{fmt.substr(0, end + 3), index, FmtReplacement::kUnset},115          fmt.substr(end + 3)};116    }117 118    if (end == StringRef::npos) {119      // All the remaining characters are part of the positional placeholder.120      return {FmtReplacement{fmt, index}, StringRef()};121    }122    return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};123  }124 125  end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1);126  auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1));127  if (end == StringRef::npos) {128    // All the remaining characters are part of the special placeholder.129    return {FmtReplacement{fmt, placeholder}, StringRef()};130  }131  return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};132}133 134std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {135  std::vector<FmtReplacement> replacements;136  FmtReplacement repl;137  while (!fmt.empty()) {138    std::tie(repl, fmt) = splitFmtSegment(fmt);139    if (repl.type != FmtReplacement::Type::Empty)140      replacements.push_back(repl);141  }142  return replacements;143}144 145void FmtObjectBase::format(raw_ostream &s) const {146  for (auto &repl : replacements) {147    if (repl.type == FmtReplacement::Type::Empty)148      continue;149 150    if (repl.type == FmtReplacement::Type::Literal) {151      s << repl.spec;152      continue;153    }154 155    if (repl.type == FmtReplacement::Type::SpecialPH) {156      if (repl.placeholder == FmtContext::PHKind::None) {157        s << repl.spec;158      } else if (!context) {159        // We need the context to replace special placeholders.160        s << repl.spec << kMarkerForNoSubst;161      } else {162        std::optional<StringRef> subst;163        if (repl.placeholder == FmtContext::PHKind::Custom) {164          // Skip the leading '$' sign for the custom placeholder165          subst = context->getSubstFor(repl.spec.substr(1));166        } else {167          subst = context->getSubstFor(repl.placeholder);168        }169        if (subst)170          s << *subst;171        else172          s << repl.spec << kMarkerForNoSubst;173      }174      continue;175    }176 177    if (repl.type == FmtReplacement::Type::PositionalRangePH) {178      if (repl.index >= adapters.size()) {179        s << repl.spec << kMarkerForNoSubst;180        continue;181      }182      auto range = llvm::ArrayRef(adapters);183      range = range.drop_front(repl.index);184      if (repl.end != FmtReplacement::kUnset)185        range = range.drop_back(adapters.size() - repl.end);186      llvm::interleaveComma(range, s,187                            [&](auto &x) { x->format(s, /*Options=*/""); });188      continue;189    }190 191    assert(repl.type == FmtReplacement::Type::PositionalPH);192 193    if (repl.index >= adapters.size()) {194      s << repl.spec << kMarkerForNoSubst;195      continue;196    }197    adapters[repl.index]->format(s, /*Options=*/"");198  }199}200 201FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,202                                 ArrayRef<std::string> params)203    : FmtObjectBase(fmt, ctx, params.size()) {204  parameters.reserve(params.size());205  for (std::string p : params)206    parameters.push_back(207        llvm::support::detail::build_format_adapter(std::move(p)));208 209  adapters.reserve(parameters.size());210  for (auto &p : parameters)211    adapters.push_back(&p);212}213 214FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)215    : FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {216  adapters.reserve(parameters.size());217  for (auto &p : parameters)218    adapters.push_back(&p);219}220