104d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org//
204d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
304d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org// Use of this source code is governed by a BSD-style license that can be
404d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org// found in the LICENSE file.
504d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org//
604d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
704d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org#include "MacroExpander.h"
804d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
97fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org#include <algorithm>
10f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org#include <sstream>
117fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
12b3077d086b365b1d54c3d755df19c6f8f349a562daniel@transgaming.com#include "DiagnosticsBase.h"
137fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org#include "Token.h"
147fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1504d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.orgnamespace pp
1604d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org{
1704d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
187fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.orgclass TokenLexer : public Lexer
197fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
207fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org public:
217fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    typedef std::vector<Token> TokenVector;
227fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
23d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    TokenLexer(TokenVector *tokens)
247fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
257fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        tokens->swap(mTokens);
267fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        mIter = mTokens.begin();
277fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
287fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
29d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    virtual void lex(Token *token)
307fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
317fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (mIter == mTokens.end())
327fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
337fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            token->reset();
347fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            token->type = Token::LAST;
357fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
367fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        else
377fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
387fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            *token = *mIter++;
397fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
407fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
417fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
427fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org private:
437fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer);
447fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
457fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    TokenVector mTokens;
467fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    TokenVector::const_iterator mIter;
477fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org};
487fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
49d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao MoMacroExpander::MacroExpander(Lexer *lexer,
50d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                             MacroSet *macroSet,
51d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                             Diagnostics *diagnostics)
52d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    : mLexer(lexer),
53d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo      mMacroSet(macroSet),
54d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo      mDiagnostics(diagnostics)
5504d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org{
5604d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org}
5704d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
587fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.orgMacroExpander::~MacroExpander()
597fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
60a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com    for (std::size_t i = 0; i < mContextStack.size(); ++i)
616c0c2d87aaa51ec9242284f6d1de9e79a9db771calokp@chromium.org    {
626c0c2d87aaa51ec9242284f6d1de9e79a9db771calokp@chromium.org        delete mContextStack[i];
636c0c2d87aaa51ec9242284f6d1de9e79a9db771calokp@chromium.org    }
647fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
657fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
66d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Movoid MacroExpander::lex(Token *token)
6704d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org{
687fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    while (true)
697fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
707fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        getToken(token);
717fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
727fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (token->type != Token::IDENTIFIER)
737fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
747fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
757fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (token->expansionDisabled())
767fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
777fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
785b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org        MacroSet::const_iterator iter = mMacroSet->find(token->text);
797fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (iter == mMacroSet->end())
807fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
817fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
827fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        const Macro& macro = iter->second;
837fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (macro.disabled)
847fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
857fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // If a particular token is not expanded, it is never expanded.
867fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            token->setExpansionDisabled(true);
877fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
887fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
897fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
907fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
917fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // If the token immediately after the macro name is not a '(',
927fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // this macro should not be expanded.
937fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
947fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
957fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
967fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        pushMacro(macro, *token);
977fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
987fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
997fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
100d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Movoid MacroExpander::getToken(Token *token)
1017fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
1027fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (mReserveToken.get())
1037fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1047fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        *token = *mReserveToken;
1057fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        mReserveToken.reset();
1067fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        return;
1077fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1087fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1097fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // First pop all empty macro contexts.
1107fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    while (!mContextStack.empty() && mContextStack.back()->empty())
1117fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1127fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        popMacro();
1137fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1147fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1157fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (!mContextStack.empty())
1167fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1177fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        *token = mContextStack.back()->get();
1187fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1197fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    else
1207fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1217fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        mLexer->lex(token);
1227fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1237fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
1247fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
125d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Movoid MacroExpander::ungetToken(const Token &token)
1267fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
1277fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (!mContextStack.empty())
1287fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
129d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo        MacroContext *context = mContextStack.back();
1307fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        context->unget();
1317fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        assert(context->replacements[context->index] == token);
1327fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1337fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    else
1347fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1357fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        assert(!mReserveToken.get());
1367fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        mReserveToken.reset(new Token(token));
1377fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
1387fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
1397fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1407fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.orgbool MacroExpander::isNextTokenLeftParen()
1417fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
1427fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    Token token;
1437fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    getToken(&token);
1447fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1457fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    bool lparen = token.type == '(';
1467fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    ungetToken(token);
1477fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1487fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    return lparen;
1497fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
1507fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
151d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mobool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
1527fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
1537fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(!macro.disabled);
1547fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(!identifier.expansionDisabled());
1557fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(identifier.type == Token::IDENTIFIER);
1565b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org    assert(identifier.text == macro.name);
1577fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1587fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    std::vector<Token> replacements;
1597fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (!expandMacro(macro, identifier, &replacements))
1607fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        return false;
1617fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1627fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // Macro is disabled for expansion until it is popped off the stack.
1637fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    macro.disabled = true;
1647fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
165d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    MacroContext *context = new MacroContext;
1667fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    context->macro = &macro;
1677fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    context->replacements.swap(replacements);
1687fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    mContextStack.push_back(context);
1697fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    return true;
1707fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
1717fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1727fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.orgvoid MacroExpander::popMacro()
1737fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
1747fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(!mContextStack.empty());
1757fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
176d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    MacroContext *context = mContextStack.back();
1777fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    mContextStack.pop_back();
1787fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
1797fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(context->empty());
1807fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(context->macro->disabled);
1817fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    context->macro->disabled = false;
1827fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    delete context;
1837fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
1847fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
185d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mobool MacroExpander::expandMacro(const Macro &macro,
186d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                const Token &identifier,
187d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                std::vector<Token> *replacements)
1887fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
189f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org    replacements->clear();
1907fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (macro.type == Macro::kTypeObj)
1917fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
1927fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        replacements->assign(macro.replacements.begin(),
1937fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org                             macro.replacements.end());
194f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org
195f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org        if (macro.predefined)
196f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org        {
197f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            static const std::string kLine = "__LINE__";
198f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            static const std::string kFile = "__FILE__";
199f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org
200f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            assert(replacements->size() == 1);
201f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            Token& repl = replacements->front();
202f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            if (macro.name == kLine)
203f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            {
204f115592d96dc61ffca5cc23cd13a2ccd1743017falokp@chromium.org                std::ostringstream stream;
205f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org                stream << identifier.location.line;
2065b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org                repl.text = stream.str();
207f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            }
208f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            else if (macro.name == kFile)
209f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            {
210f115592d96dc61ffca5cc23cd13a2ccd1743017falokp@chromium.org                std::ostringstream stream;
211f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org                stream << identifier.location.file;
2125b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org                repl.text = stream.str();
213f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org            }
214f3cdb460d1dde723815eecd006274a3da5d81099alokp@chromium.org        }
2157fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
2167fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    else
2177fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
2187fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        assert(macro.type == Macro::kTypeFunc);
2197fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        std::vector<MacroArg> args;
2207fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        args.reserve(macro.parameters.size());
2217fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (!collectMacroArgs(macro, identifier, &args))
2227fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            return false;
2237fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
2247fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        replaceMacroParams(macro, args, replacements);
2257fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
2267fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
227a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com    for (std::size_t i = 0; i < replacements->size(); ++i)
2287fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
2297fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        Token& repl = replacements->at(i);
2307fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (i == 0)
2317fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
2327fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // The first token in the replacement list inherits the padding
2337fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // properties of the identifier token.
2347fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            repl.setAtStartOfLine(identifier.atStartOfLine());
2357fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            repl.setHasLeadingSpace(identifier.hasLeadingSpace());
2367fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
2377fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        repl.location = identifier.location;
2387fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
2397fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    return true;
2407fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
2417fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
242d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mobool MacroExpander::collectMacroArgs(const Macro &macro,
243d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                     const Token &identifier,
244d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                     std::vector<MacroArg> *args)
2457fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
2467fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    Token token;
2477fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    getToken(&token);
2487fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    assert(token.type == '(');
2497fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
2507fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    args->push_back(MacroArg());
2517fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    for (int openParens = 1; openParens != 0; )
2527fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
2537fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        getToken(&token);
2547fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
2557fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (token.type == Token::LAST)
2567fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
2577f2d7945ee702241829727751bd4c7424662b225Shannon Woods            mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
2585b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org                                 identifier.location, identifier.text);
2597fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // Do not lose EOF token.
2607fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            ungetToken(token);
2617fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            return false;
2627fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
2637fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
2647fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        bool isArg = false; // True if token is part of the current argument.
2657fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        switch (token.type)
2667fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
2677fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org          case '(':
2687fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            ++openParens;
2697fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            isArg = true;
2707fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
2717fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org          case ')':
2727fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            --openParens;
2737fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            isArg = openParens != 0;
2747fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
2757fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org          case ',':
2767fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // The individual arguments are separated by comma tokens, but
2777fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // the comma tokens between matching inner parentheses do not
2787fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // seperate arguments.
279d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo            if (openParens == 1)
280d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                args->push_back(MacroArg());
2817fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            isArg = openParens != 1;
2827fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
2837fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org          default:
2847fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            isArg = true;
2857fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            break;
2867fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
2877fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (isArg)
2887fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
289d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo            MacroArg &arg = args->back();
2907fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            // Initial whitespace is not part of the argument.
291d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo            if (arg.empty())
292d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                token.setHasLeadingSpace(false);
2937fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            arg.push_back(token);
2947fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
2957fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
2967fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
297d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo    const Macro::Parameters &params = macro.parameters;
2987fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // If there is only one empty argument, it is equivalent to no argument.
2997fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (params.empty() && (args->size() == 1) && args->front().empty())
3007fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
3017fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        args->clear();
3027fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
3037fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // Validate the number of arguments.
3047fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    if (args->size() != params.size())
3057fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
3067fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        Diagnostics::ID id = args->size() < macro.parameters.size() ?
3077f2d7945ee702241829727751bd4c7424662b225Shannon Woods            Diagnostics::PP_MACRO_TOO_FEW_ARGS :
3087f2d7945ee702241829727751bd4c7424662b225Shannon Woods            Diagnostics::PP_MACRO_TOO_MANY_ARGS;
3095b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org        mDiagnostics->report(id, identifier.location, identifier.text);
3107fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        return false;
3117fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
3127fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
3137fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // Pre-expand each argument before substitution.
3147fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // This step expands each argument individually before they are
3157fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    // inserted into the macro body.
316a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com    for (std::size_t i = 0; i < args->size(); ++i)
3177fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
318d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo        MacroArg &arg = args->at(i);
3197fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        TokenLexer lexer(&arg);
3207fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
3217fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
3227fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        arg.clear();
3237fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        expander.lex(&token);
3247fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        while (token.type != Token::LAST)
3257fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
3267fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            arg.push_back(token);
3277fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            expander.lex(&token);
3287fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
3297fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
3307fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    return true;
3317fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org}
3327fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
333d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Movoid MacroExpander::replaceMacroParams(const Macro &macro,
334d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                       const std::vector<MacroArg> &args,
335d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo                                       std::vector<Token> *replacements)
3367fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org{
337a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com    for (std::size_t i = 0; i < macro.replacements.size(); ++i)
3387fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    {
339d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo        const Token &repl = macro.replacements[i];
3407fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (repl.type != Token::IDENTIFIER)
3417fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
3427fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            replacements->push_back(repl);
3437fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            continue;
3447fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
3457fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
3467fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        // TODO(alokp): Optimize this.
3477fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        // There is no need to search for macro params every time.
3487fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        // The param index can be cached with the replacement token.
3497fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        Macro::Parameters::const_iterator iter = std::find(
3505b6a68e04a7d27dab71524fa481f42818f84a240alokp@chromium.org            macro.parameters.begin(), macro.parameters.end(), repl.text);
3517fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (iter == macro.parameters.end())
3527fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
3537fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            replacements->push_back(repl);
3547fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            continue;
3557fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
3567fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org
357a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com        std::size_t iArg = std::distance(macro.parameters.begin(), iter);
358d526f9895c0aa867adb74a78e53832a9240c6ad6Zhenyao Mo        const MacroArg &arg = args[iArg];
3597fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        if (arg.empty())
3607fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        {
3617fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org            continue;
3627fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        }
363a16a55f7286ac5b4bbfc0d767e6e55e5714fa062daniel@transgaming.com        std::size_t iRepl = replacements->size();
3647fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        replacements->insert(replacements->end(), arg.begin(), arg.end());
3657fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        // The replacement token inherits padding properties from
3667fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        // macro replacement token.
3677fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org        replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
3687fc38dddd2f5b6349348230475ca359666af583aalokp@chromium.org    }
36904d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org}
37004d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
37104d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org}  // namespace pp
37204d7d22bb835408a82600244e09b9dcacbc0fa11alokp@chromium.org
373