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