1d88c1a5aSDimitry Andric //===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===//
2d88c1a5aSDimitry Andric //
3d88c1a5aSDimitry Andric // The LLVM Compiler Infrastructure
4d88c1a5aSDimitry Andric //
5d88c1a5aSDimitry Andric // This file is distributed under the University of Illinois Open Source
6d88c1a5aSDimitry Andric // License. See LICENSE.TXT for details.
7d88c1a5aSDimitry Andric //===----------------------------------------------------------------------===//
8d88c1a5aSDimitry Andric
9d88c1a5aSDimitry Andric #include "llvm/Support/FormatVariadic.h"
10d88c1a5aSDimitry Andric
11d88c1a5aSDimitry Andric using namespace llvm;
12d88c1a5aSDimitry Andric
translateLocChar(char C)13d88c1a5aSDimitry Andric static Optional<AlignStyle> translateLocChar(char C) {
14d88c1a5aSDimitry Andric switch (C) {
15d88c1a5aSDimitry Andric case '-':
16d88c1a5aSDimitry Andric return AlignStyle::Left;
17d88c1a5aSDimitry Andric case '=':
18d88c1a5aSDimitry Andric return AlignStyle::Center;
19d88c1a5aSDimitry Andric case '+':
20d88c1a5aSDimitry Andric return AlignStyle::Right;
21d88c1a5aSDimitry Andric default:
22d88c1a5aSDimitry Andric return None;
23d88c1a5aSDimitry Andric }
24d88c1a5aSDimitry Andric LLVM_BUILTIN_UNREACHABLE;
25d88c1a5aSDimitry Andric }
26d88c1a5aSDimitry Andric
consumeFieldLayout(StringRef & Spec,AlignStyle & Where,size_t & Align,char & Pad)27d88c1a5aSDimitry Andric bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
28d88c1a5aSDimitry Andric size_t &Align, char &Pad) {
29d88c1a5aSDimitry Andric Where = AlignStyle::Right;
30d88c1a5aSDimitry Andric Align = 0;
31d88c1a5aSDimitry Andric Pad = ' ';
32d88c1a5aSDimitry Andric if (Spec.empty())
33d88c1a5aSDimitry Andric return true;
34d88c1a5aSDimitry Andric
35d88c1a5aSDimitry Andric if (Spec.size() > 1) {
36d88c1a5aSDimitry Andric // A maximum of 2 characters at the beginning can be used for something
37d88c1a5aSDimitry Andric // other
38d88c1a5aSDimitry Andric // than the width.
39d88c1a5aSDimitry Andric // If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
40d88c1a5aSDimitry Andric // contains the width.
41d88c1a5aSDimitry Andric // Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
42d88c1a5aSDimitry Andric // Otherwise, Spec[0:...] contains the width.
43d88c1a5aSDimitry Andric if (auto Loc = translateLocChar(Spec[1])) {
44d88c1a5aSDimitry Andric Pad = Spec[0];
45d88c1a5aSDimitry Andric Where = *Loc;
46d88c1a5aSDimitry Andric Spec = Spec.drop_front(2);
47d88c1a5aSDimitry Andric } else if (auto Loc = translateLocChar(Spec[0])) {
48d88c1a5aSDimitry Andric Where = *Loc;
49d88c1a5aSDimitry Andric Spec = Spec.drop_front(1);
50d88c1a5aSDimitry Andric }
51d88c1a5aSDimitry Andric }
52d88c1a5aSDimitry Andric
53d88c1a5aSDimitry Andric bool Failed = Spec.consumeInteger(0, Align);
54d88c1a5aSDimitry Andric return !Failed;
55d88c1a5aSDimitry Andric }
56d88c1a5aSDimitry Andric
57d88c1a5aSDimitry Andric Optional<ReplacementItem>
parseReplacementItem(StringRef Spec)58d88c1a5aSDimitry Andric formatv_object_base::parseReplacementItem(StringRef Spec) {
59d88c1a5aSDimitry Andric StringRef RepString = Spec.trim("{}");
60d88c1a5aSDimitry Andric
61d88c1a5aSDimitry Andric // If the replacement sequence does not start with a non-negative integer,
62d88c1a5aSDimitry Andric // this is an error.
63d88c1a5aSDimitry Andric char Pad = ' ';
64d88c1a5aSDimitry Andric std::size_t Align = 0;
65d88c1a5aSDimitry Andric AlignStyle Where = AlignStyle::Right;
66d88c1a5aSDimitry Andric StringRef Options;
67d88c1a5aSDimitry Andric size_t Index = 0;
68d88c1a5aSDimitry Andric RepString = RepString.trim();
69d88c1a5aSDimitry Andric if (RepString.consumeInteger(0, Index)) {
70d88c1a5aSDimitry Andric assert(false && "Invalid replacement sequence index!");
71d88c1a5aSDimitry Andric return ReplacementItem{};
72d88c1a5aSDimitry Andric }
73d88c1a5aSDimitry Andric RepString = RepString.trim();
74d88c1a5aSDimitry Andric if (!RepString.empty() && RepString.front() == ',') {
75d88c1a5aSDimitry Andric RepString = RepString.drop_front();
76d88c1a5aSDimitry Andric if (!consumeFieldLayout(RepString, Where, Align, Pad))
77d88c1a5aSDimitry Andric assert(false && "Invalid replacement field layout specification!");
78d88c1a5aSDimitry Andric }
79d88c1a5aSDimitry Andric RepString = RepString.trim();
80d88c1a5aSDimitry Andric if (!RepString.empty() && RepString.front() == ':') {
81d88c1a5aSDimitry Andric Options = RepString.drop_front().trim();
82d88c1a5aSDimitry Andric RepString = StringRef();
83d88c1a5aSDimitry Andric }
84d88c1a5aSDimitry Andric RepString = RepString.trim();
85d88c1a5aSDimitry Andric if (!RepString.empty()) {
86d88c1a5aSDimitry Andric assert(false && "Unexpected characters found in replacement string!");
87d88c1a5aSDimitry Andric }
88d88c1a5aSDimitry Andric
89d88c1a5aSDimitry Andric return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
90d88c1a5aSDimitry Andric }
91d88c1a5aSDimitry Andric
92d88c1a5aSDimitry Andric std::pair<ReplacementItem, StringRef>
splitLiteralAndReplacement(StringRef Fmt)93d88c1a5aSDimitry Andric formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) {
94d88c1a5aSDimitry Andric std::size_t From = 0;
95d88c1a5aSDimitry Andric while (From < Fmt.size() && From != StringRef::npos) {
96d88c1a5aSDimitry Andric std::size_t BO = Fmt.find_first_of('{', From);
97d88c1a5aSDimitry Andric // Everything up until the first brace is a literal.
98d88c1a5aSDimitry Andric if (BO != 0)
99d88c1a5aSDimitry Andric return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO));
100d88c1a5aSDimitry Andric
101d88c1a5aSDimitry Andric StringRef Braces =
102d88c1a5aSDimitry Andric Fmt.drop_front(BO).take_while([](char C) { return C == '{'; });
103d88c1a5aSDimitry Andric // If there is more than one brace, then some of them are escaped. Treat
104d88c1a5aSDimitry Andric // these as replacements.
105d88c1a5aSDimitry Andric if (Braces.size() > 1) {
106d88c1a5aSDimitry Andric size_t NumEscapedBraces = Braces.size() / 2;
107d88c1a5aSDimitry Andric StringRef Middle = Fmt.substr(BO, NumEscapedBraces);
108d88c1a5aSDimitry Andric StringRef Right = Fmt.drop_front(BO + NumEscapedBraces * 2);
109d88c1a5aSDimitry Andric return std::make_pair(ReplacementItem{Middle}, Right);
110d88c1a5aSDimitry Andric }
111d88c1a5aSDimitry Andric // An unterminated open brace is undefined. We treat the rest of the string
112d88c1a5aSDimitry Andric // as a literal replacement, but we assert to indicate that this is
113d88c1a5aSDimitry Andric // undefined and that we consider it an error.
114d88c1a5aSDimitry Andric std::size_t BC = Fmt.find_first_of('}', BO);
115d88c1a5aSDimitry Andric if (BC == StringRef::npos) {
116d88c1a5aSDimitry Andric assert(
117d88c1a5aSDimitry Andric false &&
118d88c1a5aSDimitry Andric "Unterminated brace sequence. Escape with {{ for a literal brace.");
119d88c1a5aSDimitry Andric return std::make_pair(ReplacementItem{Fmt}, StringRef());
120d88c1a5aSDimitry Andric }
121d88c1a5aSDimitry Andric
122d88c1a5aSDimitry Andric // Even if there is a closing brace, if there is another open brace before
123d88c1a5aSDimitry Andric // this closing brace, treat this portion as literal, and try again with the
124d88c1a5aSDimitry Andric // next one.
125d88c1a5aSDimitry Andric std::size_t BO2 = Fmt.find_first_of('{', BO + 1);
126d88c1a5aSDimitry Andric if (BO2 < BC)
127d88c1a5aSDimitry Andric return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)},
128d88c1a5aSDimitry Andric Fmt.substr(BO2));
129d88c1a5aSDimitry Andric
130d88c1a5aSDimitry Andric StringRef Spec = Fmt.slice(BO + 1, BC);
131d88c1a5aSDimitry Andric StringRef Right = Fmt.substr(BC + 1);
132d88c1a5aSDimitry Andric
133d88c1a5aSDimitry Andric auto RI = parseReplacementItem(Spec);
134d88c1a5aSDimitry Andric if (RI.hasValue())
135d88c1a5aSDimitry Andric return std::make_pair(*RI, Right);
136d88c1a5aSDimitry Andric
137d88c1a5aSDimitry Andric // If there was an error parsing the replacement item, treat it as an
138d88c1a5aSDimitry Andric // invalid replacement spec, and just continue.
139d88c1a5aSDimitry Andric From = BC + 1;
140d88c1a5aSDimitry Andric }
141d88c1a5aSDimitry Andric return std::make_pair(ReplacementItem{Fmt}, StringRef());
142d88c1a5aSDimitry Andric }
143d88c1a5aSDimitry Andric
144d88c1a5aSDimitry Andric std::vector<ReplacementItem>
parseFormatString(StringRef Fmt)145d88c1a5aSDimitry Andric formatv_object_base::parseFormatString(StringRef Fmt) {
146d88c1a5aSDimitry Andric std::vector<ReplacementItem> Replacements;
147d88c1a5aSDimitry Andric ReplacementItem I;
148d88c1a5aSDimitry Andric while (!Fmt.empty()) {
149d88c1a5aSDimitry Andric std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
150d88c1a5aSDimitry Andric if (I.Type != ReplacementType::Empty)
151d88c1a5aSDimitry Andric Replacements.push_back(I);
152d88c1a5aSDimitry Andric }
153d88c1a5aSDimitry Andric return Replacements;
154d88c1a5aSDimitry Andric }
155*b5893f02SDimitry Andric
anchor()156*b5893f02SDimitry Andric void detail::format_adapter::anchor() { }
157