blob: 20d7bb3f53124e9e8f7668e8a50e80a21c8b7c3d [file] [log] [blame]
John Kesseniche01a9bc2016-03-12 20:11:22 -07001//
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 Kessenichd016be12016-03-13 11:24:20 -060036//
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,
45// with all other work being farmed out to hlslParseHelper.cpp, which it turn
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 Kesseniche01a9bc2016-03-12 20:11:22 -070053#include "hlslTokens.h"
54#include "hlslGrammar.h"
55
56namespace glslang {
57
58// Root entry point to this recursive decent parser.
59// Return true if compilation unit was successfully accepted.
60bool HlslGrammar::parse()
61{
62 advanceToken();
63 return acceptCompilationUnit();
64}
65
66void HlslGrammar::expected(const char* syntax)
67{
68 parseContext.error(token.loc, "Expected", syntax, "");
69}
70
71// Load 'token' with the next token in the stream of tokens.
72void HlslGrammar::advanceToken()
73{
74 scanContext.tokenize(token);
75}
76
77// Return true and advance to the next token if the current token is the
78// expected (passed in) token class.
79bool HlslGrammar::acceptTokenClass(EHlslTokenClass tokenClass)
80{
81 if (token.tokenClass == tokenClass) {
82 advanceToken();
83 return true;
84 }
85
86 return false;
87}
88
89// compilationUnit
90// : list of externalDeclaration
91//
92bool HlslGrammar::acceptCompilationUnit()
93{
John Kessenichd016be12016-03-13 11:24:20 -060094 TIntermNode* unitNode = nullptr;
95
John Kesseniche01a9bc2016-03-12 20:11:22 -070096 while (token.tokenClass != EHTokNone) {
John Kessenichd016be12016-03-13 11:24:20 -060097 // externalDeclaration
98 TIntermNode* declarationNode;
99 if (! acceptDeclaration(declarationNode))
John Kesseniche01a9bc2016-03-12 20:11:22 -0700100 return false;
John Kessenichd016be12016-03-13 11:24:20 -0600101
102 // hook it up
103 unitNode = parseContext.intermediate.growAggregate(unitNode, declarationNode);
John Kesseniche01a9bc2016-03-12 20:11:22 -0700104 }
105
John Kessenichd016be12016-03-13 11:24:20 -0600106 // set root of AST
107 parseContext.intermediate.setTreeRoot(unitNode);
108
John Kesseniche01a9bc2016-03-12 20:11:22 -0700109 return true;
110}
111
112// declaration
John Kessenich87142c72016-03-12 20:24:24 -0700113// : SEMICOLON
114// : fully_specified_type ;
115// | fully_specified_type identifier ;
116// | fully_specified_type identifier = expression ;
117// | fully_specified_type identifier function_parameters ; // function prototype
118// | fully_specified_type function_parameters compound_statement // function definition
119//
John Kessenichd016be12016-03-13 11:24:20 -0600120// 'node' could get created if the declaration creates code, like an initializer
121// or a function body.
122//
123bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
John Kesseniche01a9bc2016-03-12 20:11:22 -0700124{
John Kessenichd016be12016-03-13 11:24:20 -0600125 node = nullptr;
126
John Kessenich87142c72016-03-12 20:24:24 -0700127 // fully_specified_type
128 TType type;
129 if (! acceptFullySpecifiedType(type))
130 return false;
131
132 // identifier
133 if (token.tokenClass == EHTokIdentifier) {
134 TSourceLoc declLoc = token.loc;
135 TString* declName = token.string;
136 advanceToken();
137
138 // = expression
139 TIntermTyped* expressionNode = nullptr;
140 if (acceptTokenClass(EHTokEqual)) {
141 if (! acceptExpression(expressionNode)) {
142 expected("initializer");
143 return false;
144 }
145 }
146
147 // ;
148 if (acceptTokenClass(EHTokSemicolon)) {
John Kessenichd016be12016-03-13 11:24:20 -0600149 node = parseContext.declareVariable(declLoc, *declName, type, 0, expressionNode);
John Kessenich87142c72016-03-12 20:24:24 -0700150 return true;
151 }
152 }
153
154 // no identifier, just ;
155 if (acceptTokenClass(EHTokSemicolon))
156 return true;
157
John Kesseniche01a9bc2016-03-12 20:11:22 -0700158 return true;
159}
160
John Kessenich87142c72016-03-12 20:24:24 -0700161// fully_specified_type
162// : type_specifier
163// | type_qualifier type_specifier
164//
165bool HlslGrammar::acceptFullySpecifiedType(TType& type)
166{
167 // type_qualifier
168 TQualifier qualifier;
169 qualifier.clear();
170 acceptQualifier(qualifier);
171
172 // type_specifier
173 if (! acceptType(type))
174 return false;
175 type.getQualifier() = qualifier;
176
177 return true;
178}
179
180// If token is a qualifier, return its token class and advance to the next
181// qualifier. Otherwise, return false, and don't advance.
182void HlslGrammar::acceptQualifier(TQualifier& qualifier)
183{
184 switch (token.tokenClass) {
185 case EHTokUniform:
186 qualifier.storage = EvqUniform;
187 break;
188 case EHTokConst:
189 qualifier.storage = EvqConst;
190 break;
191 default:
192 return;
193 }
194
195 advanceToken();
196}
197
198// If token is for a type, update 'type' with the type information,
199// and return true and advance.
200// Otherwise, return false, and don't advance
201bool HlslGrammar::acceptType(TType& type)
202{
203 if (! token.isType)
204 return false;
205
206 switch (token.tokenClass) {
207 case EHTokInt:
208 case EHTokInt1:
209 case EHTokDword:
210 new(&type) TType(EbtInt);
211 break;
212 case EHTokFloat:
213 case EHTokFloat1:
214 new(&type) TType(EbtFloat);
215 break;
216
217 case EHTokFloat2:
218 new(&type) TType(EbtFloat, EvqTemporary, 2);
219 break;
220 case EHTokFloat3:
221 new(&type) TType(EbtFloat, EvqTemporary, 3);
222 break;
223 case EHTokFloat4:
224 new(&type) TType(EbtFloat, EvqTemporary, 4);
225 break;
226
227 case EHTokInt2:
228 new(&type) TType(EbtInt, EvqTemporary, 2);
229 break;
230 case EHTokInt3:
231 new(&type) TType(EbtInt, EvqTemporary, 3);
232 break;
233 case EHTokInt4:
234 new(&type) TType(EbtInt, EvqTemporary, 4);
235 break;
236
237 case EHTokBool2:
238 new(&type) TType(EbtBool, EvqTemporary, 2);
239 break;
240 case EHTokBool3:
241 new(&type) TType(EbtBool, EvqTemporary, 3);
242 break;
243 case EHTokBool4:
244 new(&type) TType(EbtBool, EvqTemporary, 4);
245 break;
246
247 case EHTokFloat2x2:
248 new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2);
249 break;
250 case EHTokFloat2x3:
251 new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2);
252 break;
253 case EHTokFloat2x4:
254 new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2);
255 break;
256 case EHTokFloat3x2:
257 new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3);
258 break;
259 case EHTokFloat3x3:
260 new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3);
261 break;
262 case EHTokFloat3x4:
263 new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3);
264 break;
265 case EHTokFloat4x2:
266 new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4);
267 break;
268 case EHTokFloat4x3:
269 new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4);
270 break;
271 case EHTokFloat4x4:
272 new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4);
273 break;
274
275 default:
276 return false;
277 }
278
279 advanceToken();
280
281 return true;
282}
283
284// expression
285// : identifier
286// | ( expression )
John Kessenichd016be12016-03-13 11:24:20 -0600287// | type(...) // constructor
John Kessenich87142c72016-03-12 20:24:24 -0700288// | literal
John Kessenichd016be12016-03-13 11:24:20 -0600289// | identifier operator identifier // to be generalized to all expressions
John Kessenich87142c72016-03-12 20:24:24 -0700290//
291bool HlslGrammar::acceptExpression(TIntermTyped*& node)
292{
293 // identifier
294 if (token.tokenClass == EHTokIdentifier) {
295 node = parseContext.handleVariable(token.loc, token.symbol, token.string);
296 return true;
297 }
298
299 // ( expression )
300 if (acceptTokenClass(EHTokLeftParen)) {
301 if (! acceptExpression(node)) {
302 expected("expression");
303 return false;
304 }
305 if (! acceptTokenClass(EHTokRightParen)) {
306 expected("right parenthesis");
307 return false;
308 }
309
310 return true;
311 }
312
313 // literal
314 if (acceptLiteral(node))
315 return true;
316
John Kessenichd016be12016-03-13 11:24:20 -0600317 // type(...) // constructor
318 if (acceptConstructor(node))
John Kessenich87142c72016-03-12 20:24:24 -0700319 return true;
John Kessenich87142c72016-03-12 20:24:24 -0700320
John Kessenichd016be12016-03-13 11:24:20 -0600321 // identifier operator identifier
John Kessenich87142c72016-03-12 20:24:24 -0700322 if (token.tokenClass == EHTokIdentifier) {
323 TIntermTyped* left = parseContext.handleVariable(token.loc, token.symbol, token.string);
324 advanceToken();
325
326 // operator
327 TOperator op;
328 if (! acceptOperator(op))
329 return false;
330 TSourceLoc loc = token.loc;
331
332 // right
333 if (token.tokenClass == EHTokIdentifier) {
334 TIntermTyped* right = parseContext.handleVariable(token.loc, token.symbol, token.string);
335 advanceToken();
336 node = parseContext.intermediate.addBinaryMath(op, left, right, loc);
337 } else
338 return false;
339 } else
340 return false;
341
342 return true;
343}
344
John Kessenichd016be12016-03-13 11:24:20 -0600345// constructor
346// : type arguments
347//
348bool HlslGrammar::acceptConstructor(TIntermTyped*& node)
349{
350 // type
351 TType type;
352 if (acceptType(type)) {
353 TFunction* constructorFunction = parseContext.handleConstructorCall(token.loc, type);
354 if (constructorFunction == nullptr)
355 return false;
356
357 // arguments
358 TIntermAggregate* arguments = nullptr;
359 if (! acceptArguments(constructorFunction, arguments)) {
360 expected("constructor arguments");
361 return false;
362 }
363
364 // hook it up
365 node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments);
366
367 return true;
368 }
369
370 return false;
371}
372
John Kessenich87142c72016-03-12 20:24:24 -0700373// arguments
374// : ( expression , expression, ... )
375//
John Kessenichd016be12016-03-13 11:24:20 -0600376// The arguments are pushed onto the 'function' argument list and
377// onto the 'arguments' aggregate.
378//
379bool HlslGrammar::acceptArguments(TFunction* function, TIntermAggregate*& arguments)
John Kessenich87142c72016-03-12 20:24:24 -0700380{
John Kessenichd016be12016-03-13 11:24:20 -0600381 // (
John Kessenich87142c72016-03-12 20:24:24 -0700382 if (! acceptTokenClass(EHTokLeftParen))
383 return false;
384
385 do {
John Kessenichd016be12016-03-13 11:24:20 -0600386 // expression
John Kessenich87142c72016-03-12 20:24:24 -0700387 TIntermTyped* arg;
388 if (! acceptExpression(arg))
389 break;
John Kessenichd016be12016-03-13 11:24:20 -0600390
391 // hook it up
392 parseContext.handleFunctionArgument(function, arguments, arg);
393
394 // ,
John Kessenich87142c72016-03-12 20:24:24 -0700395 if (! acceptTokenClass(EHTokComma))
396 break;
397 } while (true);
398
John Kessenichd016be12016-03-13 11:24:20 -0600399 // )
John Kessenich87142c72016-03-12 20:24:24 -0700400 if (! acceptTokenClass(EHTokRightParen)) {
401 expected("right parenthesis");
402 return false;
403 }
404
405 return true;
406}
407
408bool HlslGrammar::acceptLiteral(TIntermTyped*& node)
409{
410 switch (token.tokenClass) {
411 case EHTokIntConstant:
412 node = parseContext.intermediate.addConstantUnion(token.i, token.loc, true);
413 break;
414 case EHTokFloatConstant:
415 node = parseContext.intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true);
416 break;
417 case EHTokDoubleConstant:
418 node = parseContext.intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true);
419 break;
420 case EHTokBoolConstant:
421 node = parseContext.intermediate.addConstantUnion(token.b, token.loc, true);
422 break;
423
424 default:
425 return false;
426 }
427
428 advanceToken();
429
430 return true;
431}
432
433bool HlslGrammar::acceptOperator(TOperator& op)
434{
435 switch (token.tokenClass) {
436 case EHTokPlus:
437 op = EOpAdd;
438 break;
439 default:
440 return false;
441 }
442
443 advanceToken();
444
445 return true;
446}
447
448bool HlslGrammar::acceptCompoundStatement()
449{
450 return false;
451}
452
John Kesseniche01a9bc2016-03-12 20:11:22 -0700453} // end namespace glslang