annotate clang/lib/Lex/MacroArgs.cpp @ 150:1d019706d866

LLVM10
author anatofuz
date Thu, 13 Feb 2020 15:10:13 +0900
parents
children 1f2b6ac9f198
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===--- MacroArgs.cpp - Formal argument info for Macros ------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8 //
anatofuz
parents:
diff changeset
9 // This file implements the MacroArgs interface.
anatofuz
parents:
diff changeset
10 //
anatofuz
parents:
diff changeset
11 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
12
anatofuz
parents:
diff changeset
13 #include "clang/Lex/MacroArgs.h"
anatofuz
parents:
diff changeset
14 #include "clang/Lex/LexDiagnostic.h"
anatofuz
parents:
diff changeset
15 #include "clang/Lex/MacroInfo.h"
anatofuz
parents:
diff changeset
16 #include "clang/Lex/Preprocessor.h"
anatofuz
parents:
diff changeset
17 #include "llvm/ADT/SmallString.h"
anatofuz
parents:
diff changeset
18 #include "llvm/Support/SaveAndRestore.h"
anatofuz
parents:
diff changeset
19 #include <algorithm>
anatofuz
parents:
diff changeset
20
anatofuz
parents:
diff changeset
21 using namespace clang;
anatofuz
parents:
diff changeset
22
anatofuz
parents:
diff changeset
23 /// MacroArgs ctor function - This destroys the vector passed in.
anatofuz
parents:
diff changeset
24 MacroArgs *MacroArgs::create(const MacroInfo *MI,
anatofuz
parents:
diff changeset
25 ArrayRef<Token> UnexpArgTokens,
anatofuz
parents:
diff changeset
26 bool VarargsElided, Preprocessor &PP) {
anatofuz
parents:
diff changeset
27 assert(MI->isFunctionLike() &&
anatofuz
parents:
diff changeset
28 "Can't have args for an object-like macro!");
anatofuz
parents:
diff changeset
29 MacroArgs **ResultEnt = nullptr;
anatofuz
parents:
diff changeset
30 unsigned ClosestMatch = ~0U;
anatofuz
parents:
diff changeset
31
anatofuz
parents:
diff changeset
32 // See if we have an entry with a big enough argument list to reuse on the
anatofuz
parents:
diff changeset
33 // free list. If so, reuse it.
anatofuz
parents:
diff changeset
34 for (MacroArgs **Entry = &PP.MacroArgCache; *Entry;
anatofuz
parents:
diff changeset
35 Entry = &(*Entry)->ArgCache) {
anatofuz
parents:
diff changeset
36 if ((*Entry)->NumUnexpArgTokens >= UnexpArgTokens.size() &&
anatofuz
parents:
diff changeset
37 (*Entry)->NumUnexpArgTokens < ClosestMatch) {
anatofuz
parents:
diff changeset
38 ResultEnt = Entry;
anatofuz
parents:
diff changeset
39
anatofuz
parents:
diff changeset
40 // If we have an exact match, use it.
anatofuz
parents:
diff changeset
41 if ((*Entry)->NumUnexpArgTokens == UnexpArgTokens.size())
anatofuz
parents:
diff changeset
42 break;
anatofuz
parents:
diff changeset
43 // Otherwise, use the best fit.
anatofuz
parents:
diff changeset
44 ClosestMatch = (*Entry)->NumUnexpArgTokens;
anatofuz
parents:
diff changeset
45 }
anatofuz
parents:
diff changeset
46 }
anatofuz
parents:
diff changeset
47 MacroArgs *Result;
anatofuz
parents:
diff changeset
48 if (!ResultEnt) {
anatofuz
parents:
diff changeset
49 // Allocate memory for a MacroArgs object with the lexer tokens at the end,
anatofuz
parents:
diff changeset
50 // and construct the MacroArgs object.
anatofuz
parents:
diff changeset
51 Result = new (
anatofuz
parents:
diff changeset
52 llvm::safe_malloc(totalSizeToAlloc<Token>(UnexpArgTokens.size())))
anatofuz
parents:
diff changeset
53 MacroArgs(UnexpArgTokens.size(), VarargsElided, MI->getNumParams());
anatofuz
parents:
diff changeset
54 } else {
anatofuz
parents:
diff changeset
55 Result = *ResultEnt;
anatofuz
parents:
diff changeset
56 // Unlink this node from the preprocessors singly linked list.
anatofuz
parents:
diff changeset
57 *ResultEnt = Result->ArgCache;
anatofuz
parents:
diff changeset
58 Result->NumUnexpArgTokens = UnexpArgTokens.size();
anatofuz
parents:
diff changeset
59 Result->VarargsElided = VarargsElided;
anatofuz
parents:
diff changeset
60 Result->NumMacroArgs = MI->getNumParams();
anatofuz
parents:
diff changeset
61 }
anatofuz
parents:
diff changeset
62
anatofuz
parents:
diff changeset
63 // Copy the actual unexpanded tokens to immediately after the result ptr.
anatofuz
parents:
diff changeset
64 if (!UnexpArgTokens.empty()) {
anatofuz
parents:
diff changeset
65 static_assert(std::is_trivial<Token>::value,
anatofuz
parents:
diff changeset
66 "assume trivial copyability if copying into the "
anatofuz
parents:
diff changeset
67 "uninitialized array (as opposed to reusing a cached "
anatofuz
parents:
diff changeset
68 "MacroArgs)");
anatofuz
parents:
diff changeset
69 std::copy(UnexpArgTokens.begin(), UnexpArgTokens.end(),
anatofuz
parents:
diff changeset
70 Result->getTrailingObjects<Token>());
anatofuz
parents:
diff changeset
71 }
anatofuz
parents:
diff changeset
72
anatofuz
parents:
diff changeset
73 return Result;
anatofuz
parents:
diff changeset
74 }
anatofuz
parents:
diff changeset
75
anatofuz
parents:
diff changeset
76 /// destroy - Destroy and deallocate the memory for this object.
anatofuz
parents:
diff changeset
77 ///
anatofuz
parents:
diff changeset
78 void MacroArgs::destroy(Preprocessor &PP) {
anatofuz
parents:
diff changeset
79 // Don't clear PreExpArgTokens, just clear the entries. Clearing the entries
anatofuz
parents:
diff changeset
80 // would deallocate the element vectors.
anatofuz
parents:
diff changeset
81 for (unsigned i = 0, e = PreExpArgTokens.size(); i != e; ++i)
anatofuz
parents:
diff changeset
82 PreExpArgTokens[i].clear();
anatofuz
parents:
diff changeset
83
anatofuz
parents:
diff changeset
84 // Add this to the preprocessor's free list.
anatofuz
parents:
diff changeset
85 ArgCache = PP.MacroArgCache;
anatofuz
parents:
diff changeset
86 PP.MacroArgCache = this;
anatofuz
parents:
diff changeset
87 }
anatofuz
parents:
diff changeset
88
anatofuz
parents:
diff changeset
89 /// deallocate - This should only be called by the Preprocessor when managing
anatofuz
parents:
diff changeset
90 /// its freelist.
anatofuz
parents:
diff changeset
91 MacroArgs *MacroArgs::deallocate() {
anatofuz
parents:
diff changeset
92 MacroArgs *Next = ArgCache;
anatofuz
parents:
diff changeset
93
anatofuz
parents:
diff changeset
94 // Run the dtor to deallocate the vectors.
anatofuz
parents:
diff changeset
95 this->~MacroArgs();
anatofuz
parents:
diff changeset
96 // Release the memory for the object.
anatofuz
parents:
diff changeset
97 static_assert(std::is_trivially_destructible<Token>::value,
anatofuz
parents:
diff changeset
98 "assume trivially destructible and forego destructors");
anatofuz
parents:
diff changeset
99 free(this);
anatofuz
parents:
diff changeset
100
anatofuz
parents:
diff changeset
101 return Next;
anatofuz
parents:
diff changeset
102 }
anatofuz
parents:
diff changeset
103
anatofuz
parents:
diff changeset
104
anatofuz
parents:
diff changeset
105 /// getArgLength - Given a pointer to an expanded or unexpanded argument,
anatofuz
parents:
diff changeset
106 /// return the number of tokens, not counting the EOF, that make up the
anatofuz
parents:
diff changeset
107 /// argument.
anatofuz
parents:
diff changeset
108 unsigned MacroArgs::getArgLength(const Token *ArgPtr) {
anatofuz
parents:
diff changeset
109 unsigned NumArgTokens = 0;
anatofuz
parents:
diff changeset
110 for (; ArgPtr->isNot(tok::eof); ++ArgPtr)
anatofuz
parents:
diff changeset
111 ++NumArgTokens;
anatofuz
parents:
diff changeset
112 return NumArgTokens;
anatofuz
parents:
diff changeset
113 }
anatofuz
parents:
diff changeset
114
anatofuz
parents:
diff changeset
115
anatofuz
parents:
diff changeset
116 /// getUnexpArgument - Return the unexpanded tokens for the specified formal.
anatofuz
parents:
diff changeset
117 ///
anatofuz
parents:
diff changeset
118 const Token *MacroArgs::getUnexpArgument(unsigned Arg) const {
anatofuz
parents:
diff changeset
119
anatofuz
parents:
diff changeset
120 assert(Arg < getNumMacroArguments() && "Invalid arg #");
anatofuz
parents:
diff changeset
121 // The unexpanded argument tokens start immediately after the MacroArgs object
anatofuz
parents:
diff changeset
122 // in memory.
anatofuz
parents:
diff changeset
123 const Token *Start = getTrailingObjects<Token>();
anatofuz
parents:
diff changeset
124 const Token *Result = Start;
anatofuz
parents:
diff changeset
125
anatofuz
parents:
diff changeset
126 // Scan to find Arg.
anatofuz
parents:
diff changeset
127 for (; Arg; ++Result) {
anatofuz
parents:
diff changeset
128 assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
anatofuz
parents:
diff changeset
129 if (Result->is(tok::eof))
anatofuz
parents:
diff changeset
130 --Arg;
anatofuz
parents:
diff changeset
131 }
anatofuz
parents:
diff changeset
132 assert(Result < Start+NumUnexpArgTokens && "Invalid arg #");
anatofuz
parents:
diff changeset
133 return Result;
anatofuz
parents:
diff changeset
134 }
anatofuz
parents:
diff changeset
135
anatofuz
parents:
diff changeset
136 bool MacroArgs::invokedWithVariadicArgument(const MacroInfo *const MI,
anatofuz
parents:
diff changeset
137 Preprocessor &PP) {
anatofuz
parents:
diff changeset
138 if (!MI->isVariadic())
anatofuz
parents:
diff changeset
139 return false;
anatofuz
parents:
diff changeset
140 const int VariadicArgIndex = getNumMacroArguments() - 1;
anatofuz
parents:
diff changeset
141 return getPreExpArgument(VariadicArgIndex, PP).front().isNot(tok::eof);
anatofuz
parents:
diff changeset
142 }
anatofuz
parents:
diff changeset
143
anatofuz
parents:
diff changeset
144 /// ArgNeedsPreexpansion - If we can prove that the argument won't be affected
anatofuz
parents:
diff changeset
145 /// by pre-expansion, return false. Otherwise, conservatively return true.
anatofuz
parents:
diff changeset
146 bool MacroArgs::ArgNeedsPreexpansion(const Token *ArgTok,
anatofuz
parents:
diff changeset
147 Preprocessor &PP) const {
anatofuz
parents:
diff changeset
148 // If there are no identifiers in the argument list, or if the identifiers are
anatofuz
parents:
diff changeset
149 // known to not be macros, pre-expansion won't modify it.
anatofuz
parents:
diff changeset
150 for (; ArgTok->isNot(tok::eof); ++ArgTok)
anatofuz
parents:
diff changeset
151 if (IdentifierInfo *II = ArgTok->getIdentifierInfo())
anatofuz
parents:
diff changeset
152 if (II->hasMacroDefinition())
anatofuz
parents:
diff changeset
153 // Return true even though the macro could be a function-like macro
anatofuz
parents:
diff changeset
154 // without a following '(' token, or could be disabled, or not visible.
anatofuz
parents:
diff changeset
155 return true;
anatofuz
parents:
diff changeset
156 return false;
anatofuz
parents:
diff changeset
157 }
anatofuz
parents:
diff changeset
158
anatofuz
parents:
diff changeset
159 /// getPreExpArgument - Return the pre-expanded form of the specified
anatofuz
parents:
diff changeset
160 /// argument.
anatofuz
parents:
diff changeset
161 const std::vector<Token> &MacroArgs::getPreExpArgument(unsigned Arg,
anatofuz
parents:
diff changeset
162 Preprocessor &PP) {
anatofuz
parents:
diff changeset
163 assert(Arg < getNumMacroArguments() && "Invalid argument number!");
anatofuz
parents:
diff changeset
164
anatofuz
parents:
diff changeset
165 // If we have already computed this, return it.
anatofuz
parents:
diff changeset
166 if (PreExpArgTokens.size() < getNumMacroArguments())
anatofuz
parents:
diff changeset
167 PreExpArgTokens.resize(getNumMacroArguments());
anatofuz
parents:
diff changeset
168
anatofuz
parents:
diff changeset
169 std::vector<Token> &Result = PreExpArgTokens[Arg];
anatofuz
parents:
diff changeset
170 if (!Result.empty()) return Result;
anatofuz
parents:
diff changeset
171
anatofuz
parents:
diff changeset
172 SaveAndRestore<bool> PreExpandingMacroArgs(PP.InMacroArgPreExpansion, true);
anatofuz
parents:
diff changeset
173
anatofuz
parents:
diff changeset
174 const Token *AT = getUnexpArgument(Arg);
anatofuz
parents:
diff changeset
175 unsigned NumToks = getArgLength(AT)+1; // Include the EOF.
anatofuz
parents:
diff changeset
176
anatofuz
parents:
diff changeset
177 // Otherwise, we have to pre-expand this argument, populating Result. To do
anatofuz
parents:
diff changeset
178 // this, we set up a fake TokenLexer to lex from the unexpanded argument
anatofuz
parents:
diff changeset
179 // list. With this installed, we lex expanded tokens until we hit the EOF
anatofuz
parents:
diff changeset
180 // token at the end of the unexp list.
anatofuz
parents:
diff changeset
181 PP.EnterTokenStream(AT, NumToks, false /*disable expand*/,
anatofuz
parents:
diff changeset
182 false /*owns tokens*/, false /*is reinject*/);
anatofuz
parents:
diff changeset
183
anatofuz
parents:
diff changeset
184 // Lex all of the macro-expanded tokens into Result.
anatofuz
parents:
diff changeset
185 do {
anatofuz
parents:
diff changeset
186 Result.push_back(Token());
anatofuz
parents:
diff changeset
187 Token &Tok = Result.back();
anatofuz
parents:
diff changeset
188 PP.Lex(Tok);
anatofuz
parents:
diff changeset
189 } while (Result.back().isNot(tok::eof));
anatofuz
parents:
diff changeset
190
anatofuz
parents:
diff changeset
191 // Pop the token stream off the top of the stack. We know that the internal
anatofuz
parents:
diff changeset
192 // pointer inside of it is to the "end" of the token stream, but the stack
anatofuz
parents:
diff changeset
193 // will not otherwise be popped until the next token is lexed. The problem is
anatofuz
parents:
diff changeset
194 // that the token may be lexed sometime after the vector of tokens itself is
anatofuz
parents:
diff changeset
195 // destroyed, which would be badness.
anatofuz
parents:
diff changeset
196 if (PP.InCachingLexMode())
anatofuz
parents:
diff changeset
197 PP.ExitCachingLexMode();
anatofuz
parents:
diff changeset
198 PP.RemoveTopOfLexerStack();
anatofuz
parents:
diff changeset
199 return Result;
anatofuz
parents:
diff changeset
200 }
anatofuz
parents:
diff changeset
201
anatofuz
parents:
diff changeset
202
anatofuz
parents:
diff changeset
203 /// StringifyArgument - Implement C99 6.10.3.2p2, converting a sequence of
anatofuz
parents:
diff changeset
204 /// tokens into the literal string token that should be produced by the C #
anatofuz
parents:
diff changeset
205 /// preprocessor operator. If Charify is true, then it should be turned into
anatofuz
parents:
diff changeset
206 /// a character literal for the Microsoft charize (#@) extension.
anatofuz
parents:
diff changeset
207 ///
anatofuz
parents:
diff changeset
208 Token MacroArgs::StringifyArgument(const Token *ArgToks,
anatofuz
parents:
diff changeset
209 Preprocessor &PP, bool Charify,
anatofuz
parents:
diff changeset
210 SourceLocation ExpansionLocStart,
anatofuz
parents:
diff changeset
211 SourceLocation ExpansionLocEnd) {
anatofuz
parents:
diff changeset
212 Token Tok;
anatofuz
parents:
diff changeset
213 Tok.startToken();
anatofuz
parents:
diff changeset
214 Tok.setKind(Charify ? tok::char_constant : tok::string_literal);
anatofuz
parents:
diff changeset
215
anatofuz
parents:
diff changeset
216 const Token *ArgTokStart = ArgToks;
anatofuz
parents:
diff changeset
217
anatofuz
parents:
diff changeset
218 // Stringify all the tokens.
anatofuz
parents:
diff changeset
219 SmallString<128> Result;
anatofuz
parents:
diff changeset
220 Result += "\"";
anatofuz
parents:
diff changeset
221
anatofuz
parents:
diff changeset
222 bool isFirst = true;
anatofuz
parents:
diff changeset
223 for (; ArgToks->isNot(tok::eof); ++ArgToks) {
anatofuz
parents:
diff changeset
224 const Token &Tok = *ArgToks;
anatofuz
parents:
diff changeset
225 if (!isFirst && (Tok.hasLeadingSpace() || Tok.isAtStartOfLine()))
anatofuz
parents:
diff changeset
226 Result += ' ';
anatofuz
parents:
diff changeset
227 isFirst = false;
anatofuz
parents:
diff changeset
228
anatofuz
parents:
diff changeset
229 // If this is a string or character constant, escape the token as specified
anatofuz
parents:
diff changeset
230 // by 6.10.3.2p2.
anatofuz
parents:
diff changeset
231 if (tok::isStringLiteral(Tok.getKind()) || // "foo", u8R"x(foo)x"_bar, etc.
anatofuz
parents:
diff changeset
232 Tok.is(tok::char_constant) || // 'x'
anatofuz
parents:
diff changeset
233 Tok.is(tok::wide_char_constant) || // L'x'.
anatofuz
parents:
diff changeset
234 Tok.is(tok::utf8_char_constant) || // u8'x'.
anatofuz
parents:
diff changeset
235 Tok.is(tok::utf16_char_constant) || // u'x'.
anatofuz
parents:
diff changeset
236 Tok.is(tok::utf32_char_constant)) { // U'x'.
anatofuz
parents:
diff changeset
237 bool Invalid = false;
anatofuz
parents:
diff changeset
238 std::string TokStr = PP.getSpelling(Tok, &Invalid);
anatofuz
parents:
diff changeset
239 if (!Invalid) {
anatofuz
parents:
diff changeset
240 std::string Str = Lexer::Stringify(TokStr);
anatofuz
parents:
diff changeset
241 Result.append(Str.begin(), Str.end());
anatofuz
parents:
diff changeset
242 }
anatofuz
parents:
diff changeset
243 } else if (Tok.is(tok::code_completion)) {
anatofuz
parents:
diff changeset
244 PP.CodeCompleteNaturalLanguage();
anatofuz
parents:
diff changeset
245 } else {
anatofuz
parents:
diff changeset
246 // Otherwise, just append the token. Do some gymnastics to get the token
anatofuz
parents:
diff changeset
247 // in place and avoid copies where possible.
anatofuz
parents:
diff changeset
248 unsigned CurStrLen = Result.size();
anatofuz
parents:
diff changeset
249 Result.resize(CurStrLen+Tok.getLength());
anatofuz
parents:
diff changeset
250 const char *BufPtr = Result.data() + CurStrLen;
anatofuz
parents:
diff changeset
251 bool Invalid = false;
anatofuz
parents:
diff changeset
252 unsigned ActualTokLen = PP.getSpelling(Tok, BufPtr, &Invalid);
anatofuz
parents:
diff changeset
253
anatofuz
parents:
diff changeset
254 if (!Invalid) {
anatofuz
parents:
diff changeset
255 // If getSpelling returned a pointer to an already uniqued version of
anatofuz
parents:
diff changeset
256 // the string instead of filling in BufPtr, memcpy it onto our string.
anatofuz
parents:
diff changeset
257 if (ActualTokLen && BufPtr != &Result[CurStrLen])
anatofuz
parents:
diff changeset
258 memcpy(&Result[CurStrLen], BufPtr, ActualTokLen);
anatofuz
parents:
diff changeset
259
anatofuz
parents:
diff changeset
260 // If the token was dirty, the spelling may be shorter than the token.
anatofuz
parents:
diff changeset
261 if (ActualTokLen != Tok.getLength())
anatofuz
parents:
diff changeset
262 Result.resize(CurStrLen+ActualTokLen);
anatofuz
parents:
diff changeset
263 }
anatofuz
parents:
diff changeset
264 }
anatofuz
parents:
diff changeset
265 }
anatofuz
parents:
diff changeset
266
anatofuz
parents:
diff changeset
267 // If the last character of the string is a \, and if it isn't escaped, this
anatofuz
parents:
diff changeset
268 // is an invalid string literal, diagnose it as specified in C99.
anatofuz
parents:
diff changeset
269 if (Result.back() == '\\') {
anatofuz
parents:
diff changeset
270 // Count the number of consecutive \ characters. If even, then they are
anatofuz
parents:
diff changeset
271 // just escaped backslashes, otherwise it's an error.
anatofuz
parents:
diff changeset
272 unsigned FirstNonSlash = Result.size()-2;
anatofuz
parents:
diff changeset
273 // Guaranteed to find the starting " if nothing else.
anatofuz
parents:
diff changeset
274 while (Result[FirstNonSlash] == '\\')
anatofuz
parents:
diff changeset
275 --FirstNonSlash;
anatofuz
parents:
diff changeset
276 if ((Result.size()-1-FirstNonSlash) & 1) {
anatofuz
parents:
diff changeset
277 // Diagnose errors for things like: #define F(X) #X / F(\)
anatofuz
parents:
diff changeset
278 PP.Diag(ArgToks[-1], diag::pp_invalid_string_literal);
anatofuz
parents:
diff changeset
279 Result.pop_back(); // remove one of the \'s.
anatofuz
parents:
diff changeset
280 }
anatofuz
parents:
diff changeset
281 }
anatofuz
parents:
diff changeset
282 Result += '"';
anatofuz
parents:
diff changeset
283
anatofuz
parents:
diff changeset
284 // If this is the charify operation and the result is not a legal character
anatofuz
parents:
diff changeset
285 // constant, diagnose it.
anatofuz
parents:
diff changeset
286 if (Charify) {
anatofuz
parents:
diff changeset
287 // First step, turn double quotes into single quotes:
anatofuz
parents:
diff changeset
288 Result[0] = '\'';
anatofuz
parents:
diff changeset
289 Result[Result.size()-1] = '\'';
anatofuz
parents:
diff changeset
290
anatofuz
parents:
diff changeset
291 // Check for bogus character.
anatofuz
parents:
diff changeset
292 bool isBad = false;
anatofuz
parents:
diff changeset
293 if (Result.size() == 3)
anatofuz
parents:
diff changeset
294 isBad = Result[1] == '\''; // ''' is not legal. '\' already fixed above.
anatofuz
parents:
diff changeset
295 else
anatofuz
parents:
diff changeset
296 isBad = (Result.size() != 4 || Result[1] != '\\'); // Not '\x'
anatofuz
parents:
diff changeset
297
anatofuz
parents:
diff changeset
298 if (isBad) {
anatofuz
parents:
diff changeset
299 PP.Diag(ArgTokStart[0], diag::err_invalid_character_to_charify);
anatofuz
parents:
diff changeset
300 Result = "' '"; // Use something arbitrary, but legal.
anatofuz
parents:
diff changeset
301 }
anatofuz
parents:
diff changeset
302 }
anatofuz
parents:
diff changeset
303
anatofuz
parents:
diff changeset
304 PP.CreateString(Result, Tok,
anatofuz
parents:
diff changeset
305 ExpansionLocStart, ExpansionLocEnd);
anatofuz
parents:
diff changeset
306 return Tok;
anatofuz
parents:
diff changeset
307 }