1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,370 @@ 1.4 +// 1.5 +// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved. 1.6 +// Use of this source code is governed by a BSD-style license that can be 1.7 +// found in the LICENSE file. 1.8 +// 1.9 + 1.10 +#include "MacroExpander.h" 1.11 + 1.12 +#include <algorithm> 1.13 +#include <sstream> 1.14 + 1.15 +#include "DiagnosticsBase.h" 1.16 +#include "Token.h" 1.17 + 1.18 +namespace pp 1.19 +{ 1.20 + 1.21 +class TokenLexer : public Lexer 1.22 +{ 1.23 + public: 1.24 + typedef std::vector<Token> TokenVector; 1.25 + 1.26 + TokenLexer(TokenVector* tokens) 1.27 + { 1.28 + tokens->swap(mTokens); 1.29 + mIter = mTokens.begin(); 1.30 + } 1.31 + 1.32 + virtual void lex(Token* token) 1.33 + { 1.34 + if (mIter == mTokens.end()) 1.35 + { 1.36 + token->reset(); 1.37 + token->type = Token::LAST; 1.38 + } 1.39 + else 1.40 + { 1.41 + *token = *mIter++; 1.42 + } 1.43 + } 1.44 + 1.45 + private: 1.46 + PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer); 1.47 + 1.48 + TokenVector mTokens; 1.49 + TokenVector::const_iterator mIter; 1.50 +}; 1.51 + 1.52 +MacroExpander::MacroExpander(Lexer* lexer, 1.53 + MacroSet* macroSet, 1.54 + Diagnostics* diagnostics) : 1.55 + mLexer(lexer), 1.56 + mMacroSet(macroSet), 1.57 + mDiagnostics(diagnostics) 1.58 +{ 1.59 +} 1.60 + 1.61 +MacroExpander::~MacroExpander() 1.62 +{ 1.63 + for (std::size_t i = 0; i < mContextStack.size(); ++i) 1.64 + { 1.65 + delete mContextStack[i]; 1.66 + } 1.67 +} 1.68 + 1.69 +void MacroExpander::lex(Token* token) 1.70 +{ 1.71 + while (true) 1.72 + { 1.73 + getToken(token); 1.74 + 1.75 + if (token->type != Token::IDENTIFIER) 1.76 + break; 1.77 + 1.78 + if (token->expansionDisabled()) 1.79 + break; 1.80 + 1.81 + MacroSet::const_iterator iter = mMacroSet->find(token->text); 1.82 + if (iter == mMacroSet->end()) 1.83 + break; 1.84 + 1.85 + const Macro& macro = iter->second; 1.86 + if (macro.disabled) 1.87 + { 1.88 + // If a particular token is not expanded, it is never expanded. 1.89 + token->setExpansionDisabled(true); 1.90 + break; 1.91 + } 1.92 + if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen()) 1.93 + { 1.94 + // If the token immediately after the macro name is not a '(', 1.95 + // this macro should not be expanded. 1.96 + break; 1.97 + } 1.98 + 1.99 + pushMacro(macro, *token); 1.100 + } 1.101 +} 1.102 + 1.103 +void MacroExpander::getToken(Token* token) 1.104 +{ 1.105 + if (mReserveToken.get()) 1.106 + { 1.107 + *token = *mReserveToken; 1.108 + mReserveToken.reset(); 1.109 + return; 1.110 + } 1.111 + 1.112 + // First pop all empty macro contexts. 1.113 + while (!mContextStack.empty() && mContextStack.back()->empty()) 1.114 + { 1.115 + popMacro(); 1.116 + } 1.117 + 1.118 + if (!mContextStack.empty()) 1.119 + { 1.120 + *token = mContextStack.back()->get(); 1.121 + } 1.122 + else 1.123 + { 1.124 + mLexer->lex(token); 1.125 + } 1.126 +} 1.127 + 1.128 +void MacroExpander::ungetToken(const Token& token) 1.129 +{ 1.130 + if (!mContextStack.empty()) 1.131 + { 1.132 + MacroContext* context = mContextStack.back(); 1.133 + context->unget(); 1.134 + assert(context->replacements[context->index] == token); 1.135 + } 1.136 + else 1.137 + { 1.138 + assert(!mReserveToken.get()); 1.139 + mReserveToken.reset(new Token(token)); 1.140 + } 1.141 +} 1.142 + 1.143 +bool MacroExpander::isNextTokenLeftParen() 1.144 +{ 1.145 + Token token; 1.146 + getToken(&token); 1.147 + 1.148 + bool lparen = token.type == '('; 1.149 + ungetToken(token); 1.150 + 1.151 + return lparen; 1.152 +} 1.153 + 1.154 +bool MacroExpander::pushMacro(const Macro& macro, const Token& identifier) 1.155 +{ 1.156 + assert(!macro.disabled); 1.157 + assert(!identifier.expansionDisabled()); 1.158 + assert(identifier.type == Token::IDENTIFIER); 1.159 + assert(identifier.text == macro.name); 1.160 + 1.161 + std::vector<Token> replacements; 1.162 + if (!expandMacro(macro, identifier, &replacements)) 1.163 + return false; 1.164 + 1.165 + // Macro is disabled for expansion until it is popped off the stack. 1.166 + macro.disabled = true; 1.167 + 1.168 + MacroContext* context = new MacroContext; 1.169 + context->macro = ¯o; 1.170 + context->replacements.swap(replacements); 1.171 + mContextStack.push_back(context); 1.172 + return true; 1.173 +} 1.174 + 1.175 +void MacroExpander::popMacro() 1.176 +{ 1.177 + assert(!mContextStack.empty()); 1.178 + 1.179 + MacroContext* context = mContextStack.back(); 1.180 + mContextStack.pop_back(); 1.181 + 1.182 + assert(context->empty()); 1.183 + assert(context->macro->disabled); 1.184 + context->macro->disabled = false; 1.185 + delete context; 1.186 +} 1.187 + 1.188 +bool MacroExpander::expandMacro(const Macro& macro, 1.189 + const Token& identifier, 1.190 + std::vector<Token>* replacements) 1.191 +{ 1.192 + replacements->clear(); 1.193 + if (macro.type == Macro::kTypeObj) 1.194 + { 1.195 + replacements->assign(macro.replacements.begin(), 1.196 + macro.replacements.end()); 1.197 + 1.198 + if (macro.predefined) 1.199 + { 1.200 + static const std::string kLine = "__LINE__"; 1.201 + static const std::string kFile = "__FILE__"; 1.202 + 1.203 + assert(replacements->size() == 1); 1.204 + Token& repl = replacements->front(); 1.205 + if (macro.name == kLine) 1.206 + { 1.207 + std::ostringstream stream; 1.208 + stream << identifier.location.line; 1.209 + repl.text = stream.str(); 1.210 + } 1.211 + else if (macro.name == kFile) 1.212 + { 1.213 + std::ostringstream stream; 1.214 + stream << identifier.location.file; 1.215 + repl.text = stream.str(); 1.216 + } 1.217 + } 1.218 + } 1.219 + else 1.220 + { 1.221 + assert(macro.type == Macro::kTypeFunc); 1.222 + std::vector<MacroArg> args; 1.223 + args.reserve(macro.parameters.size()); 1.224 + if (!collectMacroArgs(macro, identifier, &args)) 1.225 + return false; 1.226 + 1.227 + replaceMacroParams(macro, args, replacements); 1.228 + } 1.229 + 1.230 + for (std::size_t i = 0; i < replacements->size(); ++i) 1.231 + { 1.232 + Token& repl = replacements->at(i); 1.233 + if (i == 0) 1.234 + { 1.235 + // The first token in the replacement list inherits the padding 1.236 + // properties of the identifier token. 1.237 + repl.setAtStartOfLine(identifier.atStartOfLine()); 1.238 + repl.setHasLeadingSpace(identifier.hasLeadingSpace()); 1.239 + } 1.240 + repl.location = identifier.location; 1.241 + } 1.242 + return true; 1.243 +} 1.244 + 1.245 +bool MacroExpander::collectMacroArgs(const Macro& macro, 1.246 + const Token& identifier, 1.247 + std::vector<MacroArg>* args) 1.248 +{ 1.249 + Token token; 1.250 + getToken(&token); 1.251 + assert(token.type == '('); 1.252 + 1.253 + args->push_back(MacroArg()); 1.254 + for (int openParens = 1; openParens != 0; ) 1.255 + { 1.256 + getToken(&token); 1.257 + 1.258 + if (token.type == Token::LAST) 1.259 + { 1.260 + mDiagnostics->report(Diagnostics::MACRO_UNTERMINATED_INVOCATION, 1.261 + identifier.location, identifier.text); 1.262 + // Do not lose EOF token. 1.263 + ungetToken(token); 1.264 + return false; 1.265 + } 1.266 + 1.267 + bool isArg = false; // True if token is part of the current argument. 1.268 + switch (token.type) 1.269 + { 1.270 + case '(': 1.271 + ++openParens; 1.272 + isArg = true; 1.273 + break; 1.274 + case ')': 1.275 + --openParens; 1.276 + isArg = openParens != 0; 1.277 + break; 1.278 + case ',': 1.279 + // The individual arguments are separated by comma tokens, but 1.280 + // the comma tokens between matching inner parentheses do not 1.281 + // seperate arguments. 1.282 + if (openParens == 1) args->push_back(MacroArg()); 1.283 + isArg = openParens != 1; 1.284 + break; 1.285 + default: 1.286 + isArg = true; 1.287 + break; 1.288 + } 1.289 + if (isArg) 1.290 + { 1.291 + MacroArg& arg = args->back(); 1.292 + // Initial whitespace is not part of the argument. 1.293 + if (arg.empty()) token.setHasLeadingSpace(false); 1.294 + arg.push_back(token); 1.295 + } 1.296 + } 1.297 + 1.298 + const Macro::Parameters& params = macro.parameters; 1.299 + // If there is only one empty argument, it is equivalent to no argument. 1.300 + if (params.empty() && (args->size() == 1) && args->front().empty()) 1.301 + { 1.302 + args->clear(); 1.303 + } 1.304 + // Validate the number of arguments. 1.305 + if (args->size() != params.size()) 1.306 + { 1.307 + Diagnostics::ID id = args->size() < macro.parameters.size() ? 1.308 + Diagnostics::MACRO_TOO_FEW_ARGS : 1.309 + Diagnostics::MACRO_TOO_MANY_ARGS; 1.310 + mDiagnostics->report(id, identifier.location, identifier.text); 1.311 + return false; 1.312 + } 1.313 + 1.314 + // Pre-expand each argument before substitution. 1.315 + // This step expands each argument individually before they are 1.316 + // inserted into the macro body. 1.317 + for (std::size_t i = 0; i < args->size(); ++i) 1.318 + { 1.319 + MacroArg& arg = args->at(i); 1.320 + TokenLexer lexer(&arg); 1.321 + MacroExpander expander(&lexer, mMacroSet, mDiagnostics); 1.322 + 1.323 + arg.clear(); 1.324 + expander.lex(&token); 1.325 + while (token.type != Token::LAST) 1.326 + { 1.327 + arg.push_back(token); 1.328 + expander.lex(&token); 1.329 + } 1.330 + } 1.331 + return true; 1.332 +} 1.333 + 1.334 +void MacroExpander::replaceMacroParams(const Macro& macro, 1.335 + const std::vector<MacroArg>& args, 1.336 + std::vector<Token>* replacements) 1.337 +{ 1.338 + for (std::size_t i = 0; i < macro.replacements.size(); ++i) 1.339 + { 1.340 + const Token& repl = macro.replacements[i]; 1.341 + if (repl.type != Token::IDENTIFIER) 1.342 + { 1.343 + replacements->push_back(repl); 1.344 + continue; 1.345 + } 1.346 + 1.347 + // TODO(alokp): Optimize this. 1.348 + // There is no need to search for macro params every time. 1.349 + // The param index can be cached with the replacement token. 1.350 + Macro::Parameters::const_iterator iter = std::find( 1.351 + macro.parameters.begin(), macro.parameters.end(), repl.text); 1.352 + if (iter == macro.parameters.end()) 1.353 + { 1.354 + replacements->push_back(repl); 1.355 + continue; 1.356 + } 1.357 + 1.358 + std::size_t iArg = std::distance(macro.parameters.begin(), iter); 1.359 + const MacroArg& arg = args[iArg]; 1.360 + if (arg.empty()) 1.361 + { 1.362 + continue; 1.363 + } 1.364 + std::size_t iRepl = replacements->size(); 1.365 + replacements->insert(replacements->end(), arg.begin(), arg.end()); 1.366 + // The replacement token inherits padding properties from 1.367 + // macro replacement token. 1.368 + replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace()); 1.369 + } 1.370 +} 1.371 + 1.372 +} // namespace pp 1.373 +