111db2642SZachary Turner //===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===// 211db2642SZachary Turner // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 611db2642SZachary Turner //===----------------------------------------------------------------------===// 711db2642SZachary Turner 811db2642SZachary Turner #include "llvm/Support/FormatVariadic.h" 9eb812efaSJoerg Sonnenberger #include <cassert> 1011db2642SZachary Turner 1111db2642SZachary Turner using namespace llvm; 1211db2642SZachary Turner 1311db2642SZachary Turner static Optional<AlignStyle> translateLocChar(char C) { 1411db2642SZachary Turner switch (C) { 1511db2642SZachary Turner case '-': 1611db2642SZachary Turner return AlignStyle::Left; 1711db2642SZachary Turner case '=': 1811db2642SZachary Turner return AlignStyle::Center; 1911db2642SZachary Turner case '+': 2011db2642SZachary Turner return AlignStyle::Right; 2111db2642SZachary Turner } 22*7275de7fSSimon Pilgrim return None; 2311db2642SZachary Turner } 2411db2642SZachary Turner 2511db2642SZachary Turner bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where, 2611db2642SZachary Turner size_t &Align, char &Pad) { 2711db2642SZachary Turner Where = AlignStyle::Right; 2811db2642SZachary Turner Align = 0; 2911db2642SZachary Turner Pad = ' '; 3011db2642SZachary Turner if (Spec.empty()) 3111db2642SZachary Turner return true; 3211db2642SZachary Turner 3311db2642SZachary Turner if (Spec.size() > 1) { 3411db2642SZachary Turner // A maximum of 2 characters at the beginning can be used for something 3511db2642SZachary Turner // other 3611db2642SZachary Turner // than the width. 3711db2642SZachary Turner // If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...] 3811db2642SZachary Turner // contains the width. 3911db2642SZachary Turner // Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width. 4011db2642SZachary Turner // Otherwise, Spec[0:...] contains the width. 4111db2642SZachary Turner if (auto Loc = translateLocChar(Spec[1])) { 4211db2642SZachary Turner Pad = Spec[0]; 4311db2642SZachary Turner Where = *Loc; 4411db2642SZachary Turner Spec = Spec.drop_front(2); 4511db2642SZachary Turner } else if (auto Loc = translateLocChar(Spec[0])) { 4611db2642SZachary Turner Where = *Loc; 4711db2642SZachary Turner Spec = Spec.drop_front(1); 4811db2642SZachary Turner } 4911db2642SZachary Turner } 5011db2642SZachary Turner 5111db2642SZachary Turner bool Failed = Spec.consumeInteger(0, Align); 5211db2642SZachary Turner return !Failed; 5311db2642SZachary Turner } 5411db2642SZachary Turner 5511db2642SZachary Turner Optional<ReplacementItem> 5611db2642SZachary Turner formatv_object_base::parseReplacementItem(StringRef Spec) { 5711db2642SZachary Turner StringRef RepString = Spec.trim("{}"); 5811db2642SZachary Turner 5911db2642SZachary Turner // If the replacement sequence does not start with a non-negative integer, 6011db2642SZachary Turner // this is an error. 6111db2642SZachary Turner char Pad = ' '; 6211db2642SZachary Turner std::size_t Align = 0; 6311db2642SZachary Turner AlignStyle Where = AlignStyle::Right; 6411db2642SZachary Turner StringRef Options; 6511db2642SZachary Turner size_t Index = 0; 6611db2642SZachary Turner RepString = RepString.trim(); 6711db2642SZachary Turner if (RepString.consumeInteger(0, Index)) { 6811db2642SZachary Turner assert(false && "Invalid replacement sequence index!"); 6911db2642SZachary Turner return ReplacementItem{}; 7011db2642SZachary Turner } 7111db2642SZachary Turner RepString = RepString.trim(); 7211db2642SZachary Turner if (!RepString.empty() && RepString.front() == ',') { 7311db2642SZachary Turner RepString = RepString.drop_front(); 7411db2642SZachary Turner if (!consumeFieldLayout(RepString, Where, Align, Pad)) 7511db2642SZachary Turner assert(false && "Invalid replacement field layout specification!"); 7611db2642SZachary Turner } 7711db2642SZachary Turner RepString = RepString.trim(); 7811db2642SZachary Turner if (!RepString.empty() && RepString.front() == ':') { 7911db2642SZachary Turner Options = RepString.drop_front().trim(); 8011db2642SZachary Turner RepString = StringRef(); 8111db2642SZachary Turner } 8211db2642SZachary Turner RepString = RepString.trim(); 8311db2642SZachary Turner if (!RepString.empty()) { 8411db2642SZachary Turner assert(false && "Unexpected characters found in replacement string!"); 8511db2642SZachary Turner } 8611db2642SZachary Turner 8711db2642SZachary Turner return ReplacementItem{Spec, Index, Align, Where, Pad, Options}; 8811db2642SZachary Turner } 8911db2642SZachary Turner 9011db2642SZachary Turner std::pair<ReplacementItem, StringRef> 9111db2642SZachary Turner formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) { 92ed88cd77SRahul Joshi while (!Fmt.empty()) { 9311db2642SZachary Turner // Everything up until the first brace is a literal. 94ed88cd77SRahul Joshi if (Fmt.front() != '{') { 95ed88cd77SRahul Joshi std::size_t BO = Fmt.find_first_of('{'); 9611db2642SZachary Turner return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO)); 97ed88cd77SRahul Joshi } 9811db2642SZachary Turner 99ed88cd77SRahul Joshi StringRef Braces = Fmt.take_while([](char C) { return C == '{'; }); 10011db2642SZachary Turner // If there is more than one brace, then some of them are escaped. Treat 10111db2642SZachary Turner // these as replacements. 10211db2642SZachary Turner if (Braces.size() > 1) { 10311db2642SZachary Turner size_t NumEscapedBraces = Braces.size() / 2; 104ed88cd77SRahul Joshi StringRef Middle = Fmt.take_front(NumEscapedBraces); 105ed88cd77SRahul Joshi StringRef Right = Fmt.drop_front(NumEscapedBraces * 2); 10611db2642SZachary Turner return std::make_pair(ReplacementItem{Middle}, Right); 10711db2642SZachary Turner } 10811db2642SZachary Turner // An unterminated open brace is undefined. We treat the rest of the string 10911db2642SZachary Turner // as a literal replacement, but we assert to indicate that this is 11011db2642SZachary Turner // undefined and that we consider it an error. 111ed88cd77SRahul Joshi std::size_t BC = Fmt.find_first_of('}'); 11211db2642SZachary Turner if (BC == StringRef::npos) { 11311db2642SZachary Turner assert( 11411db2642SZachary Turner false && 11511db2642SZachary Turner "Unterminated brace sequence. Escape with {{ for a literal brace."); 11611db2642SZachary Turner return std::make_pair(ReplacementItem{Fmt}, StringRef()); 11711db2642SZachary Turner } 11811db2642SZachary Turner 11911db2642SZachary Turner // Even if there is a closing brace, if there is another open brace before 12011db2642SZachary Turner // this closing brace, treat this portion as literal, and try again with the 12111db2642SZachary Turner // next one. 122ed88cd77SRahul Joshi std::size_t BO2 = Fmt.find_first_of('{', 1); 12311db2642SZachary Turner if (BO2 < BC) 12411db2642SZachary Turner return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)}, 12511db2642SZachary Turner Fmt.substr(BO2)); 12611db2642SZachary Turner 127ed88cd77SRahul Joshi StringRef Spec = Fmt.slice(1, BC); 12811db2642SZachary Turner StringRef Right = Fmt.substr(BC + 1); 12911db2642SZachary Turner 13011db2642SZachary Turner auto RI = parseReplacementItem(Spec); 13111db2642SZachary Turner if (RI.hasValue()) 13211db2642SZachary Turner return std::make_pair(*RI, Right); 13311db2642SZachary Turner 13411db2642SZachary Turner // If there was an error parsing the replacement item, treat it as an 13511db2642SZachary Turner // invalid replacement spec, and just continue. 136ed88cd77SRahul Joshi Fmt = Fmt.drop_front(BC + 1); 13711db2642SZachary Turner } 13811db2642SZachary Turner return std::make_pair(ReplacementItem{Fmt}, StringRef()); 13911db2642SZachary Turner } 14011db2642SZachary Turner 1415ef2cb3dSBenjamin Kramer SmallVector<ReplacementItem, 2> 14211db2642SZachary Turner formatv_object_base::parseFormatString(StringRef Fmt) { 1435ef2cb3dSBenjamin Kramer SmallVector<ReplacementItem, 2> Replacements; 14411db2642SZachary Turner ReplacementItem I; 14511db2642SZachary Turner while (!Fmt.empty()) { 14611db2642SZachary Turner std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt); 14711db2642SZachary Turner if (I.Type != ReplacementType::Empty) 14811db2642SZachary Turner Replacements.push_back(I); 14911db2642SZachary Turner } 15011db2642SZachary Turner return Replacements; 15111db2642SZachary Turner } 152a87b70d1SRichard Trieu 153a87b70d1SRichard Trieu void detail::format_adapter::anchor() { } 154