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