Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
michael@0 | 1 | // |
michael@0 | 2 | // Copyright (c) 2011 The ANGLE Project Authors. All rights reserved. |
michael@0 | 3 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 4 | // found in the LICENSE file. |
michael@0 | 5 | // |
michael@0 | 6 | |
michael@0 | 7 | #include "DirectiveParser.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <cassert> |
michael@0 | 10 | #include <cstdlib> |
michael@0 | 11 | #include <sstream> |
michael@0 | 12 | |
michael@0 | 13 | #include "DiagnosticsBase.h" |
michael@0 | 14 | #include "DirectiveHandlerBase.h" |
michael@0 | 15 | #include "ExpressionParser.h" |
michael@0 | 16 | #include "MacroExpander.h" |
michael@0 | 17 | #include "Token.h" |
michael@0 | 18 | #include "Tokenizer.h" |
michael@0 | 19 | |
michael@0 | 20 | namespace { |
michael@0 | 21 | enum DirectiveType |
michael@0 | 22 | { |
michael@0 | 23 | DIRECTIVE_NONE, |
michael@0 | 24 | DIRECTIVE_DEFINE, |
michael@0 | 25 | DIRECTIVE_UNDEF, |
michael@0 | 26 | DIRECTIVE_IF, |
michael@0 | 27 | DIRECTIVE_IFDEF, |
michael@0 | 28 | DIRECTIVE_IFNDEF, |
michael@0 | 29 | DIRECTIVE_ELSE, |
michael@0 | 30 | DIRECTIVE_ELIF, |
michael@0 | 31 | DIRECTIVE_ENDIF, |
michael@0 | 32 | DIRECTIVE_ERROR, |
michael@0 | 33 | DIRECTIVE_PRAGMA, |
michael@0 | 34 | DIRECTIVE_EXTENSION, |
michael@0 | 35 | DIRECTIVE_VERSION, |
michael@0 | 36 | DIRECTIVE_LINE |
michael@0 | 37 | }; |
michael@0 | 38 | } // namespace |
michael@0 | 39 | |
michael@0 | 40 | static DirectiveType getDirective(const pp::Token* token) |
michael@0 | 41 | { |
michael@0 | 42 | static const std::string kDirectiveDefine("define"); |
michael@0 | 43 | static const std::string kDirectiveUndef("undef"); |
michael@0 | 44 | static const std::string kDirectiveIf("if"); |
michael@0 | 45 | static const std::string kDirectiveIfdef("ifdef"); |
michael@0 | 46 | static const std::string kDirectiveIfndef("ifndef"); |
michael@0 | 47 | static const std::string kDirectiveElse("else"); |
michael@0 | 48 | static const std::string kDirectiveElif("elif"); |
michael@0 | 49 | static const std::string kDirectiveEndif("endif"); |
michael@0 | 50 | static const std::string kDirectiveError("error"); |
michael@0 | 51 | static const std::string kDirectivePragma("pragma"); |
michael@0 | 52 | static const std::string kDirectiveExtension("extension"); |
michael@0 | 53 | static const std::string kDirectiveVersion("version"); |
michael@0 | 54 | static const std::string kDirectiveLine("line"); |
michael@0 | 55 | |
michael@0 | 56 | if (token->type != pp::Token::IDENTIFIER) |
michael@0 | 57 | return DIRECTIVE_NONE; |
michael@0 | 58 | |
michael@0 | 59 | if (token->text == kDirectiveDefine) |
michael@0 | 60 | return DIRECTIVE_DEFINE; |
michael@0 | 61 | else if (token->text == kDirectiveUndef) |
michael@0 | 62 | return DIRECTIVE_UNDEF; |
michael@0 | 63 | else if (token->text == kDirectiveIf) |
michael@0 | 64 | return DIRECTIVE_IF; |
michael@0 | 65 | else if (token->text == kDirectiveIfdef) |
michael@0 | 66 | return DIRECTIVE_IFDEF; |
michael@0 | 67 | else if (token->text == kDirectiveIfndef) |
michael@0 | 68 | return DIRECTIVE_IFNDEF; |
michael@0 | 69 | else if (token->text == kDirectiveElse) |
michael@0 | 70 | return DIRECTIVE_ELSE; |
michael@0 | 71 | else if (token->text == kDirectiveElif) |
michael@0 | 72 | return DIRECTIVE_ELIF; |
michael@0 | 73 | else if (token->text == kDirectiveEndif) |
michael@0 | 74 | return DIRECTIVE_ENDIF; |
michael@0 | 75 | else if (token->text == kDirectiveError) |
michael@0 | 76 | return DIRECTIVE_ERROR; |
michael@0 | 77 | else if (token->text == kDirectivePragma) |
michael@0 | 78 | return DIRECTIVE_PRAGMA; |
michael@0 | 79 | else if (token->text == kDirectiveExtension) |
michael@0 | 80 | return DIRECTIVE_EXTENSION; |
michael@0 | 81 | else if (token->text == kDirectiveVersion) |
michael@0 | 82 | return DIRECTIVE_VERSION; |
michael@0 | 83 | else if (token->text == kDirectiveLine) |
michael@0 | 84 | return DIRECTIVE_LINE; |
michael@0 | 85 | |
michael@0 | 86 | return DIRECTIVE_NONE; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | static bool isConditionalDirective(DirectiveType directive) |
michael@0 | 90 | { |
michael@0 | 91 | switch (directive) |
michael@0 | 92 | { |
michael@0 | 93 | case DIRECTIVE_IF: |
michael@0 | 94 | case DIRECTIVE_IFDEF: |
michael@0 | 95 | case DIRECTIVE_IFNDEF: |
michael@0 | 96 | case DIRECTIVE_ELSE: |
michael@0 | 97 | case DIRECTIVE_ELIF: |
michael@0 | 98 | case DIRECTIVE_ENDIF: |
michael@0 | 99 | return true; |
michael@0 | 100 | default: |
michael@0 | 101 | return false; |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | // Returns true if the token represents End Of Directive. |
michael@0 | 106 | static bool isEOD(const pp::Token* token) |
michael@0 | 107 | { |
michael@0 | 108 | return (token->type == '\n') || (token->type == pp::Token::LAST); |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token) |
michael@0 | 112 | { |
michael@0 | 113 | while(!isEOD(token)) |
michael@0 | 114 | { |
michael@0 | 115 | lexer->lex(token); |
michael@0 | 116 | } |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | static bool isMacroNameReserved(const std::string& name) |
michael@0 | 120 | { |
michael@0 | 121 | // Names prefixed with "GL_" are reserved. |
michael@0 | 122 | if (name.substr(0, 3) == "GL_") |
michael@0 | 123 | return true; |
michael@0 | 124 | |
michael@0 | 125 | // Names containing two consecutive underscores are reserved. |
michael@0 | 126 | if (name.find("__") != std::string::npos) |
michael@0 | 127 | return true; |
michael@0 | 128 | |
michael@0 | 129 | return false; |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | static bool isMacroPredefined(const std::string& name, |
michael@0 | 133 | const pp::MacroSet& macroSet) |
michael@0 | 134 | { |
michael@0 | 135 | pp::MacroSet::const_iterator iter = macroSet.find(name); |
michael@0 | 136 | return iter != macroSet.end() ? iter->second.predefined : false; |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | namespace pp |
michael@0 | 140 | { |
michael@0 | 141 | |
michael@0 | 142 | class DefinedParser : public Lexer |
michael@0 | 143 | { |
michael@0 | 144 | public: |
michael@0 | 145 | DefinedParser(Lexer* lexer, |
michael@0 | 146 | const MacroSet* macroSet, |
michael@0 | 147 | Diagnostics* diagnostics) : |
michael@0 | 148 | mLexer(lexer), |
michael@0 | 149 | mMacroSet(macroSet), |
michael@0 | 150 | mDiagnostics(diagnostics) |
michael@0 | 151 | { |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | protected: |
michael@0 | 155 | virtual void lex(Token* token) |
michael@0 | 156 | { |
michael@0 | 157 | static const std::string kDefined("defined"); |
michael@0 | 158 | |
michael@0 | 159 | mLexer->lex(token); |
michael@0 | 160 | if (token->type != Token::IDENTIFIER) |
michael@0 | 161 | return; |
michael@0 | 162 | if (token->text != kDefined) |
michael@0 | 163 | return; |
michael@0 | 164 | |
michael@0 | 165 | bool paren = false; |
michael@0 | 166 | mLexer->lex(token); |
michael@0 | 167 | if (token->type == '(') |
michael@0 | 168 | { |
michael@0 | 169 | paren = true; |
michael@0 | 170 | mLexer->lex(token); |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | if (token->type != Token::IDENTIFIER) |
michael@0 | 174 | { |
michael@0 | 175 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 176 | token->location, token->text); |
michael@0 | 177 | skipUntilEOD(mLexer, token); |
michael@0 | 178 | return; |
michael@0 | 179 | } |
michael@0 | 180 | MacroSet::const_iterator iter = mMacroSet->find(token->text); |
michael@0 | 181 | std::string expression = iter != mMacroSet->end() ? "1" : "0"; |
michael@0 | 182 | |
michael@0 | 183 | if (paren) |
michael@0 | 184 | { |
michael@0 | 185 | mLexer->lex(token); |
michael@0 | 186 | if (token->type != ')') |
michael@0 | 187 | { |
michael@0 | 188 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 189 | token->location, token->text); |
michael@0 | 190 | skipUntilEOD(mLexer, token); |
michael@0 | 191 | return; |
michael@0 | 192 | } |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | // We have a valid defined operator. |
michael@0 | 196 | // Convert the current token into a CONST_INT token. |
michael@0 | 197 | token->type = Token::CONST_INT; |
michael@0 | 198 | token->text = expression; |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | private: |
michael@0 | 202 | Lexer* mLexer; |
michael@0 | 203 | const MacroSet* mMacroSet; |
michael@0 | 204 | Diagnostics* mDiagnostics; |
michael@0 | 205 | }; |
michael@0 | 206 | |
michael@0 | 207 | DirectiveParser::DirectiveParser(Tokenizer* tokenizer, |
michael@0 | 208 | MacroSet* macroSet, |
michael@0 | 209 | Diagnostics* diagnostics, |
michael@0 | 210 | DirectiveHandler* directiveHandler) : |
michael@0 | 211 | mPastFirstStatement(false), |
michael@0 | 212 | mTokenizer(tokenizer), |
michael@0 | 213 | mMacroSet(macroSet), |
michael@0 | 214 | mDiagnostics(diagnostics), |
michael@0 | 215 | mDirectiveHandler(directiveHandler) |
michael@0 | 216 | { |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | void DirectiveParser::lex(Token* token) |
michael@0 | 220 | { |
michael@0 | 221 | do |
michael@0 | 222 | { |
michael@0 | 223 | mTokenizer->lex(token); |
michael@0 | 224 | |
michael@0 | 225 | if (token->type == Token::PP_HASH) |
michael@0 | 226 | { |
michael@0 | 227 | parseDirective(token); |
michael@0 | 228 | mPastFirstStatement = true; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | if (token->type == Token::LAST) |
michael@0 | 232 | { |
michael@0 | 233 | if (!mConditionalStack.empty()) |
michael@0 | 234 | { |
michael@0 | 235 | const ConditionalBlock& block = mConditionalStack.back(); |
michael@0 | 236 | mDiagnostics->report(Diagnostics::CONDITIONAL_UNTERMINATED, |
michael@0 | 237 | block.location, block.type); |
michael@0 | 238 | } |
michael@0 | 239 | break; |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | } while (skipping() || (token->type == '\n')); |
michael@0 | 243 | |
michael@0 | 244 | mPastFirstStatement = true; |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | void DirectiveParser::parseDirective(Token* token) |
michael@0 | 248 | { |
michael@0 | 249 | assert(token->type == Token::PP_HASH); |
michael@0 | 250 | |
michael@0 | 251 | mTokenizer->lex(token); |
michael@0 | 252 | if (isEOD(token)) |
michael@0 | 253 | { |
michael@0 | 254 | // Empty Directive. |
michael@0 | 255 | return; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | DirectiveType directive = getDirective(token); |
michael@0 | 259 | |
michael@0 | 260 | // While in an excluded conditional block/group, |
michael@0 | 261 | // we only parse conditional directives. |
michael@0 | 262 | if (skipping() && !isConditionalDirective(directive)) |
michael@0 | 263 | { |
michael@0 | 264 | skipUntilEOD(mTokenizer, token); |
michael@0 | 265 | return; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | switch(directive) |
michael@0 | 269 | { |
michael@0 | 270 | case DIRECTIVE_NONE: |
michael@0 | 271 | mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME, |
michael@0 | 272 | token->location, token->text); |
michael@0 | 273 | skipUntilEOD(mTokenizer, token); |
michael@0 | 274 | break; |
michael@0 | 275 | case DIRECTIVE_DEFINE: |
michael@0 | 276 | parseDefine(token); |
michael@0 | 277 | break; |
michael@0 | 278 | case DIRECTIVE_UNDEF: |
michael@0 | 279 | parseUndef(token); |
michael@0 | 280 | break; |
michael@0 | 281 | case DIRECTIVE_IF: |
michael@0 | 282 | parseIf(token); |
michael@0 | 283 | break; |
michael@0 | 284 | case DIRECTIVE_IFDEF: |
michael@0 | 285 | parseIfdef(token); |
michael@0 | 286 | break; |
michael@0 | 287 | case DIRECTIVE_IFNDEF: |
michael@0 | 288 | parseIfndef(token); |
michael@0 | 289 | break; |
michael@0 | 290 | case DIRECTIVE_ELSE: |
michael@0 | 291 | parseElse(token); |
michael@0 | 292 | break; |
michael@0 | 293 | case DIRECTIVE_ELIF: |
michael@0 | 294 | parseElif(token); |
michael@0 | 295 | break; |
michael@0 | 296 | case DIRECTIVE_ENDIF: |
michael@0 | 297 | parseEndif(token); |
michael@0 | 298 | break; |
michael@0 | 299 | case DIRECTIVE_ERROR: |
michael@0 | 300 | parseError(token); |
michael@0 | 301 | break; |
michael@0 | 302 | case DIRECTIVE_PRAGMA: |
michael@0 | 303 | parsePragma(token); |
michael@0 | 304 | break; |
michael@0 | 305 | case DIRECTIVE_EXTENSION: |
michael@0 | 306 | parseExtension(token); |
michael@0 | 307 | break; |
michael@0 | 308 | case DIRECTIVE_VERSION: |
michael@0 | 309 | parseVersion(token); |
michael@0 | 310 | break; |
michael@0 | 311 | case DIRECTIVE_LINE: |
michael@0 | 312 | parseLine(token); |
michael@0 | 313 | break; |
michael@0 | 314 | default: |
michael@0 | 315 | assert(false); |
michael@0 | 316 | break; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | skipUntilEOD(mTokenizer, token); |
michael@0 | 320 | if (token->type == Token::LAST) |
michael@0 | 321 | { |
michael@0 | 322 | mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE, |
michael@0 | 323 | token->location, token->text); |
michael@0 | 324 | } |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | void DirectiveParser::parseDefine(Token* token) |
michael@0 | 328 | { |
michael@0 | 329 | assert(getDirective(token) == DIRECTIVE_DEFINE); |
michael@0 | 330 | |
michael@0 | 331 | mTokenizer->lex(token); |
michael@0 | 332 | if (token->type != Token::IDENTIFIER) |
michael@0 | 333 | { |
michael@0 | 334 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 335 | token->location, token->text); |
michael@0 | 336 | return; |
michael@0 | 337 | } |
michael@0 | 338 | if (isMacroPredefined(token->text, *mMacroSet)) |
michael@0 | 339 | { |
michael@0 | 340 | mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED, |
michael@0 | 341 | token->location, token->text); |
michael@0 | 342 | return; |
michael@0 | 343 | } |
michael@0 | 344 | if (isMacroNameReserved(token->text)) |
michael@0 | 345 | { |
michael@0 | 346 | mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, |
michael@0 | 347 | token->location, token->text); |
michael@0 | 348 | return; |
michael@0 | 349 | } |
michael@0 | 350 | |
michael@0 | 351 | Macro macro; |
michael@0 | 352 | macro.type = Macro::kTypeObj; |
michael@0 | 353 | macro.name = token->text; |
michael@0 | 354 | |
michael@0 | 355 | mTokenizer->lex(token); |
michael@0 | 356 | if (token->type == '(' && !token->hasLeadingSpace()) |
michael@0 | 357 | { |
michael@0 | 358 | // Function-like macro. Collect arguments. |
michael@0 | 359 | macro.type = Macro::kTypeFunc; |
michael@0 | 360 | do { |
michael@0 | 361 | mTokenizer->lex(token); |
michael@0 | 362 | if (token->type != Token::IDENTIFIER) |
michael@0 | 363 | break; |
michael@0 | 364 | macro.parameters.push_back(token->text); |
michael@0 | 365 | |
michael@0 | 366 | mTokenizer->lex(token); // Get ','. |
michael@0 | 367 | } while (token->type == ','); |
michael@0 | 368 | |
michael@0 | 369 | if (token->type != ')') |
michael@0 | 370 | { |
michael@0 | 371 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 372 | token->location, |
michael@0 | 373 | token->text); |
michael@0 | 374 | return; |
michael@0 | 375 | } |
michael@0 | 376 | mTokenizer->lex(token); // Get ')'. |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 380 | { |
michael@0 | 381 | // Reset the token location because it is unnecessary in replacement |
michael@0 | 382 | // list. Resetting it also allows us to reuse Token::equals() to |
michael@0 | 383 | // compare macros. |
michael@0 | 384 | token->location = SourceLocation(); |
michael@0 | 385 | macro.replacements.push_back(*token); |
michael@0 | 386 | mTokenizer->lex(token); |
michael@0 | 387 | } |
michael@0 | 388 | if (!macro.replacements.empty()) |
michael@0 | 389 | { |
michael@0 | 390 | // Whitespace preceding the replacement list is not considered part of |
michael@0 | 391 | // the replacement list for either form of macro. |
michael@0 | 392 | macro.replacements.front().setHasLeadingSpace(false); |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | // Check for macro redefinition. |
michael@0 | 396 | MacroSet::const_iterator iter = mMacroSet->find(macro.name); |
michael@0 | 397 | if (iter != mMacroSet->end() && !macro.equals(iter->second)) |
michael@0 | 398 | { |
michael@0 | 399 | mDiagnostics->report(Diagnostics::MACRO_REDEFINED, |
michael@0 | 400 | token->location, |
michael@0 | 401 | macro.name); |
michael@0 | 402 | return; |
michael@0 | 403 | } |
michael@0 | 404 | mMacroSet->insert(std::make_pair(macro.name, macro)); |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | void DirectiveParser::parseUndef(Token* token) |
michael@0 | 408 | { |
michael@0 | 409 | assert(getDirective(token) == DIRECTIVE_UNDEF); |
michael@0 | 410 | |
michael@0 | 411 | mTokenizer->lex(token); |
michael@0 | 412 | if (token->type != Token::IDENTIFIER) |
michael@0 | 413 | { |
michael@0 | 414 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 415 | token->location, token->text); |
michael@0 | 416 | return; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | MacroSet::iterator iter = mMacroSet->find(token->text); |
michael@0 | 420 | if (iter != mMacroSet->end()) |
michael@0 | 421 | { |
michael@0 | 422 | if (iter->second.predefined) |
michael@0 | 423 | { |
michael@0 | 424 | mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED, |
michael@0 | 425 | token->location, token->text); |
michael@0 | 426 | } |
michael@0 | 427 | else |
michael@0 | 428 | { |
michael@0 | 429 | mMacroSet->erase(iter); |
michael@0 | 430 | } |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | mTokenizer->lex(token); |
michael@0 | 434 | } |
michael@0 | 435 | |
michael@0 | 436 | void DirectiveParser::parseIf(Token* token) |
michael@0 | 437 | { |
michael@0 | 438 | assert(getDirective(token) == DIRECTIVE_IF); |
michael@0 | 439 | parseConditionalIf(token); |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | void DirectiveParser::parseIfdef(Token* token) |
michael@0 | 443 | { |
michael@0 | 444 | assert(getDirective(token) == DIRECTIVE_IFDEF); |
michael@0 | 445 | parseConditionalIf(token); |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | void DirectiveParser::parseIfndef(Token* token) |
michael@0 | 449 | { |
michael@0 | 450 | assert(getDirective(token) == DIRECTIVE_IFNDEF); |
michael@0 | 451 | parseConditionalIf(token); |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | void DirectiveParser::parseElse(Token* token) |
michael@0 | 455 | { |
michael@0 | 456 | assert(getDirective(token) == DIRECTIVE_ELSE); |
michael@0 | 457 | |
michael@0 | 458 | if (mConditionalStack.empty()) |
michael@0 | 459 | { |
michael@0 | 460 | mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF, |
michael@0 | 461 | token->location, token->text); |
michael@0 | 462 | skipUntilEOD(mTokenizer, token); |
michael@0 | 463 | return; |
michael@0 | 464 | } |
michael@0 | 465 | |
michael@0 | 466 | ConditionalBlock& block = mConditionalStack.back(); |
michael@0 | 467 | if (block.skipBlock) |
michael@0 | 468 | { |
michael@0 | 469 | // No diagnostics. Just skip the whole line. |
michael@0 | 470 | skipUntilEOD(mTokenizer, token); |
michael@0 | 471 | return; |
michael@0 | 472 | } |
michael@0 | 473 | if (block.foundElseGroup) |
michael@0 | 474 | { |
michael@0 | 475 | mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_AFTER_ELSE, |
michael@0 | 476 | token->location, token->text); |
michael@0 | 477 | skipUntilEOD(mTokenizer, token); |
michael@0 | 478 | return; |
michael@0 | 479 | } |
michael@0 | 480 | |
michael@0 | 481 | block.foundElseGroup = true; |
michael@0 | 482 | block.skipGroup = block.foundValidGroup; |
michael@0 | 483 | block.foundValidGroup = true; |
michael@0 | 484 | |
michael@0 | 485 | // Warn if there are extra tokens after #else. |
michael@0 | 486 | mTokenizer->lex(token); |
michael@0 | 487 | if (!isEOD(token)) |
michael@0 | 488 | { |
michael@0 | 489 | mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
michael@0 | 490 | token->location, token->text); |
michael@0 | 491 | skipUntilEOD(mTokenizer, token); |
michael@0 | 492 | } |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | void DirectiveParser::parseElif(Token* token) |
michael@0 | 496 | { |
michael@0 | 497 | assert(getDirective(token) == DIRECTIVE_ELIF); |
michael@0 | 498 | |
michael@0 | 499 | if (mConditionalStack.empty()) |
michael@0 | 500 | { |
michael@0 | 501 | mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF, |
michael@0 | 502 | token->location, token->text); |
michael@0 | 503 | skipUntilEOD(mTokenizer, token); |
michael@0 | 504 | return; |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | ConditionalBlock& block = mConditionalStack.back(); |
michael@0 | 508 | if (block.skipBlock) |
michael@0 | 509 | { |
michael@0 | 510 | // No diagnostics. Just skip the whole line. |
michael@0 | 511 | skipUntilEOD(mTokenizer, token); |
michael@0 | 512 | return; |
michael@0 | 513 | } |
michael@0 | 514 | if (block.foundElseGroup) |
michael@0 | 515 | { |
michael@0 | 516 | mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_AFTER_ELSE, |
michael@0 | 517 | token->location, token->text); |
michael@0 | 518 | skipUntilEOD(mTokenizer, token); |
michael@0 | 519 | return; |
michael@0 | 520 | } |
michael@0 | 521 | if (block.foundValidGroup) |
michael@0 | 522 | { |
michael@0 | 523 | // Do not parse the expression. |
michael@0 | 524 | // Also be careful not to emit a diagnostic. |
michael@0 | 525 | block.skipGroup = true; |
michael@0 | 526 | skipUntilEOD(mTokenizer, token); |
michael@0 | 527 | return; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | int expression = parseExpressionIf(token); |
michael@0 | 531 | block.skipGroup = expression == 0; |
michael@0 | 532 | block.foundValidGroup = expression != 0; |
michael@0 | 533 | } |
michael@0 | 534 | |
michael@0 | 535 | void DirectiveParser::parseEndif(Token* token) |
michael@0 | 536 | { |
michael@0 | 537 | assert(getDirective(token) == DIRECTIVE_ENDIF); |
michael@0 | 538 | |
michael@0 | 539 | if (mConditionalStack.empty()) |
michael@0 | 540 | { |
michael@0 | 541 | mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF, |
michael@0 | 542 | token->location, token->text); |
michael@0 | 543 | skipUntilEOD(mTokenizer, token); |
michael@0 | 544 | return; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | mConditionalStack.pop_back(); |
michael@0 | 548 | |
michael@0 | 549 | // Warn if there are tokens after #endif. |
michael@0 | 550 | mTokenizer->lex(token); |
michael@0 | 551 | if (!isEOD(token)) |
michael@0 | 552 | { |
michael@0 | 553 | mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
michael@0 | 554 | token->location, token->text); |
michael@0 | 555 | skipUntilEOD(mTokenizer, token); |
michael@0 | 556 | } |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | void DirectiveParser::parseError(Token* token) |
michael@0 | 560 | { |
michael@0 | 561 | assert(getDirective(token) == DIRECTIVE_ERROR); |
michael@0 | 562 | |
michael@0 | 563 | std::ostringstream stream; |
michael@0 | 564 | mTokenizer->lex(token); |
michael@0 | 565 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 566 | { |
michael@0 | 567 | stream << *token; |
michael@0 | 568 | mTokenizer->lex(token); |
michael@0 | 569 | } |
michael@0 | 570 | mDirectiveHandler->handleError(token->location, stream.str()); |
michael@0 | 571 | } |
michael@0 | 572 | |
michael@0 | 573 | // Parses pragma of form: #pragma name[(value)]. |
michael@0 | 574 | void DirectiveParser::parsePragma(Token* token) |
michael@0 | 575 | { |
michael@0 | 576 | assert(getDirective(token) == DIRECTIVE_PRAGMA); |
michael@0 | 577 | |
michael@0 | 578 | enum State |
michael@0 | 579 | { |
michael@0 | 580 | PRAGMA_NAME, |
michael@0 | 581 | LEFT_PAREN, |
michael@0 | 582 | PRAGMA_VALUE, |
michael@0 | 583 | RIGHT_PAREN |
michael@0 | 584 | }; |
michael@0 | 585 | |
michael@0 | 586 | bool valid = true; |
michael@0 | 587 | std::string name, value; |
michael@0 | 588 | int state = PRAGMA_NAME; |
michael@0 | 589 | |
michael@0 | 590 | mTokenizer->lex(token); |
michael@0 | 591 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 592 | { |
michael@0 | 593 | switch(state++) |
michael@0 | 594 | { |
michael@0 | 595 | case PRAGMA_NAME: |
michael@0 | 596 | name = token->text; |
michael@0 | 597 | valid = valid && (token->type == Token::IDENTIFIER); |
michael@0 | 598 | break; |
michael@0 | 599 | case LEFT_PAREN: |
michael@0 | 600 | valid = valid && (token->type == '('); |
michael@0 | 601 | break; |
michael@0 | 602 | case PRAGMA_VALUE: |
michael@0 | 603 | value = token->text; |
michael@0 | 604 | valid = valid && (token->type == Token::IDENTIFIER); |
michael@0 | 605 | break; |
michael@0 | 606 | case RIGHT_PAREN: |
michael@0 | 607 | valid = valid && (token->type == ')'); |
michael@0 | 608 | break; |
michael@0 | 609 | default: |
michael@0 | 610 | valid = false; |
michael@0 | 611 | break; |
michael@0 | 612 | } |
michael@0 | 613 | mTokenizer->lex(token); |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | valid = valid && ((state == PRAGMA_NAME) || // Empty pragma. |
michael@0 | 617 | (state == LEFT_PAREN) || // Without value. |
michael@0 | 618 | (state == RIGHT_PAREN + 1)); // With value. |
michael@0 | 619 | if (!valid) |
michael@0 | 620 | { |
michael@0 | 621 | mDiagnostics->report(Diagnostics::UNRECOGNIZED_PRAGMA, |
michael@0 | 622 | token->location, name); |
michael@0 | 623 | } |
michael@0 | 624 | else if (state > PRAGMA_NAME) // Do not notify for empty pragma. |
michael@0 | 625 | { |
michael@0 | 626 | mDirectiveHandler->handlePragma(token->location, name, value); |
michael@0 | 627 | } |
michael@0 | 628 | } |
michael@0 | 629 | |
michael@0 | 630 | void DirectiveParser::parseExtension(Token* token) |
michael@0 | 631 | { |
michael@0 | 632 | assert(getDirective(token) == DIRECTIVE_EXTENSION); |
michael@0 | 633 | |
michael@0 | 634 | enum State |
michael@0 | 635 | { |
michael@0 | 636 | EXT_NAME, |
michael@0 | 637 | COLON, |
michael@0 | 638 | EXT_BEHAVIOR |
michael@0 | 639 | }; |
michael@0 | 640 | |
michael@0 | 641 | bool valid = true; |
michael@0 | 642 | std::string name, behavior; |
michael@0 | 643 | int state = EXT_NAME; |
michael@0 | 644 | |
michael@0 | 645 | mTokenizer->lex(token); |
michael@0 | 646 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 647 | { |
michael@0 | 648 | switch (state++) |
michael@0 | 649 | { |
michael@0 | 650 | case EXT_NAME: |
michael@0 | 651 | if (valid && (token->type != Token::IDENTIFIER)) |
michael@0 | 652 | { |
michael@0 | 653 | mDiagnostics->report(Diagnostics::INVALID_EXTENSION_NAME, |
michael@0 | 654 | token->location, token->text); |
michael@0 | 655 | valid = false; |
michael@0 | 656 | } |
michael@0 | 657 | if (valid) name = token->text; |
michael@0 | 658 | break; |
michael@0 | 659 | case COLON: |
michael@0 | 660 | if (valid && (token->type != ':')) |
michael@0 | 661 | { |
michael@0 | 662 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 663 | token->location, token->text); |
michael@0 | 664 | valid = false; |
michael@0 | 665 | } |
michael@0 | 666 | break; |
michael@0 | 667 | case EXT_BEHAVIOR: |
michael@0 | 668 | if (valid && (token->type != Token::IDENTIFIER)) |
michael@0 | 669 | { |
michael@0 | 670 | mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR, |
michael@0 | 671 | token->location, token->text); |
michael@0 | 672 | valid = false; |
michael@0 | 673 | } |
michael@0 | 674 | if (valid) behavior = token->text; |
michael@0 | 675 | break; |
michael@0 | 676 | default: |
michael@0 | 677 | if (valid) |
michael@0 | 678 | { |
michael@0 | 679 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 680 | token->location, token->text); |
michael@0 | 681 | valid = false; |
michael@0 | 682 | } |
michael@0 | 683 | break; |
michael@0 | 684 | } |
michael@0 | 685 | mTokenizer->lex(token); |
michael@0 | 686 | } |
michael@0 | 687 | if (valid && (state != EXT_BEHAVIOR + 1)) |
michael@0 | 688 | { |
michael@0 | 689 | mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE, |
michael@0 | 690 | token->location, token->text); |
michael@0 | 691 | valid = false; |
michael@0 | 692 | } |
michael@0 | 693 | if (valid) |
michael@0 | 694 | mDirectiveHandler->handleExtension(token->location, name, behavior); |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | void DirectiveParser::parseVersion(Token* token) |
michael@0 | 698 | { |
michael@0 | 699 | assert(getDirective(token) == DIRECTIVE_VERSION); |
michael@0 | 700 | |
michael@0 | 701 | if (mPastFirstStatement) |
michael@0 | 702 | { |
michael@0 | 703 | mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT, |
michael@0 | 704 | token->location, token->text); |
michael@0 | 705 | skipUntilEOD(mTokenizer, token); |
michael@0 | 706 | return; |
michael@0 | 707 | } |
michael@0 | 708 | |
michael@0 | 709 | enum State |
michael@0 | 710 | { |
michael@0 | 711 | VERSION_NUMBER |
michael@0 | 712 | }; |
michael@0 | 713 | |
michael@0 | 714 | bool valid = true; |
michael@0 | 715 | int version = 0; |
michael@0 | 716 | int state = VERSION_NUMBER; |
michael@0 | 717 | |
michael@0 | 718 | mTokenizer->lex(token); |
michael@0 | 719 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 720 | { |
michael@0 | 721 | switch (state++) |
michael@0 | 722 | { |
michael@0 | 723 | case VERSION_NUMBER: |
michael@0 | 724 | if (valid && (token->type != Token::CONST_INT)) |
michael@0 | 725 | { |
michael@0 | 726 | mDiagnostics->report(Diagnostics::INVALID_VERSION_NUMBER, |
michael@0 | 727 | token->location, token->text); |
michael@0 | 728 | valid = false; |
michael@0 | 729 | } |
michael@0 | 730 | if (valid && !token->iValue(&version)) |
michael@0 | 731 | { |
michael@0 | 732 | mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
michael@0 | 733 | token->location, token->text); |
michael@0 | 734 | valid = false; |
michael@0 | 735 | } |
michael@0 | 736 | break; |
michael@0 | 737 | default: |
michael@0 | 738 | if (valid) |
michael@0 | 739 | { |
michael@0 | 740 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 741 | token->location, token->text); |
michael@0 | 742 | valid = false; |
michael@0 | 743 | } |
michael@0 | 744 | break; |
michael@0 | 745 | } |
michael@0 | 746 | mTokenizer->lex(token); |
michael@0 | 747 | } |
michael@0 | 748 | if (valid && (state != VERSION_NUMBER + 1)) |
michael@0 | 749 | { |
michael@0 | 750 | mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE, |
michael@0 | 751 | token->location, token->text); |
michael@0 | 752 | valid = false; |
michael@0 | 753 | } |
michael@0 | 754 | if (valid) |
michael@0 | 755 | mDirectiveHandler->handleVersion(token->location, version); |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | void DirectiveParser::parseLine(Token* token) |
michael@0 | 759 | { |
michael@0 | 760 | assert(getDirective(token) == DIRECTIVE_LINE); |
michael@0 | 761 | |
michael@0 | 762 | enum State |
michael@0 | 763 | { |
michael@0 | 764 | LINE_NUMBER, |
michael@0 | 765 | FILE_NUMBER |
michael@0 | 766 | }; |
michael@0 | 767 | |
michael@0 | 768 | bool valid = true; |
michael@0 | 769 | int line = 0, file = 0; |
michael@0 | 770 | int state = LINE_NUMBER; |
michael@0 | 771 | |
michael@0 | 772 | MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); |
michael@0 | 773 | macroExpander.lex(token); |
michael@0 | 774 | while ((token->type != '\n') && (token->type != Token::LAST)) |
michael@0 | 775 | { |
michael@0 | 776 | switch (state++) |
michael@0 | 777 | { |
michael@0 | 778 | case LINE_NUMBER: |
michael@0 | 779 | if (valid && (token->type != Token::CONST_INT)) |
michael@0 | 780 | { |
michael@0 | 781 | mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER, |
michael@0 | 782 | token->location, token->text); |
michael@0 | 783 | valid = false; |
michael@0 | 784 | } |
michael@0 | 785 | if (valid && !token->iValue(&line)) |
michael@0 | 786 | { |
michael@0 | 787 | mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
michael@0 | 788 | token->location, token->text); |
michael@0 | 789 | valid = false; |
michael@0 | 790 | } |
michael@0 | 791 | break; |
michael@0 | 792 | case FILE_NUMBER: |
michael@0 | 793 | if (valid && (token->type != Token::CONST_INT)) |
michael@0 | 794 | { |
michael@0 | 795 | mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER, |
michael@0 | 796 | token->location, token->text); |
michael@0 | 797 | valid = false; |
michael@0 | 798 | } |
michael@0 | 799 | if (valid && !token->iValue(&file)) |
michael@0 | 800 | { |
michael@0 | 801 | mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
michael@0 | 802 | token->location, token->text); |
michael@0 | 803 | valid = false; |
michael@0 | 804 | } |
michael@0 | 805 | break; |
michael@0 | 806 | default: |
michael@0 | 807 | if (valid) |
michael@0 | 808 | { |
michael@0 | 809 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 810 | token->location, token->text); |
michael@0 | 811 | valid = false; |
michael@0 | 812 | } |
michael@0 | 813 | break; |
michael@0 | 814 | } |
michael@0 | 815 | macroExpander.lex(token); |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) |
michael@0 | 819 | { |
michael@0 | 820 | mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE, |
michael@0 | 821 | token->location, token->text); |
michael@0 | 822 | valid = false; |
michael@0 | 823 | } |
michael@0 | 824 | if (valid) |
michael@0 | 825 | { |
michael@0 | 826 | mTokenizer->setLineNumber(line); |
michael@0 | 827 | if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file); |
michael@0 | 828 | } |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | bool DirectiveParser::skipping() const |
michael@0 | 832 | { |
michael@0 | 833 | if (mConditionalStack.empty()) return false; |
michael@0 | 834 | |
michael@0 | 835 | const ConditionalBlock& block = mConditionalStack.back(); |
michael@0 | 836 | return block.skipBlock || block.skipGroup; |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | void DirectiveParser::parseConditionalIf(Token* token) |
michael@0 | 840 | { |
michael@0 | 841 | ConditionalBlock block; |
michael@0 | 842 | block.type = token->text; |
michael@0 | 843 | block.location = token->location; |
michael@0 | 844 | |
michael@0 | 845 | if (skipping()) |
michael@0 | 846 | { |
michael@0 | 847 | // This conditional block is inside another conditional group |
michael@0 | 848 | // which is skipped. As a consequence this whole block is skipped. |
michael@0 | 849 | // Be careful not to parse the conditional expression that might |
michael@0 | 850 | // emit a diagnostic. |
michael@0 | 851 | skipUntilEOD(mTokenizer, token); |
michael@0 | 852 | block.skipBlock = true; |
michael@0 | 853 | } |
michael@0 | 854 | else |
michael@0 | 855 | { |
michael@0 | 856 | DirectiveType directive = getDirective(token); |
michael@0 | 857 | |
michael@0 | 858 | int expression = 0; |
michael@0 | 859 | switch (directive) |
michael@0 | 860 | { |
michael@0 | 861 | case DIRECTIVE_IF: |
michael@0 | 862 | expression = parseExpressionIf(token); |
michael@0 | 863 | break; |
michael@0 | 864 | case DIRECTIVE_IFDEF: |
michael@0 | 865 | expression = parseExpressionIfdef(token); |
michael@0 | 866 | break; |
michael@0 | 867 | case DIRECTIVE_IFNDEF: |
michael@0 | 868 | expression = parseExpressionIfdef(token) == 0 ? 1 : 0; |
michael@0 | 869 | break; |
michael@0 | 870 | default: |
michael@0 | 871 | assert(false); |
michael@0 | 872 | break; |
michael@0 | 873 | } |
michael@0 | 874 | block.skipGroup = expression == 0; |
michael@0 | 875 | block.foundValidGroup = expression != 0; |
michael@0 | 876 | } |
michael@0 | 877 | mConditionalStack.push_back(block); |
michael@0 | 878 | } |
michael@0 | 879 | |
michael@0 | 880 | int DirectiveParser::parseExpressionIf(Token* token) |
michael@0 | 881 | { |
michael@0 | 882 | assert((getDirective(token) == DIRECTIVE_IF) || |
michael@0 | 883 | (getDirective(token) == DIRECTIVE_ELIF)); |
michael@0 | 884 | |
michael@0 | 885 | DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); |
michael@0 | 886 | MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); |
michael@0 | 887 | ExpressionParser expressionParser(¯oExpander, mDiagnostics); |
michael@0 | 888 | |
michael@0 | 889 | int expression = 0; |
michael@0 | 890 | macroExpander.lex(token); |
michael@0 | 891 | expressionParser.parse(token, &expression); |
michael@0 | 892 | |
michael@0 | 893 | // Warn if there are tokens after #if expression. |
michael@0 | 894 | if (!isEOD(token)) |
michael@0 | 895 | { |
michael@0 | 896 | mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
michael@0 | 897 | token->location, token->text); |
michael@0 | 898 | skipUntilEOD(mTokenizer, token); |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | return expression; |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | int DirectiveParser::parseExpressionIfdef(Token* token) |
michael@0 | 905 | { |
michael@0 | 906 | assert((getDirective(token) == DIRECTIVE_IFDEF) || |
michael@0 | 907 | (getDirective(token) == DIRECTIVE_IFNDEF)); |
michael@0 | 908 | |
michael@0 | 909 | mTokenizer->lex(token); |
michael@0 | 910 | if (token->type != Token::IDENTIFIER) |
michael@0 | 911 | { |
michael@0 | 912 | mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
michael@0 | 913 | token->location, token->text); |
michael@0 | 914 | skipUntilEOD(mTokenizer, token); |
michael@0 | 915 | return 0; |
michael@0 | 916 | } |
michael@0 | 917 | |
michael@0 | 918 | MacroSet::const_iterator iter = mMacroSet->find(token->text); |
michael@0 | 919 | int expression = iter != mMacroSet->end() ? 1 : 0; |
michael@0 | 920 | |
michael@0 | 921 | // Warn if there are tokens after #ifdef expression. |
michael@0 | 922 | mTokenizer->lex(token); |
michael@0 | 923 | if (!isEOD(token)) |
michael@0 | 924 | { |
michael@0 | 925 | mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
michael@0 | 926 | token->location, token->text); |
michael@0 | 927 | skipUntilEOD(mTokenizer, token); |
michael@0 | 928 | } |
michael@0 | 929 | return expression; |
michael@0 | 930 | } |
michael@0 | 931 | |
michael@0 | 932 | } // namespace pp |