1//
2// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "MacroExpander.h"
8
9#include <algorithm>
10#include <sstream>
11
12#include "DiagnosticsBase.h"
13#include "Token.h"
14
15namespace pp
16{
17
18class TokenLexer : public Lexer
19{
20 public:
21    typedef std::vector<Token> TokenVector;
22
23    TokenLexer(TokenVector *tokens)
24    {
25        tokens->swap(mTokens);
26        mIter = mTokens.begin();
27    }
28
29    virtual void lex(Token *token)
30    {
31        if (mIter == mTokens.end())
32        {
33            token->reset();
34            token->type = Token::LAST;
35        }
36        else
37        {
38            *token = *mIter++;
39        }
40    }
41
42 private:
43    PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer);
44
45    TokenVector mTokens;
46    TokenVector::const_iterator mIter;
47};
48
49MacroExpander::MacroExpander(Lexer *lexer,
50                             MacroSet *macroSet,
51                             Diagnostics *diagnostics)
52    : mLexer(lexer),
53      mMacroSet(macroSet),
54      mDiagnostics(diagnostics)
55{
56}
57
58MacroExpander::~MacroExpander()
59{
60    for (std::size_t i = 0; i < mContextStack.size(); ++i)
61    {
62        delete mContextStack[i];
63    }
64}
65
66void MacroExpander::lex(Token *token)
67{
68    while (true)
69    {
70        getToken(token);
71
72        if (token->type != Token::IDENTIFIER)
73            break;
74
75        if (token->expansionDisabled())
76            break;
77
78        MacroSet::const_iterator iter = mMacroSet->find(token->text);
79        if (iter == mMacroSet->end())
80            break;
81
82        const Macro& macro = iter->second;
83        if (macro.disabled)
84        {
85            // If a particular token is not expanded, it is never expanded.
86            token->setExpansionDisabled(true);
87            break;
88        }
89        if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
90        {
91            // If the token immediately after the macro name is not a '(',
92            // this macro should not be expanded.
93            break;
94        }
95
96        pushMacro(macro, *token);
97    }
98}
99
100void MacroExpander::getToken(Token *token)
101{
102    if (mReserveToken.get())
103    {
104        *token = *mReserveToken;
105        mReserveToken.reset();
106        return;
107    }
108
109    // First pop all empty macro contexts.
110    while (!mContextStack.empty() && mContextStack.back()->empty())
111    {
112        popMacro();
113    }
114
115    if (!mContextStack.empty())
116    {
117        *token = mContextStack.back()->get();
118    }
119    else
120    {
121        mLexer->lex(token);
122    }
123}
124
125void MacroExpander::ungetToken(const Token &token)
126{
127    if (!mContextStack.empty())
128    {
129        MacroContext *context = mContextStack.back();
130        context->unget();
131        assert(context->replacements[context->index] == token);
132    }
133    else
134    {
135        assert(!mReserveToken.get());
136        mReserveToken.reset(new Token(token));
137    }
138}
139
140bool MacroExpander::isNextTokenLeftParen()
141{
142    Token token;
143    getToken(&token);
144
145    bool lparen = token.type == '(';
146    ungetToken(token);
147
148    return lparen;
149}
150
151bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
152{
153    assert(!macro.disabled);
154    assert(!identifier.expansionDisabled());
155    assert(identifier.type == Token::IDENTIFIER);
156    assert(identifier.text == macro.name);
157
158    std::vector<Token> replacements;
159    if (!expandMacro(macro, identifier, &replacements))
160        return false;
161
162    // Macro is disabled for expansion until it is popped off the stack.
163    macro.disabled = true;
164
165    MacroContext *context = new MacroContext;
166    context->macro = &macro;
167    context->replacements.swap(replacements);
168    mContextStack.push_back(context);
169    return true;
170}
171
172void MacroExpander::popMacro()
173{
174    assert(!mContextStack.empty());
175
176    MacroContext *context = mContextStack.back();
177    mContextStack.pop_back();
178
179    assert(context->empty());
180    assert(context->macro->disabled);
181    context->macro->disabled = false;
182    delete context;
183}
184
185bool MacroExpander::expandMacro(const Macro &macro,
186                                const Token &identifier,
187                                std::vector<Token> *replacements)
188{
189    replacements->clear();
190    if (macro.type == Macro::kTypeObj)
191    {
192        replacements->assign(macro.replacements.begin(),
193                             macro.replacements.end());
194
195        if (macro.predefined)
196        {
197            static const std::string kLine = "__LINE__";
198            static const std::string kFile = "__FILE__";
199
200            assert(replacements->size() == 1);
201            Token& repl = replacements->front();
202            if (macro.name == kLine)
203            {
204                std::ostringstream stream;
205                stream << identifier.location.line;
206                repl.text = stream.str();
207            }
208            else if (macro.name == kFile)
209            {
210                std::ostringstream stream;
211                stream << identifier.location.file;
212                repl.text = stream.str();
213            }
214        }
215    }
216    else
217    {
218        assert(macro.type == Macro::kTypeFunc);
219        std::vector<MacroArg> args;
220        args.reserve(macro.parameters.size());
221        if (!collectMacroArgs(macro, identifier, &args))
222            return false;
223
224        replaceMacroParams(macro, args, replacements);
225    }
226
227    for (std::size_t i = 0; i < replacements->size(); ++i)
228    {
229        Token& repl = replacements->at(i);
230        if (i == 0)
231        {
232            // The first token in the replacement list inherits the padding
233            // properties of the identifier token.
234            repl.setAtStartOfLine(identifier.atStartOfLine());
235            repl.setHasLeadingSpace(identifier.hasLeadingSpace());
236        }
237        repl.location = identifier.location;
238    }
239    return true;
240}
241
242bool MacroExpander::collectMacroArgs(const Macro &macro,
243                                     const Token &identifier,
244                                     std::vector<MacroArg> *args)
245{
246    Token token;
247    getToken(&token);
248    assert(token.type == '(');
249
250    args->push_back(MacroArg());
251    for (int openParens = 1; openParens != 0; )
252    {
253        getToken(&token);
254
255        if (token.type == Token::LAST)
256        {
257            mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
258                                 identifier.location, identifier.text);
259            // Do not lose EOF token.
260            ungetToken(token);
261            return false;
262        }
263
264        bool isArg = false; // True if token is part of the current argument.
265        switch (token.type)
266        {
267          case '(':
268            ++openParens;
269            isArg = true;
270            break;
271          case ')':
272            --openParens;
273            isArg = openParens != 0;
274            break;
275          case ',':
276            // The individual arguments are separated by comma tokens, but
277            // the comma tokens between matching inner parentheses do not
278            // seperate arguments.
279            if (openParens == 1)
280                args->push_back(MacroArg());
281            isArg = openParens != 1;
282            break;
283          default:
284            isArg = true;
285            break;
286        }
287        if (isArg)
288        {
289            MacroArg &arg = args->back();
290            // Initial whitespace is not part of the argument.
291            if (arg.empty())
292                token.setHasLeadingSpace(false);
293            arg.push_back(token);
294        }
295    }
296
297    const Macro::Parameters &params = macro.parameters;
298    // If there is only one empty argument, it is equivalent to no argument.
299    if (params.empty() && (args->size() == 1) && args->front().empty())
300    {
301        args->clear();
302    }
303    // Validate the number of arguments.
304    if (args->size() != params.size())
305    {
306        Diagnostics::ID id = args->size() < macro.parameters.size() ?
307            Diagnostics::PP_MACRO_TOO_FEW_ARGS :
308            Diagnostics::PP_MACRO_TOO_MANY_ARGS;
309        mDiagnostics->report(id, identifier.location, identifier.text);
310        return false;
311    }
312
313    // Pre-expand each argument before substitution.
314    // This step expands each argument individually before they are
315    // inserted into the macro body.
316    for (std::size_t i = 0; i < args->size(); ++i)
317    {
318        MacroArg &arg = args->at(i);
319        TokenLexer lexer(&arg);
320        MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
321
322        arg.clear();
323        expander.lex(&token);
324        while (token.type != Token::LAST)
325        {
326            arg.push_back(token);
327            expander.lex(&token);
328        }
329    }
330    return true;
331}
332
333void MacroExpander::replaceMacroParams(const Macro &macro,
334                                       const std::vector<MacroArg> &args,
335                                       std::vector<Token> *replacements)
336{
337    for (std::size_t i = 0; i < macro.replacements.size(); ++i)
338    {
339        const Token &repl = macro.replacements[i];
340        if (repl.type != Token::IDENTIFIER)
341        {
342            replacements->push_back(repl);
343            continue;
344        }
345
346        // TODO(alokp): Optimize this.
347        // There is no need to search for macro params every time.
348        // The param index can be cached with the replacement token.
349        Macro::Parameters::const_iterator iter = std::find(
350            macro.parameters.begin(), macro.parameters.end(), repl.text);
351        if (iter == macro.parameters.end())
352        {
353            replacements->push_back(repl);
354            continue;
355        }
356
357        std::size_t iArg = std::distance(macro.parameters.begin(), iter);
358        const MacroArg &arg = args[iArg];
359        if (arg.empty())
360        {
361            continue;
362        }
363        std::size_t iRepl = replacements->size();
364        replacements->insert(replacements->end(), arg.begin(), arg.end());
365        // The replacement token inherits padding properties from
366        // macro replacement token.
367        replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
368    }
369}
370
371}  // namespace pp
372
373