forked from OSchip/llvm-project
218 lines
7.0 KiB
C++
218 lines
7.0 KiB
C++
//===- Format.cpp - Utilities for String Format ---------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines utilities for formatting strings. They are specially
|
|
// tailored to the needs of TableGen'ing op definitions and rewrite rules,
|
|
// so they are not expected to be used as widely applicable utilities.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/TableGen/Format.h"
|
|
#include <cctype>
|
|
|
|
using namespace mlir;
|
|
using namespace mlir::tblgen;
|
|
|
|
// Marker to indicate an error happened when replacing a placeholder.
|
|
const char *const kMarkerForNoSubst = "<no-subst-found>";
|
|
|
|
FmtContext &FmtContext::addSubst(StringRef placeholder, Twine subst) {
|
|
customSubstMap[placeholder] = subst.str();
|
|
return *this;
|
|
}
|
|
|
|
FmtContext &FmtContext::withBuilder(Twine subst) {
|
|
builtinSubstMap[PHKind::Builder] = subst.str();
|
|
return *this;
|
|
}
|
|
|
|
FmtContext &FmtContext::withOp(Twine subst) {
|
|
builtinSubstMap[PHKind::Op] = subst.str();
|
|
return *this;
|
|
}
|
|
|
|
FmtContext &FmtContext::withSelf(Twine subst) {
|
|
builtinSubstMap[PHKind::Self] = subst.str();
|
|
return *this;
|
|
}
|
|
|
|
Optional<StringRef>
|
|
FmtContext::getSubstFor(FmtContext::PHKind placeholder) const {
|
|
if (placeholder == FmtContext::PHKind::None ||
|
|
placeholder == FmtContext::PHKind::Custom)
|
|
return {};
|
|
auto it = builtinSubstMap.find(placeholder);
|
|
if (it == builtinSubstMap.end())
|
|
return {};
|
|
return StringRef(it->second);
|
|
}
|
|
|
|
Optional<StringRef> FmtContext::getSubstFor(StringRef placeholder) const {
|
|
auto it = customSubstMap.find(placeholder);
|
|
if (it == customSubstMap.end())
|
|
return {};
|
|
return StringRef(it->second);
|
|
}
|
|
|
|
FmtContext::PHKind FmtContext::getPlaceHolderKind(StringRef str) {
|
|
return StringSwitch<FmtContext::PHKind>(str)
|
|
.Case("_builder", FmtContext::PHKind::Builder)
|
|
.Case("_op", FmtContext::PHKind::Op)
|
|
.Case("_self", FmtContext::PHKind::Self)
|
|
.Case("", FmtContext::PHKind::None)
|
|
.Default(FmtContext::PHKind::Custom);
|
|
}
|
|
|
|
std::pair<FmtReplacement, StringRef>
|
|
FmtObjectBase::splitFmtSegment(StringRef fmt) {
|
|
size_t begin = fmt.find_first_of('$');
|
|
if (begin == StringRef::npos) {
|
|
// No placeholders: the whole format string should be returned as a
|
|
// literal string.
|
|
return {FmtReplacement{fmt}, StringRef()};
|
|
}
|
|
if (begin != 0) {
|
|
// The first placeholder is not at the beginning: we can split the format
|
|
// string into a literal string and the rest.
|
|
return {FmtReplacement{fmt.substr(0, begin)}, fmt.substr(begin)};
|
|
}
|
|
|
|
// The first placeholder is at the beginning
|
|
|
|
if (fmt.size() == 1) {
|
|
// The whole format string just contains '$': treat as literal.
|
|
return {FmtReplacement{fmt}, StringRef()};
|
|
}
|
|
|
|
// Allow escaping dollar with '$$'
|
|
if (fmt[1] == '$') {
|
|
return {FmtReplacement{fmt.substr(0, 1)}, fmt.substr(2)};
|
|
}
|
|
|
|
// First try to see if it's a positional placeholder, and then handle special
|
|
// placeholders.
|
|
|
|
size_t end =
|
|
fmt.find_if_not([](char c) { return std::isdigit(c); }, /*From=*/1);
|
|
if (end != 1) {
|
|
// We have a positional placeholder. Parse the index.
|
|
size_t index = 0;
|
|
if (fmt.substr(1, end - 1).consumeInteger(0, index)) {
|
|
llvm_unreachable("invalid replacement sequence index");
|
|
}
|
|
|
|
// Check if this is the part of a range specification.
|
|
if (fmt.substr(end, 3) == "...") {
|
|
// Currently only ranges without upper bound are supported.
|
|
return {
|
|
FmtReplacement{fmt.substr(0, end + 3), index, FmtReplacement::kUnset},
|
|
fmt.substr(end + 3)};
|
|
}
|
|
|
|
if (end == StringRef::npos) {
|
|
// All the remaining characters are part of the positional placeholder.
|
|
return {FmtReplacement{fmt, index}, StringRef()};
|
|
}
|
|
return {FmtReplacement{fmt.substr(0, end), index}, fmt.substr(end)};
|
|
}
|
|
|
|
end = fmt.find_if_not([](char c) { return std::isalnum(c) || c == '_'; }, 1);
|
|
auto placeholder = FmtContext::getPlaceHolderKind(fmt.substr(1, end - 1));
|
|
if (end == StringRef::npos) {
|
|
// All the remaining characters are part of the special placeholder.
|
|
return {FmtReplacement{fmt, placeholder}, StringRef()};
|
|
}
|
|
return {FmtReplacement{fmt.substr(0, end), placeholder}, fmt.substr(end)};
|
|
}
|
|
|
|
std::vector<FmtReplacement> FmtObjectBase::parseFormatString(StringRef fmt) {
|
|
std::vector<FmtReplacement> replacements;
|
|
FmtReplacement repl;
|
|
while (!fmt.empty()) {
|
|
std::tie(repl, fmt) = splitFmtSegment(fmt);
|
|
if (repl.type != FmtReplacement::Type::Empty)
|
|
replacements.push_back(repl);
|
|
}
|
|
return replacements;
|
|
}
|
|
|
|
void FmtObjectBase::format(raw_ostream &s) const {
|
|
for (auto &repl : replacements) {
|
|
if (repl.type == FmtReplacement::Type::Empty)
|
|
continue;
|
|
|
|
if (repl.type == FmtReplacement::Type::Literal) {
|
|
s << repl.spec;
|
|
continue;
|
|
}
|
|
|
|
if (repl.type == FmtReplacement::Type::SpecialPH) {
|
|
if (repl.placeholder == FmtContext::PHKind::None) {
|
|
s << repl.spec;
|
|
} else if (!context) {
|
|
// We need the context to replace special placeholders.
|
|
s << repl.spec << kMarkerForNoSubst;
|
|
} else {
|
|
Optional<StringRef> subst;
|
|
if (repl.placeholder == FmtContext::PHKind::Custom) {
|
|
// Skip the leading '$' sign for the custom placeholder
|
|
subst = context->getSubstFor(repl.spec.substr(1));
|
|
} else {
|
|
subst = context->getSubstFor(repl.placeholder);
|
|
}
|
|
if (subst)
|
|
s << *subst;
|
|
else
|
|
s << repl.spec << kMarkerForNoSubst;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (repl.type == FmtReplacement::Type::PositionalRangePH) {
|
|
if (repl.index >= adapters.size()) {
|
|
s << repl.spec << kMarkerForNoSubst;
|
|
continue;
|
|
}
|
|
auto range = llvm::makeArrayRef(adapters);
|
|
range = range.drop_front(repl.index);
|
|
if (repl.end != FmtReplacement::kUnset)
|
|
range = range.drop_back(adapters.size() - repl.end);
|
|
llvm::interleaveComma(range, s,
|
|
[&](auto &x) { x->format(s, /*Options=*/""); });
|
|
continue;
|
|
}
|
|
|
|
assert(repl.type == FmtReplacement::Type::PositionalPH);
|
|
|
|
if (repl.index >= adapters.size()) {
|
|
s << repl.spec << kMarkerForNoSubst;
|
|
continue;
|
|
}
|
|
adapters[repl.index]->format(s, /*Options=*/"");
|
|
}
|
|
}
|
|
|
|
FmtStrVecObject::FmtStrVecObject(StringRef fmt, const FmtContext *ctx,
|
|
ArrayRef<std::string> params)
|
|
: FmtObjectBase(fmt, ctx, params.size()) {
|
|
parameters.reserve(params.size());
|
|
for (std::string p : params)
|
|
parameters.push_back(llvm::detail::build_format_adapter(std::move(p)));
|
|
|
|
adapters.reserve(parameters.size());
|
|
for (auto &p : parameters)
|
|
adapters.push_back(&p);
|
|
}
|
|
|
|
FmtStrVecObject::FmtStrVecObject(FmtStrVecObject &&that)
|
|
: FmtObjectBase(std::move(that)), parameters(std::move(that.parameters)) {
|
|
adapters.reserve(parameters.size());
|
|
for (auto &p : parameters)
|
|
adapters.push_back(&p);
|
|
}
|