Use pseudo parser for folding ranges
[platform/upstream/llvm.git] / clang-tools-extra / pseudo / include / clang-pseudo / Token.h
1 //===--- Token.h - Tokens and token streams in the pseudoparser --*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Tokens are the first level of abstraction above bytes used in pseudoparsing.
10 // We use clang's lexer to scan the bytes (in raw mode, with no preprocessor).
11 // The tokens is wrapped into pseudo::Token, along with line/indent info.
12 //
13 // Unlike clang, we make multiple passes over the whole file, out-of-order.
14 // Therefore we retain the whole token sequence in memory. (This is feasible as
15 // we process one file at a time). pseudo::TokenStream holds such a stream.
16 // The initial stream holds the raw tokens read from the file, later passes
17 // operate on derived TokenStreams (e.g. with directives stripped).
18 //
19 // Similar facilities from clang that are *not* used:
20 //  - SourceManager: designed around multiple files and precise macro expansion.
21 //  - clang::Token: coupled to SourceManager, doesn't retain layout info.
22 //                  (pseudo::Token is similar, but without SourceLocations).
23 //  - syntax::TokenBuffer: coupled to SourceManager, has #includes and macros.
24 //                  (pseudo::TokenStream is similar, but a flat token list).
25 //
26 //===----------------------------------------------------------------------===//
27
28 #ifndef CLANG_PSEUDO_TOKEN_H
29 #define CLANG_PSEUDO_TOKEN_H
30
31 #include "clang/Basic/LLVM.h"
32 #include "clang/Basic/LangStandard.h"
33 #include "clang/Basic/TokenKinds.h"
34 #include "llvm/ADT/ArrayRef.h"
35 #include "llvm/Support/raw_ostream.h"
36 #include <cstdint>
37 #include <limits>
38 #include <memory>
39 #include <vector>
40
41 namespace clang {
42 class LangOptions;
43 namespace pseudo {
44
45 /// A single C++ or preprocessor token.
46 ///
47 /// Unlike clang::Token and syntax::Token, these tokens are not connected to a
48 /// SourceManager - we are not dealing with multiple files.
49 struct Token {
50   /// An Index identifies a token within a stream.
51   using Index = uint32_t;
52   /// A sentinel Index indicating no token.
53   constexpr static Index Invalid = std::numeric_limits<Index>::max();
54   struct Range;
55
56   /// The token text.
57   ///
58   /// Typically from the original source file, but may have been synthesized.
59   StringRef text() const { return StringRef(Data, Length); }
60   const char *Data = nullptr;
61   uint32_t Length = 0;
62
63   /// Zero-based line number for the start of the token.
64   /// This refers to the original source file as written.
65   uint32_t Line = 0;
66   /// Width of whitespace before the first token on this line.
67   uint8_t Indent = 0;
68   /// Flags have some meaning defined by the function that produced this stream.
69   uint8_t Flags = 0;
70   /// Index into the original token stream (as raw-lexed from the source code).
71   Index OriginalIndex = Invalid;
72   // Helpers to get/set Flags based on `enum class`.
73   template <class T> bool flag(T Mask) const {
74     return Flags & uint8_t{static_cast<std::underlying_type_t<T>>(Mask)};
75   }
76   template <class T> void setFlag(T Mask) {
77     Flags |= uint8_t{static_cast<std::underlying_type_t<T>>(Mask)};
78   }
79
80   /// Returns the next token in the stream. this may not be a sentinel.
81   const Token &next() const {
82     assert(Kind != tok::eof);
83     return *(this + 1);
84   }
85   /// Returns the next token in the stream, skipping over comments.
86   const Token &nextNC() const {
87     const Token *T = this;
88     do
89       T = &T->next();
90     while (T->Kind == tok::comment);
91     return *T;
92   }
93   /// Returns the bracket paired with this one, if any.
94   const Token *pair() const { return Pair == 0 ? nullptr : this + Pair; }
95
96   /// The type of token as determined by clang's lexer.
97   clang::tok::TokenKind Kind = clang::tok::unknown;
98   /// If this token is a paired bracket, the offset of the pair in the stream.
99   int32_t Pair = 0;
100 };
101 static_assert(sizeof(Token) <= sizeof(char *) + 24, "Careful with layout!");
102 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Token &);
103
104 /// A half-open range of tokens within a stream.
105 struct Token::Range {
106   Index Begin = 0;
107   Index End = 0;
108
109   uint32_t size() const { return End - Begin; }
110   static Range emptyAt(Index Index) { return Range{Index, Index}; }
111 };
112 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Token::Range &);
113
114 /// A complete sequence of Tokens representing a source file.
115 ///
116 /// This may match a raw file from disk, or be derived from a previous stream.
117 /// For example, stripping comments from a TokenStream results in a new stream.
118 ///
119 /// A stream has sentinel 'eof' tokens at each end, e.g `int main();` becomes:
120 ///       int      main   (        )        ;
121 ///   eof kw_int   ident  l_paren  r_paren  semi   eof
122 ///       front()                           back()
123 ///       0        1      2        3        4      5
124 class TokenStream {
125 public:
126   /// Create an empty stream.
127   ///
128   /// Initially, the stream is appendable and not finalized.
129   /// The token sequence may only be accessed after finalize() is called.
130   ///
131   /// Payload is an opaque object which will be owned by the stream.
132   /// e.g. an allocator to hold backing storage for synthesized token text.
133   explicit TokenStream(std::shared_ptr<void> Payload = nullptr);
134
135   /// Append a token to the stream, which must not be finalized.
136   void push(Token T) {
137     assert(!isFinalized());
138     Storage.push_back(std::move(T));
139   }
140
141   /// Finalize the token stream, allowing tokens to be accessed.
142   /// Tokens may no longer be appended.
143   void finalize();
144   bool isFinalized() const;
145
146   /// Returns the index of T within the stream.
147   ///
148   /// T must be within the stream or the end sentinel (not the start sentinel).
149   Token::Index index(const Token &T) const {
150     assert(isFinalized());
151     assert(&T >= Storage.data() && &T < Storage.data() + Storage.size());
152     assert(&T != Storage.data() && "start sentinel");
153     return &T - Tokens.data();
154   }
155
156   ArrayRef<Token> tokens() const {
157     assert(isFinalized());
158     return Tokens;
159   }
160   ArrayRef<Token> tokens(Token::Range R) const {
161     return tokens().slice(R.Begin, R.End - R.Begin);
162   }
163
164   MutableArrayRef<Token> tokens() {
165     assert(isFinalized());
166     return Tokens;
167   }
168
169   /// May return the end sentinel if the stream is empty.
170   const Token &front() const {
171     assert(isFinalized());
172     return Storage[1];
173   }
174
175   /// Returns the shared payload.
176   std::shared_ptr<void> getPayload() const { return Payload; }
177   /// Adds the given payload to the stream.
178   void addPayload(std::shared_ptr<void> P) {
179     if (!Payload)
180       Payload = std::move(P);
181     else
182       Payload = std::make_shared<
183           std::pair<std::shared_ptr<void>, std::shared_ptr<void>>>(
184           std::move(P), std::move(Payload));
185   }
186
187   /// Print the tokens in this stream to the output stream.
188   ///
189   /// The presence of newlines/spaces is preserved, but not the quantity.
190   void print(llvm::raw_ostream &) const;
191
192 private:
193   std::shared_ptr<void> Payload;
194
195   MutableArrayRef<Token> Tokens;
196   std::vector<Token> Storage; // eof + Tokens + eof
197 };
198 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TokenStream &);
199
200 /// Extracts a raw token stream from the source code.
201 ///
202 /// All tokens will reference the data of the provided string.
203 /// "word-like" tokens such as identifiers and keywords will be raw_identifier.
204 TokenStream lex(const std::string &, const clang::LangOptions &);
205 enum class LexFlags : uint8_t {
206   /// Marks the token at the start of a logical preprocessor line.
207   /// This is a position where a directive might start.
208   ///
209   /// Here, the first # is StartsPPLine, but second is not (same logical line).
210   ///   #define X(error) \
211   ///   #error // not a directive!
212   ///
213   /// Careful, the directive may not start exactly on the StartsPPLine token:
214   ///   /*comment*/ #include <foo.h>
215   StartsPPLine = 1 << 0,
216   /// Marks tokens containing trigraphs, escaped newlines, UCNs etc.
217   /// The text() of such tokens will contain the raw trigrah.
218   NeedsCleaning = 1 << 1,
219 };
220 /// A generic lang options suitable for lexing/parsing a langage.
221 clang::LangOptions genericLangOpts(
222     clang::Language = clang::Language::CXX,
223     clang::LangStandard::Kind = clang::LangStandard::lang_unspecified);
224
225 /// Decoding raw tokens written in the source code, returning a derived stream.
226 ///
227 /// - escaped newlines within tokens are removed
228 /// - trigraphs are replaced with the characters they encode
229 /// - UCNs within raw_identifiers are replaced by the characters they encode
230 ///   (UCNs within strings, comments etc are not translated)
231 /// - raw_identifier tokens are assigned their correct keyword type
232 /// - the >> token is split into separate > > tokens
233 ///   (we use a modified grammar where >> is a nonterminal, not a token)
234 ///
235 /// The StartsPPLine flag is preserved.
236 ///
237 /// Formally the identifier correctly happens before preprocessing, while we
238 /// should only cook raw_identifiers that survive preprocessing.
239 /// However, ignoring the Token::Kind of tokens in directives achieves the same.
240 /// (And having cooked token kinds in PP-disabled sections is useful for us).
241 TokenStream cook(const TokenStream &, const clang::LangOptions &);
242
243 /// Drops comment tokens.
244 TokenStream stripComments(const TokenStream &);
245
246 } // namespace pseudo
247 } // namespace clang
248
249 #endif // CLANG_PSEUDO_TOKEN_H