John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 1 | // |
| 2 | //Copyright (C) 2016 Google, Inc. |
| 3 | // |
| 4 | //All rights reserved. |
| 5 | // |
| 6 | //Redistribution and use in source and binary forms, with or without |
| 7 | //modification, are permitted provided that the following conditions |
| 8 | //are met: |
| 9 | // |
| 10 | // Redistributions of source code must retain the above copyright |
| 11 | // notice, this list of conditions and the following disclaimer. |
| 12 | // |
| 13 | // Redistributions in binary form must reproduce the above |
| 14 | // copyright notice, this list of conditions and the following |
| 15 | // disclaimer in the documentation and/or other materials provided |
| 16 | // with the distribution. |
| 17 | // |
| 18 | // Neither the name of Google, Inc., nor the names of its |
| 19 | // contributors may be used to endorse or promote products derived |
| 20 | // from this software without specific prior written permission. |
| 21 | // |
| 22 | //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 23 | //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 24 | //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 25 | //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| 26 | //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 27 | //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| 28 | //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 29 | //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 30 | //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 31 | //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| 32 | //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 33 | //POSSIBILITY OF SUCH DAMAGE. |
| 34 | // |
| 35 | |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 36 | // |
| 37 | // This is a set of mutually recursive methods implementing the HLSL grammar. |
| 38 | // Generally, each returns |
| 39 | // - through an argument: a type specifically appropriate to which rule it |
| 40 | // recognized |
| 41 | // - through the return value: true/false to indicate whether or not it |
| 42 | // recognized its rule |
| 43 | // |
| 44 | // As much as possible, only grammar recognition should happen in this file, |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 45 | // with all other work being farmed out to hlslParseHelper.cpp, which in turn |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 46 | // will build the AST. |
| 47 | // |
| 48 | // The next token, yet to be "accepted" is always sitting in 'token'. |
| 49 | // When a method says it accepts a rule, that means all tokens involved |
| 50 | // in the rule will have been consumed, and none left in 'token'. |
| 51 | // |
| 52 | |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 53 | #include "hlslTokens.h" |
| 54 | #include "hlslGrammar.h" |
| 55 | |
| 56 | namespace glslang { |
| 57 | |
| 58 | // Root entry point to this recursive decent parser. |
| 59 | // Return true if compilation unit was successfully accepted. |
| 60 | bool HlslGrammar::parse() |
| 61 | { |
| 62 | advanceToken(); |
| 63 | return acceptCompilationUnit(); |
| 64 | } |
| 65 | |
| 66 | void HlslGrammar::expected(const char* syntax) |
| 67 | { |
| 68 | parseContext.error(token.loc, "Expected", syntax, ""); |
| 69 | } |
| 70 | |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 71 | // Only process the next token if it is an identifier. |
| 72 | // Return true if it was an identifier. |
| 73 | bool HlslGrammar::acceptIdentifier(HlslToken& idToken) |
| 74 | { |
| 75 | if (peekTokenClass(EHTokIdentifier)) { |
| 76 | idToken = token; |
| 77 | advanceToken(); |
| 78 | return true; |
| 79 | } |
| 80 | |
| 81 | return false; |
| 82 | } |
| 83 | |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 84 | // compilationUnit |
| 85 | // : list of externalDeclaration |
| 86 | // |
| 87 | bool HlslGrammar::acceptCompilationUnit() |
| 88 | { |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 89 | TIntermNode* unitNode = nullptr; |
| 90 | |
John Kessenich | 9c86c6a | 2016-05-03 22:49:24 -0600 | [diff] [blame] | 91 | while (! peekTokenClass(EHTokNone)) { |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 92 | // externalDeclaration |
| 93 | TIntermNode* declarationNode; |
| 94 | if (! acceptDeclaration(declarationNode)) |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 95 | return false; |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 96 | |
| 97 | // hook it up |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 98 | unitNode = intermediate.growAggregate(unitNode, declarationNode); |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 99 | } |
| 100 | |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 101 | // set root of AST |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 102 | intermediate.setTreeRoot(unitNode); |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 103 | |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 104 | return true; |
| 105 | } |
| 106 | |
| 107 | // declaration |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 108 | // : SEMICOLON |
| 109 | // : fully_specified_type SEMICOLON |
| 110 | // | fully_specified_type identifier SEMICOLON |
| 111 | // | fully_specified_type identifier = expression SEMICOLON |
| 112 | // | fully_specified_type identifier function_parameters SEMICOLON // function prototype |
| 113 | // | fully_specified_type identifier function_parameters COLON semantic compound_statement // function definition |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 114 | // |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 115 | // 'node' could get created if the declaration creates code, like an initializer |
| 116 | // or a function body. |
| 117 | // |
| 118 | bool HlslGrammar::acceptDeclaration(TIntermNode*& node) |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 119 | { |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 120 | node = nullptr; |
| 121 | |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 122 | // fully_specified_type |
| 123 | TType type; |
| 124 | if (! acceptFullySpecifiedType(type)) |
| 125 | return false; |
| 126 | |
| 127 | // identifier |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 128 | HlslToken idToken; |
| 129 | if (acceptIdentifier(idToken)) { |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 130 | // = expression |
| 131 | TIntermTyped* expressionNode = nullptr; |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 132 | if (acceptTokenClass(EHTokAssign)) { |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 133 | if (! acceptExpression(expressionNode)) { |
| 134 | expected("initializer"); |
| 135 | return false; |
| 136 | } |
| 137 | } |
| 138 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 139 | // SEMICOLON |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 140 | if (acceptTokenClass(EHTokSemicolon)) { |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 141 | node = parseContext.declareVariable(idToken.loc, *idToken.string, type, 0, expressionNode); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 142 | return true; |
| 143 | } |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 144 | |
| 145 | // function_parameters |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 146 | TFunction* function = new TFunction(idToken.string, type); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 147 | if (acceptFunctionParameters(*function)) { |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 148 | // COLON semantic |
| 149 | acceptSemantic(); |
| 150 | |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 151 | // compound_statement |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 152 | if (peekTokenClass(EHTokLeftBrace)) |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 153 | return acceptFunctionDefinition(*function, node); |
| 154 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 155 | // SEMICOLON |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 156 | if (acceptTokenClass(EHTokSemicolon)) |
| 157 | return true; |
| 158 | |
| 159 | return false; |
| 160 | } |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 161 | } |
| 162 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 163 | // SEMICOLON |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 164 | if (acceptTokenClass(EHTokSemicolon)) |
| 165 | return true; |
| 166 | |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 167 | return true; |
| 168 | } |
| 169 | |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 170 | // fully_specified_type |
| 171 | // : type_specifier |
| 172 | // | type_qualifier type_specifier |
| 173 | // |
| 174 | bool HlslGrammar::acceptFullySpecifiedType(TType& type) |
| 175 | { |
| 176 | // type_qualifier |
| 177 | TQualifier qualifier; |
| 178 | qualifier.clear(); |
| 179 | acceptQualifier(qualifier); |
| 180 | |
| 181 | // type_specifier |
| 182 | if (! acceptType(type)) |
| 183 | return false; |
| 184 | type.getQualifier() = qualifier; |
| 185 | |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | // If token is a qualifier, return its token class and advance to the next |
| 190 | // qualifier. Otherwise, return false, and don't advance. |
| 191 | void HlslGrammar::acceptQualifier(TQualifier& qualifier) |
| 192 | { |
John Kessenich | 9c86c6a | 2016-05-03 22:49:24 -0600 | [diff] [blame] | 193 | switch (peek()) { |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 194 | case EHTokUniform: |
| 195 | qualifier.storage = EvqUniform; |
| 196 | break; |
| 197 | case EHTokConst: |
| 198 | qualifier.storage = EvqConst; |
| 199 | break; |
| 200 | default: |
| 201 | return; |
| 202 | } |
| 203 | |
| 204 | advanceToken(); |
| 205 | } |
| 206 | |
| 207 | // If token is for a type, update 'type' with the type information, |
| 208 | // and return true and advance. |
| 209 | // Otherwise, return false, and don't advance |
| 210 | bool HlslGrammar::acceptType(TType& type) |
| 211 | { |
| 212 | if (! token.isType) |
| 213 | return false; |
| 214 | |
John Kessenich | 9c86c6a | 2016-05-03 22:49:24 -0600 | [diff] [blame] | 215 | switch (peek()) { |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 216 | case EHTokInt: |
| 217 | case EHTokInt1: |
| 218 | case EHTokDword: |
| 219 | new(&type) TType(EbtInt); |
| 220 | break; |
| 221 | case EHTokFloat: |
| 222 | case EHTokFloat1: |
| 223 | new(&type) TType(EbtFloat); |
| 224 | break; |
| 225 | |
| 226 | case EHTokFloat2: |
| 227 | new(&type) TType(EbtFloat, EvqTemporary, 2); |
| 228 | break; |
| 229 | case EHTokFloat3: |
| 230 | new(&type) TType(EbtFloat, EvqTemporary, 3); |
| 231 | break; |
| 232 | case EHTokFloat4: |
| 233 | new(&type) TType(EbtFloat, EvqTemporary, 4); |
| 234 | break; |
| 235 | |
| 236 | case EHTokInt2: |
| 237 | new(&type) TType(EbtInt, EvqTemporary, 2); |
| 238 | break; |
| 239 | case EHTokInt3: |
| 240 | new(&type) TType(EbtInt, EvqTemporary, 3); |
| 241 | break; |
| 242 | case EHTokInt4: |
| 243 | new(&type) TType(EbtInt, EvqTemporary, 4); |
| 244 | break; |
| 245 | |
| 246 | case EHTokBool2: |
| 247 | new(&type) TType(EbtBool, EvqTemporary, 2); |
| 248 | break; |
| 249 | case EHTokBool3: |
| 250 | new(&type) TType(EbtBool, EvqTemporary, 3); |
| 251 | break; |
| 252 | case EHTokBool4: |
| 253 | new(&type) TType(EbtBool, EvqTemporary, 4); |
| 254 | break; |
| 255 | |
| 256 | case EHTokFloat2x2: |
| 257 | new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2); |
| 258 | break; |
| 259 | case EHTokFloat2x3: |
| 260 | new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2); |
| 261 | break; |
| 262 | case EHTokFloat2x4: |
| 263 | new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2); |
| 264 | break; |
| 265 | case EHTokFloat3x2: |
| 266 | new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3); |
| 267 | break; |
| 268 | case EHTokFloat3x3: |
| 269 | new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3); |
| 270 | break; |
| 271 | case EHTokFloat3x4: |
| 272 | new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3); |
| 273 | break; |
| 274 | case EHTokFloat4x2: |
| 275 | new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4); |
| 276 | break; |
| 277 | case EHTokFloat4x3: |
| 278 | new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4); |
| 279 | break; |
| 280 | case EHTokFloat4x4: |
| 281 | new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); |
| 282 | break; |
| 283 | |
| 284 | default: |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | advanceToken(); |
| 289 | |
| 290 | return true; |
| 291 | } |
| 292 | |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 293 | // function_parameters |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 294 | // : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 295 | // |
| 296 | bool HlslGrammar::acceptFunctionParameters(TFunction& function) |
| 297 | { |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 298 | // LEFT_PAREN |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 299 | if (! acceptTokenClass(EHTokLeftParen)) |
| 300 | return false; |
| 301 | |
| 302 | do { |
| 303 | // parameter_declaration |
| 304 | if (! acceptParameterDeclaration(function)) |
| 305 | break; |
| 306 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 307 | // COMMA |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 308 | if (! acceptTokenClass(EHTokComma)) |
| 309 | break; |
| 310 | } while (true); |
| 311 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 312 | // RIGHT_PAREN |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 313 | if (! acceptTokenClass(EHTokRightParen)) { |
| 314 | expected("right parenthesis"); |
| 315 | return false; |
| 316 | } |
| 317 | |
| 318 | return true; |
| 319 | } |
| 320 | |
| 321 | // parameter_declaration |
| 322 | // : fully_specified_type |
| 323 | // | fully_specified_type identifier |
| 324 | // |
| 325 | bool HlslGrammar::acceptParameterDeclaration(TFunction& function) |
| 326 | { |
| 327 | // fully_specified_type |
| 328 | TType* type = new TType; |
| 329 | if (! acceptFullySpecifiedType(*type)) |
| 330 | return false; |
| 331 | |
| 332 | // identifier |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 333 | HlslToken idToken; |
| 334 | acceptIdentifier(idToken); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 335 | |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 336 | TParameter param = { idToken.string, type }; |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 337 | function.addParameter(param); |
| 338 | |
| 339 | return true; |
| 340 | } |
| 341 | |
| 342 | // Do the work to create the function definition in addition to |
| 343 | // parsing the body (compound_statement). |
| 344 | bool HlslGrammar::acceptFunctionDefinition(TFunction& function, TIntermNode*& node) |
| 345 | { |
| 346 | TFunction* functionDeclarator = parseContext.handleFunctionDeclarator(token.loc, function, false /* not prototype */); |
| 347 | |
| 348 | // This does a symbol table push |
| 349 | node = parseContext.handleFunctionDefinition(token.loc, *functionDeclarator); |
| 350 | |
| 351 | // compound_statement |
| 352 | TIntermAggregate* functionBody = nullptr; |
| 353 | if (acceptCompoundStatement(functionBody)) { |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 354 | node = intermediate.growAggregate(node, functionBody); |
| 355 | intermediate.setAggregateOperator(node, EOpFunction, functionDeclarator->getType(), token.loc); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 356 | node->getAsAggregate()->setName(functionDeclarator->getMangledName().c_str()); |
| 357 | parseContext.symbolTable.pop(nullptr); |
| 358 | |
| 359 | return true; |
| 360 | } |
| 361 | |
| 362 | return false; |
| 363 | } |
| 364 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 365 | // The top-level full expression recognizer. |
| 366 | // |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 367 | // expression |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 368 | // : assignment_expression COMMA assignment_expression COMMA assignment_expression ... |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 369 | // |
| 370 | bool HlslGrammar::acceptExpression(TIntermTyped*& node) |
| 371 | { |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 372 | // assignment_expression |
| 373 | if (! acceptAssignmentExpression(node)) |
| 374 | return false; |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 375 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 376 | if (! peekTokenClass(EHTokComma)) |
| 377 | return true; |
| 378 | |
| 379 | do { |
| 380 | // ... COMMA |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 381 | TSourceLoc loc = token.loc; |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 382 | advanceToken(); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 383 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 384 | // ... assignment_expression |
| 385 | TIntermTyped* rightNode = nullptr; |
| 386 | if (! acceptAssignmentExpression(rightNode)) { |
| 387 | expected("assignment expression"); |
| 388 | return false; |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 389 | } |
| 390 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 391 | node = intermediate.addComma(node, rightNode, loc); |
| 392 | |
| 393 | if (! peekTokenClass(EHTokComma)) |
| 394 | return true; |
| 395 | } while (true); |
| 396 | } |
| 397 | |
| 398 | // Accept an assignment expression, where assignment operations |
| 399 | // associate right-to-left. This is, it is implicit, for example |
| 400 | // |
| 401 | // a op (b op (c op d)) |
| 402 | // |
| 403 | // assigment_expression |
| 404 | // : binary_expression op binary_expression op binary_expression ... |
| 405 | // |
| 406 | bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node) |
| 407 | { |
| 408 | if (! acceptBinaryExpression(node, PlLogicalOr)) |
| 409 | return false; |
| 410 | |
| 411 | TOperator assignOp = HlslOpMap::assignment(peek()); |
| 412 | if (assignOp == EOpNull) |
| 413 | return true; |
| 414 | |
| 415 | // ... op |
| 416 | TSourceLoc loc = token.loc; |
| 417 | advanceToken(); |
| 418 | |
| 419 | // ... binary_expression |
| 420 | // But, done by recursing this function, which automatically |
| 421 | // gets the right-to-left associativity. |
| 422 | TIntermTyped* rightNode = nullptr; |
| 423 | if (! acceptAssignmentExpression(rightNode)) { |
| 424 | expected("assignment expression"); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 425 | return false; |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 426 | } |
| 427 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 428 | node = intermediate.addAssign(assignOp, node, rightNode, loc); |
| 429 | |
| 430 | if (! peekTokenClass(EHTokComma)) |
| 431 | return true; |
| 432 | |
| 433 | return true; |
| 434 | } |
| 435 | |
| 436 | // Accept a binary expression, for binary operations that |
| 437 | // associate left-to-right. This is, it is implicit, for example |
| 438 | // |
| 439 | // ((a op b) op c) op d |
| 440 | // |
| 441 | // binary_expression |
| 442 | // : expression op expression op expression ... |
| 443 | // |
| 444 | // where 'expression' is the next higher level in precedence. |
| 445 | // |
| 446 | bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel precedenceLevel) |
| 447 | { |
| 448 | if (precedenceLevel > PlMul) |
| 449 | return acceptUnaryExpression(node); |
| 450 | |
| 451 | // assignment_expression |
| 452 | if (! acceptBinaryExpression(node, (PrecedenceLevel)(precedenceLevel + 1))) |
| 453 | return false; |
| 454 | |
| 455 | TOperator op = HlslOpMap::binary(peek()); |
| 456 | PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op); |
| 457 | if (tokenLevel < precedenceLevel) |
| 458 | return true; |
| 459 | |
| 460 | do { |
| 461 | // ... op |
| 462 | TSourceLoc loc = token.loc; |
| 463 | advanceToken(); |
| 464 | |
| 465 | // ... expression |
| 466 | TIntermTyped* rightNode = nullptr; |
| 467 | if (! acceptBinaryExpression(rightNode, (PrecedenceLevel)(precedenceLevel + 1))) { |
| 468 | expected("expression"); |
| 469 | return false; |
| 470 | } |
| 471 | |
| 472 | node = intermediate.addBinaryMath(op, node, rightNode, loc); |
| 473 | |
| 474 | if (! peekTokenClass(EHTokComma)) |
| 475 | return true; |
| 476 | } while (true); |
| 477 | } |
| 478 | |
| 479 | // unary_expression |
| 480 | // : + unary_expression |
| 481 | // | - unary_expression |
| 482 | // | ! unary_expression |
| 483 | // | ~ unary_expression |
| 484 | // | ++ unary_expression |
| 485 | // | -- unary_expression |
| 486 | // | postfix_expression |
| 487 | // |
| 488 | bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) |
| 489 | { |
| 490 | TOperator unaryOp = HlslOpMap::preUnary(peek()); |
| 491 | |
| 492 | // postfix_expression |
| 493 | if (unaryOp == EOpNull) |
| 494 | return acceptPostfixExpression(node); |
| 495 | |
| 496 | // op unary_expression |
| 497 | TSourceLoc loc = token.loc; |
| 498 | advanceToken(); |
| 499 | if (! acceptUnaryExpression(node)) |
| 500 | return false; |
| 501 | |
| 502 | // + is a no-op |
| 503 | if (unaryOp == EOpAdd) |
| 504 | return true; |
| 505 | |
| 506 | node = intermediate.addUnaryMath(unaryOp, node, loc); |
| 507 | |
| 508 | return node != nullptr; |
| 509 | } |
| 510 | |
| 511 | // postfix_expression |
| 512 | // : LEFT_PAREN expression RIGHT_PAREN |
| 513 | // | literal |
| 514 | // | constructor |
| 515 | // | identifier |
| 516 | // | function_call |
| 517 | // | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET |
| 518 | // | postfix_expression DOT IDENTIFIER |
| 519 | // | postfix_expression INC_OP |
| 520 | // | postfix_expression DEC_OP |
| 521 | // |
| 522 | bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) |
| 523 | { |
| 524 | // Not implemented as self-recursive: |
| 525 | // The logical "right recursion" is done with an loop at the end |
| 526 | |
| 527 | // idToken will pick up either a variable or a function name in a function call |
| 528 | HlslToken idToken; |
| 529 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 530 | // LEFT_PAREN expression RIGHT_PAREN |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 531 | if (acceptTokenClass(EHTokLeftParen)) { |
| 532 | if (! acceptExpression(node)) { |
| 533 | expected("expression"); |
| 534 | return false; |
| 535 | } |
| 536 | if (! acceptTokenClass(EHTokRightParen)) { |
| 537 | expected("right parenthesis"); |
| 538 | return false; |
| 539 | } |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 540 | } else if (acceptLiteral(node)) { |
| 541 | // literal (nothing else to do yet), go on to the |
| 542 | } else if (acceptConstructor(node)) { |
| 543 | // constructor (nothing else to do yet) |
| 544 | } else if (acceptIdentifier(idToken)) { |
| 545 | // identifier or function_call name |
| 546 | if (! peekTokenClass(EHTokLeftParen)) { |
| 547 | node = parseContext.handleVariable(idToken.loc, idToken.symbol, token.string); |
| 548 | } else if (acceptFunctionCall(idToken, node)) { |
| 549 | // function_call (nothing else to do yet) |
| 550 | } else { |
| 551 | expected("function call arguments"); |
| 552 | return false; |
| 553 | } |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 554 | } |
| 555 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 556 | do { |
| 557 | TSourceLoc loc = token.loc; |
| 558 | TOperator postOp = HlslOpMap::postUnary(peek()); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 559 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 560 | // Consume only a valid post-unary operator, otherwise we are done. |
| 561 | switch (postOp) { |
| 562 | case EOpIndexDirectStruct: |
| 563 | case EOpIndexIndirect: |
| 564 | case EOpPostIncrement: |
| 565 | case EOpPostDecrement: |
| 566 | advanceToken(); |
| 567 | break; |
| 568 | default: |
| 569 | return true; |
| 570 | } |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 571 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 572 | // We have a valid post-unary operator, process it. |
| 573 | switch (postOp) { |
| 574 | case EOpIndexDirectStruct: |
| 575 | // todo |
| 576 | break; |
| 577 | case EOpIndexIndirect: |
| 578 | { |
| 579 | TIntermTyped* indexNode = nullptr; |
| 580 | if (! acceptExpression(indexNode) || |
| 581 | ! peekTokenClass(EHTokRightBracket)) { |
| 582 | expected("expression followed by ']'"); |
| 583 | return false; |
| 584 | } |
| 585 | // todo: node = intermediate.addBinaryMath( |
| 586 | } |
| 587 | case EOpPostIncrement: |
| 588 | case EOpPostDecrement: |
| 589 | node = intermediate.addUnaryMath(postOp, node, loc); |
| 590 | break; |
| 591 | default: |
| 592 | assert(0); |
| 593 | break; |
| 594 | } |
| 595 | } while (true); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 596 | } |
| 597 | |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 598 | // constructor |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 599 | // : type argument_list |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 600 | // |
| 601 | bool HlslGrammar::acceptConstructor(TIntermTyped*& node) |
| 602 | { |
| 603 | // type |
| 604 | TType type; |
| 605 | if (acceptType(type)) { |
| 606 | TFunction* constructorFunction = parseContext.handleConstructorCall(token.loc, type); |
| 607 | if (constructorFunction == nullptr) |
| 608 | return false; |
| 609 | |
| 610 | // arguments |
John Kessenich | 4678ca9 | 2016-05-13 09:33:42 -0600 | [diff] [blame] | 611 | TIntermTyped* arguments = nullptr; |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 612 | if (! acceptArguments(constructorFunction, arguments)) { |
| 613 | expected("constructor arguments"); |
| 614 | return false; |
| 615 | } |
| 616 | |
| 617 | // hook it up |
| 618 | node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments); |
| 619 | |
| 620 | return true; |
| 621 | } |
| 622 | |
| 623 | return false; |
| 624 | } |
| 625 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 626 | // The function_call identifier was already recognized, and passed in as idToken. |
| 627 | // |
| 628 | // function_call |
| 629 | // : [idToken] arguments |
| 630 | // |
John Kessenich | 4678ca9 | 2016-05-13 09:33:42 -0600 | [diff] [blame] | 631 | bool HlslGrammar::acceptFunctionCall(HlslToken idToken, TIntermTyped*& node) |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 632 | { |
John Kessenich | 4678ca9 | 2016-05-13 09:33:42 -0600 | [diff] [blame] | 633 | // arguments |
| 634 | TFunction* function = new TFunction(idToken.string, TType(EbtVoid)); |
| 635 | TIntermTyped* arguments = nullptr; |
| 636 | if (! acceptArguments(function, arguments)) |
| 637 | return false; |
| 638 | |
| 639 | node = parseContext.handleFunctionCall(idToken.loc, function, arguments); |
| 640 | |
| 641 | return true; |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 642 | } |
| 643 | |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 644 | // arguments |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 645 | // : LEFT_PAREN expression COMMA expression COMMA ... RIGHT_PAREN |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 646 | // |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 647 | // The arguments are pushed onto the 'function' argument list and |
| 648 | // onto the 'arguments' aggregate. |
| 649 | // |
John Kessenich | 4678ca9 | 2016-05-13 09:33:42 -0600 | [diff] [blame] | 650 | bool HlslGrammar::acceptArguments(TFunction* function, TIntermTyped*& arguments) |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 651 | { |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 652 | // LEFT_PAREN |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 653 | if (! acceptTokenClass(EHTokLeftParen)) |
| 654 | return false; |
| 655 | |
| 656 | do { |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 657 | // expression |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 658 | TIntermTyped* arg; |
John Kessenich | 4678ca9 | 2016-05-13 09:33:42 -0600 | [diff] [blame] | 659 | if (! acceptAssignmentExpression(arg)) |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 660 | break; |
John Kessenich | d016be1 | 2016-03-13 11:24:20 -0600 | [diff] [blame] | 661 | |
| 662 | // hook it up |
| 663 | parseContext.handleFunctionArgument(function, arguments, arg); |
| 664 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 665 | // COMMA |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 666 | if (! acceptTokenClass(EHTokComma)) |
| 667 | break; |
| 668 | } while (true); |
| 669 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 670 | // RIGHT_PAREN |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 671 | if (! acceptTokenClass(EHTokRightParen)) { |
| 672 | expected("right parenthesis"); |
| 673 | return false; |
| 674 | } |
| 675 | |
| 676 | return true; |
| 677 | } |
| 678 | |
| 679 | bool HlslGrammar::acceptLiteral(TIntermTyped*& node) |
| 680 | { |
| 681 | switch (token.tokenClass) { |
| 682 | case EHTokIntConstant: |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 683 | node = intermediate.addConstantUnion(token.i, token.loc, true); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 684 | break; |
| 685 | case EHTokFloatConstant: |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 686 | node = intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 687 | break; |
| 688 | case EHTokDoubleConstant: |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 689 | node = intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 690 | break; |
| 691 | case EHTokBoolConstant: |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 692 | node = intermediate.addConstantUnion(token.b, token.loc, true); |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 693 | break; |
| 694 | |
| 695 | default: |
| 696 | return false; |
| 697 | } |
| 698 | |
| 699 | advanceToken(); |
| 700 | |
| 701 | return true; |
| 702 | } |
| 703 | |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 704 | // compound_statement |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 705 | // : LEFT_CURLY statement statement ... RIGHT_CURLY |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 706 | // |
| 707 | bool HlslGrammar::acceptCompoundStatement(TIntermAggregate*& compoundStatement) |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 708 | { |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 709 | // LEFT_CURLY |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 710 | if (! acceptTokenClass(EHTokLeftBrace)) |
| 711 | return false; |
| 712 | |
| 713 | // statement statement ... |
| 714 | TIntermNode* statement = nullptr; |
| 715 | while (acceptStatement(statement)) { |
| 716 | // hook it up |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 717 | compoundStatement = intermediate.growAggregate(compoundStatement, statement); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 718 | } |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 719 | if (compoundStatement) |
| 720 | compoundStatement->setOperator(EOpSequence); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 721 | |
John Kessenich | 34fb036 | 2016-05-03 23:17:20 -0600 | [diff] [blame] | 722 | // RIGHT_CURLY |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 723 | return acceptTokenClass(EHTokRightBrace); |
| 724 | } |
| 725 | |
| 726 | // statement |
| 727 | // : compound_statement |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 728 | // | return SEMICOLON |
| 729 | // | return expression SEMICOLON |
| 730 | // | expression SEMICOLON |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 731 | // |
| 732 | bool HlslGrammar::acceptStatement(TIntermNode*& statement) |
| 733 | { |
| 734 | // compound_statement |
| 735 | TIntermAggregate* compoundStatement = nullptr; |
| 736 | if (acceptCompoundStatement(compoundStatement)) { |
| 737 | statement = compoundStatement; |
| 738 | return true; |
| 739 | } |
| 740 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 741 | // RETURN |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 742 | if (acceptTokenClass(EHTokReturn)) { |
| 743 | // expression |
| 744 | TIntermTyped* node; |
| 745 | if (acceptExpression(node)) { |
| 746 | // hook it up |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 747 | statement = intermediate.addBranch(EOpReturn, node, token.loc); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 748 | } else |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 749 | statement = intermediate.addBranch(EOpReturn, token.loc); |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 750 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 751 | // SEMICOLON |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 752 | if (! acceptTokenClass(EHTokSemicolon)) |
| 753 | return false; |
| 754 | |
| 755 | return true; |
| 756 | } |
| 757 | |
| 758 | // expression |
| 759 | TIntermTyped* node; |
| 760 | if (acceptExpression(node)) |
| 761 | statement = node; |
| 762 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 763 | // SEMICOLON |
John Kessenich | 5f934b0 | 2016-03-13 17:58:25 -0600 | [diff] [blame] | 764 | if (! acceptTokenClass(EHTokSemicolon)) |
| 765 | return false; |
| 766 | |
| 767 | return true; |
John Kessenich | 87142c7 | 2016-03-12 20:24:24 -0700 | [diff] [blame] | 768 | } |
| 769 | |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 770 | // COLON semantic |
| 771 | bool HlslGrammar::acceptSemantic() |
| 772 | { |
| 773 | // COLON |
| 774 | if (acceptTokenClass(EHTokColon)) { |
| 775 | // semantic |
John Kessenich | aecd497 | 2016-03-14 10:46:34 -0600 | [diff] [blame] | 776 | HlslToken idToken; |
| 777 | if (! acceptIdentifier(idToken)) { |
John Kessenich | 078d7f2 | 2016-03-14 10:02:11 -0600 | [diff] [blame] | 778 | expected("semantic"); |
| 779 | return false; |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | return true; |
| 784 | } |
| 785 | |
John Kessenich | e01a9bc | 2016-03-12 20:11:22 -0700 | [diff] [blame] | 786 | } // end namespace glslang |