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 
translateLocChar(char C)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;
211b27940aSSimon Pilgrim   default:
227275de7fSSimon Pilgrim     return None;
2311db2642SZachary Turner   }
241b27940aSSimon Pilgrim   LLVM_BUILTIN_UNREACHABLE;
251b27940aSSimon Pilgrim }
2611db2642SZachary Turner 
consumeFieldLayout(StringRef & Spec,AlignStyle & Where,size_t & Align,char & Pad)2711db2642SZachary Turner bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
2811db2642SZachary Turner                                              size_t &Align, char &Pad) {
2911db2642SZachary Turner   Where = AlignStyle::Right;
3011db2642SZachary Turner   Align = 0;
3111db2642SZachary Turner   Pad = ' ';
3211db2642SZachary Turner   if (Spec.empty())
3311db2642SZachary Turner     return true;
3411db2642SZachary Turner 
3511db2642SZachary Turner   if (Spec.size() > 1) {
3611db2642SZachary Turner     // A maximum of 2 characters at the beginning can be used for something
3711db2642SZachary Turner     // other
3811db2642SZachary Turner     // than the width.
3911db2642SZachary Turner     // If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
4011db2642SZachary Turner     // contains the width.
4111db2642SZachary Turner     // Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
4211db2642SZachary Turner     // Otherwise, Spec[0:...] contains the width.
4311db2642SZachary Turner     if (auto Loc = translateLocChar(Spec[1])) {
4411db2642SZachary Turner       Pad = Spec[0];
4511db2642SZachary Turner       Where = *Loc;
4611db2642SZachary Turner       Spec = Spec.drop_front(2);
4711db2642SZachary Turner     } else if (auto Loc = translateLocChar(Spec[0])) {
4811db2642SZachary Turner       Where = *Loc;
4911db2642SZachary Turner       Spec = Spec.drop_front(1);
5011db2642SZachary Turner     }
5111db2642SZachary Turner   }
5211db2642SZachary Turner 
5311db2642SZachary Turner   bool Failed = Spec.consumeInteger(0, Align);
5411db2642SZachary Turner   return !Failed;
5511db2642SZachary Turner }
5611db2642SZachary Turner 
5711db2642SZachary Turner Optional<ReplacementItem>
parseReplacementItem(StringRef Spec)5811db2642SZachary Turner formatv_object_base::parseReplacementItem(StringRef Spec) {
5911db2642SZachary Turner   StringRef RepString = Spec.trim("{}");
6011db2642SZachary Turner 
6111db2642SZachary Turner   // If the replacement sequence does not start with a non-negative integer,
6211db2642SZachary Turner   // this is an error.
6311db2642SZachary Turner   char Pad = ' ';
6411db2642SZachary Turner   std::size_t Align = 0;
6511db2642SZachary Turner   AlignStyle Where = AlignStyle::Right;
6611db2642SZachary Turner   StringRef Options;
6711db2642SZachary Turner   size_t Index = 0;
6811db2642SZachary Turner   RepString = RepString.trim();
6911db2642SZachary Turner   if (RepString.consumeInteger(0, Index)) {
7011db2642SZachary Turner     assert(false && "Invalid replacement sequence index!");
7111db2642SZachary Turner     return ReplacementItem{};
7211db2642SZachary Turner   }
7311db2642SZachary Turner   RepString = RepString.trim();
7411db2642SZachary Turner   if (!RepString.empty() && RepString.front() == ',') {
7511db2642SZachary Turner     RepString = RepString.drop_front();
7611db2642SZachary Turner     if (!consumeFieldLayout(RepString, Where, Align, Pad))
7711db2642SZachary Turner       assert(false && "Invalid replacement field layout specification!");
7811db2642SZachary Turner   }
7911db2642SZachary Turner   RepString = RepString.trim();
8011db2642SZachary Turner   if (!RepString.empty() && RepString.front() == ':') {
8111db2642SZachary Turner     Options = RepString.drop_front().trim();
8211db2642SZachary Turner     RepString = StringRef();
8311db2642SZachary Turner   }
8411db2642SZachary Turner   RepString = RepString.trim();
8511db2642SZachary Turner   if (!RepString.empty()) {
8611db2642SZachary Turner     assert(false && "Unexpected characters found in replacement string!");
8711db2642SZachary Turner   }
8811db2642SZachary Turner 
8911db2642SZachary Turner   return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
9011db2642SZachary Turner }
9111db2642SZachary Turner 
9211db2642SZachary Turner std::pair<ReplacementItem, StringRef>
splitLiteralAndReplacement(StringRef Fmt)9311db2642SZachary Turner formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) {
94ed88cd77SRahul Joshi   while (!Fmt.empty()) {
9511db2642SZachary Turner     // Everything up until the first brace is a literal.
96ed88cd77SRahul Joshi     if (Fmt.front() != '{') {
97ed88cd77SRahul Joshi       std::size_t BO = Fmt.find_first_of('{');
9811db2642SZachary Turner       return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO));
99ed88cd77SRahul Joshi     }
10011db2642SZachary Turner 
101ed88cd77SRahul Joshi     StringRef Braces = Fmt.take_while([](char C) { return C == '{'; });
10211db2642SZachary Turner     // If there is more than one brace, then some of them are escaped.  Treat
10311db2642SZachary Turner     // these as replacements.
10411db2642SZachary Turner     if (Braces.size() > 1) {
10511db2642SZachary Turner       size_t NumEscapedBraces = Braces.size() / 2;
106ed88cd77SRahul Joshi       StringRef Middle = Fmt.take_front(NumEscapedBraces);
107ed88cd77SRahul Joshi       StringRef Right = Fmt.drop_front(NumEscapedBraces * 2);
10811db2642SZachary Turner       return std::make_pair(ReplacementItem{Middle}, Right);
10911db2642SZachary Turner     }
11011db2642SZachary Turner     // An unterminated open brace is undefined.  We treat the rest of the string
11111db2642SZachary Turner     // as a literal replacement, but we assert to indicate that this is
11211db2642SZachary Turner     // undefined and that we consider it an error.
113ed88cd77SRahul Joshi     std::size_t BC = Fmt.find_first_of('}');
11411db2642SZachary Turner     if (BC == StringRef::npos) {
11511db2642SZachary Turner       assert(
11611db2642SZachary Turner           false &&
11711db2642SZachary Turner           "Unterminated brace sequence.  Escape with {{ for a literal brace.");
11811db2642SZachary Turner       return std::make_pair(ReplacementItem{Fmt}, StringRef());
11911db2642SZachary Turner     }
12011db2642SZachary Turner 
12111db2642SZachary Turner     // Even if there is a closing brace, if there is another open brace before
12211db2642SZachary Turner     // this closing brace, treat this portion as literal, and try again with the
12311db2642SZachary Turner     // next one.
124ed88cd77SRahul Joshi     std::size_t BO2 = Fmt.find_first_of('{', 1);
12511db2642SZachary Turner     if (BO2 < BC)
12611db2642SZachary Turner       return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)},
12711db2642SZachary Turner                             Fmt.substr(BO2));
12811db2642SZachary Turner 
129ed88cd77SRahul Joshi     StringRef Spec = Fmt.slice(1, BC);
13011db2642SZachary Turner     StringRef Right = Fmt.substr(BC + 1);
13111db2642SZachary Turner 
13211db2642SZachary Turner     auto RI = parseReplacementItem(Spec);
133*e0e687a6SKazu Hirata     if (RI)
13411db2642SZachary Turner       return std::make_pair(*RI, Right);
13511db2642SZachary Turner 
13611db2642SZachary Turner     // If there was an error parsing the replacement item, treat it as an
13711db2642SZachary Turner     // invalid replacement spec, and just continue.
138ed88cd77SRahul Joshi     Fmt = Fmt.drop_front(BC + 1);
13911db2642SZachary Turner   }
14011db2642SZachary Turner   return std::make_pair(ReplacementItem{Fmt}, StringRef());
14111db2642SZachary Turner }
14211db2642SZachary Turner 
1435ef2cb3dSBenjamin Kramer SmallVector<ReplacementItem, 2>
parseFormatString(StringRef Fmt)14411db2642SZachary Turner formatv_object_base::parseFormatString(StringRef Fmt) {
1455ef2cb3dSBenjamin Kramer   SmallVector<ReplacementItem, 2> Replacements;
14611db2642SZachary Turner   ReplacementItem I;
14711db2642SZachary Turner   while (!Fmt.empty()) {
14811db2642SZachary Turner     std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
14911db2642SZachary Turner     if (I.Type != ReplacementType::Empty)
15011db2642SZachary Turner       Replacements.push_back(I);
15111db2642SZachary Turner   }
15211db2642SZachary Turner   return Replacements;
15311db2642SZachary Turner }
154a87b70d1SRichard Trieu 
anchor()155a87b70d1SRichard Trieu void detail::format_adapter::anchor() { }
156