1 /*
2 * Copyright 2021 Google LLC.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/sksl/SkSLDSLParser.h"
9
10 #include "include/private/SkSLString.h"
11 #include "src/sksl/SkSLCompiler.h"
12 #include "src/sksl/SkSLConstantFolder.h"
13 #include "src/sksl/SkSLThreadContext.h"
14 #include "src/sksl/dsl/priv/DSLWriter.h"
15 #include "src/sksl/dsl/priv/DSL_priv.h"
16 #include <memory>
17
18 using namespace SkSL::dsl;
19
20 namespace SkSL {
21
22 static constexpr int kMaxParseDepth = 50;
23
parse_modifier_token(Token::Kind token)24 static int parse_modifier_token(Token::Kind token) {
25 switch (token) {
26 case Token::Kind::TK_UNIFORM: return Modifiers::kUniform_Flag;
27 case Token::Kind::TK_CONST: return Modifiers::kConst_Flag;
28 case Token::Kind::TK_IN: return Modifiers::kIn_Flag;
29 case Token::Kind::TK_OUT: return Modifiers::kOut_Flag;
30 case Token::Kind::TK_INOUT: return Modifiers::kIn_Flag | Modifiers::kOut_Flag;
31 case Token::Kind::TK_FLAT: return Modifiers::kFlat_Flag;
32 case Token::Kind::TK_NOPERSPECTIVE: return Modifiers::kNoPerspective_Flag;
33 case Token::Kind::TK_HASSIDEEFFECTS: return Modifiers::kHasSideEffects_Flag;
34 case Token::Kind::TK_INLINE: return Modifiers::kInline_Flag;
35 case Token::Kind::TK_NOINLINE: return Modifiers::kNoInline_Flag;
36 case Token::Kind::TK_HIGHP: return Modifiers::kHighp_Flag;
37 case Token::Kind::TK_MEDIUMP: return Modifiers::kMediump_Flag;
38 case Token::Kind::TK_LOWP: return Modifiers::kLowp_Flag;
39 case Token::Kind::TK_ES3: return Modifiers::kES3_Flag;
40 default: return 0;
41 }
42 }
43
44 class AutoDSLDepth {
45 public:
AutoDSLDepth(DSLParser * p)46 AutoDSLDepth(DSLParser* p)
47 : fParser(p)
48 , fDepth(0) {}
49
~AutoDSLDepth()50 ~AutoDSLDepth() {
51 fParser->fDepth -= fDepth;
52 }
53
increase()54 bool increase() {
55 ++fDepth;
56 ++fParser->fDepth;
57 if (fParser->fDepth > kMaxParseDepth) {
58 fParser->error(fParser->peek(), "exceeded max parse depth");
59 fParser->fEncounteredFatalError = true;
60 return false;
61 }
62 return true;
63 }
64
65 private:
66 DSLParser* fParser;
67 int fDepth;
68 };
69
70 class AutoDSLSymbolTable {
71 public:
AutoDSLSymbolTable()72 AutoDSLSymbolTable() {
73 dsl::PushSymbolTable();
74 }
75
~AutoDSLSymbolTable()76 ~AutoDSLSymbolTable() {
77 dsl::PopSymbolTable();
78 }
79 };
80
81 std::unordered_map<std::string_view, DSLParser::LayoutToken>* DSLParser::layoutTokens;
82
InitLayoutMap()83 void DSLParser::InitLayoutMap() {
84 layoutTokens = new std::unordered_map<std::string_view, LayoutToken>;
85 #define TOKEN(name, text) (*layoutTokens)[text] = LayoutToken::name
86 TOKEN(LOCATION, "location");
87 TOKEN(OFFSET, "offset");
88 TOKEN(BINDING, "binding");
89 TOKEN(INDEX, "index");
90 TOKEN(SET, "set");
91 TOKEN(BUILTIN, "builtin");
92 TOKEN(INPUT_ATTACHMENT_INDEX, "input_attachment_index");
93 TOKEN(ORIGIN_UPPER_LEFT, "origin_upper_left");
94 TOKEN(BLEND_SUPPORT_ALL_EQUATIONS, "blend_support_all_equations");
95 TOKEN(PUSH_CONSTANT, "push_constant");
96 TOKEN(COLOR, "color");
97 #undef TOKEN
98 }
99
DSLParser(Compiler * compiler,const ProgramSettings & settings,ProgramKind kind,std::string text)100 DSLParser::DSLParser(Compiler* compiler, const ProgramSettings& settings, ProgramKind kind,
101 std::string text)
102 : fCompiler(*compiler)
103 , fSettings(settings)
104 , fKind(kind)
105 , fText(std::make_unique<std::string>(std::move(text)))
106 , fPushback(Token::Kind::TK_NONE, /*offset=*/-1, /*length=*/-1, /*line=*/-1) {
107 // We don't want to have to worry about manually releasing all of the objects in the event that
108 // an error occurs
109 fSettings.fAssertDSLObjectsReleased = false;
110 // We manage our symbol tables manually, so no need for name mangling
111 fSettings.fDSLMangling = false;
112 fLexer.start(*fText);
113 static const bool layoutMapInitialized = []{ InitLayoutMap(); return true; }();
114 (void) layoutMapInitialized;
115 }
116
nextRawToken()117 Token DSLParser::nextRawToken() {
118 Token token;
119 if (fPushback.fKind != Token::Kind::TK_NONE) {
120 // Retrieve the token from the pushback buffer.
121 token = fPushback;
122 fPushback.fKind = Token::Kind::TK_NONE;
123 } else {
124 // Fetch a token from the lexer.
125 token = fLexer.next();
126
127 // Some tokens are always invalid, so we detect and report them here.
128 switch (token.fKind) {
129 case Token::Kind::TK_RESERVED:
130 this->error(token, "'" + std::string(this->text(token)) + "' is a reserved word");
131 token.fKind = Token::Kind::TK_IDENTIFIER; // reduces additional follow-up errors
132 break;
133
134 case Token::Kind::TK_BAD_OCTAL:
135 this->error(token, "'" + std::string(this->text(token)) +
136 "' is not a valid octal number");
137 break;
138
139 default:
140 break;
141 }
142 }
143
144 return token;
145 }
146
nextToken()147 Token DSLParser::nextToken() {
148 for (;;) {
149 Token token = this->nextRawToken();
150 switch (token.fKind) {
151 case Token::Kind::TK_WHITESPACE:
152 case Token::Kind::TK_LINE_COMMENT:
153 case Token::Kind::TK_BLOCK_COMMENT:
154 continue;
155
156 default:
157 return token;
158 }
159 }
160 }
161
pushback(Token t)162 void DSLParser::pushback(Token t) {
163 SkASSERT(fPushback.fKind == Token::Kind::TK_NONE);
164 fPushback = std::move(t);
165 }
166
peek()167 Token DSLParser::peek() {
168 if (fPushback.fKind == Token::Kind::TK_NONE) {
169 fPushback = this->nextToken();
170 }
171 return fPushback;
172 }
173
checkNext(Token::Kind kind,Token * result)174 bool DSLParser::checkNext(Token::Kind kind, Token* result) {
175 if (fPushback.fKind != Token::Kind::TK_NONE && fPushback.fKind != kind) {
176 return false;
177 }
178 Token next = this->nextToken();
179 if (next.fKind == kind) {
180 if (result) {
181 *result = next;
182 }
183 return true;
184 }
185 this->pushback(std::move(next));
186 return false;
187 }
188
expect(Token::Kind kind,const char * expected,Token * result)189 bool DSLParser::expect(Token::Kind kind, const char* expected, Token* result) {
190 Token next = this->nextToken();
191 if (next.fKind == kind) {
192 if (result) {
193 *result = std::move(next);
194 }
195 return true;
196 } else {
197 this->error(next, "expected " + std::string(expected) + ", but found '" +
198 std::string(this->text(next)) + "'");
199 this->fEncounteredFatalError = true;
200 return false;
201 }
202 }
203
expectIdentifier(Token * result)204 bool DSLParser::expectIdentifier(Token* result) {
205 if (!this->expect(Token::Kind::TK_IDENTIFIER, "an identifier", result)) {
206 return false;
207 }
208 if (IsBuiltinType(this->text(*result))) {
209 this->error(*result, "expected an identifier, but found type '" +
210 std::string(this->text(*result)) + "'");
211 this->fEncounteredFatalError = true;
212 return false;
213 }
214 return true;
215 }
216
checkIdentifier(Token * result)217 bool DSLParser::checkIdentifier(Token* result) {
218 if (!this->checkNext(Token::Kind::TK_IDENTIFIER, result)) {
219 return false;
220 }
221 if (IsBuiltinType(this->text(*result))) {
222 this->pushback(std::move(*result));
223 return false;
224 }
225 return true;
226 }
227
text(Token token)228 std::string_view DSLParser::text(Token token) {
229 return std::string_view(fText->data() + token.fOffset, token.fLength);
230 }
231
position(Token t)232 PositionInfo DSLParser::position(Token t) {
233 return this->position(t.fLine);
234 }
235
position(int line)236 PositionInfo DSLParser::position(int line) {
237 return PositionInfo("<unknown>", line);
238 }
239
error(Token token,std::string msg)240 void DSLParser::error(Token token, std::string msg) {
241 this->error(token.fLine, msg);
242 }
243
error(int line,std::string msg)244 void DSLParser::error(int line, std::string msg) {
245 GetErrorReporter().error(msg.c_str(), this->position(line));
246 }
247
248 /* declaration* END_OF_FILE */
program()249 std::unique_ptr<Program> DSLParser::program() {
250 ErrorReporter* errorReporter = &fCompiler.errorReporter();
251 Start(&fCompiler, fKind, fSettings);
252 SetErrorReporter(errorReporter);
253 errorReporter->setSource(fText->c_str());
254 this->declarations();
255 std::unique_ptr<Program> result;
256 if (!GetErrorReporter().errorCount()) {
257 result = dsl::ReleaseProgram(std::move(fText));
258 }
259 errorReporter->setSource(nullptr);
260 End();
261 return result;
262 }
263
moduleInheritingFrom(SkSL::ParsedModule baseModule)264 SkSL::LoadedModule DSLParser::moduleInheritingFrom(SkSL::ParsedModule baseModule) {
265 ErrorReporter* errorReporter = &fCompiler.errorReporter();
266 StartModule(&fCompiler, fKind, fSettings, std::move(baseModule));
267 SetErrorReporter(errorReporter);
268 errorReporter->setSource(fText->c_str());
269 this->declarations();
270 CurrentSymbolTable()->takeOwnershipOfString(std::move(*fText));
271 SkSL::LoadedModule result{ fKind, CurrentSymbolTable(),
272 std::move(ThreadContext::ProgramElements()) };
273 errorReporter->setSource(nullptr);
274 End();
275 return result;
276 }
277
declarations()278 void DSLParser::declarations() {
279 fEncounteredFatalError = false;
280 bool done = false;
281 while (!done) {
282 switch (this->peek().fKind) {
283 case Token::Kind::TK_END_OF_FILE:
284 done = true;
285 break;
286 case Token::Kind::TK_DIRECTIVE:
287 this->directive();
288 break;
289 case Token::Kind::TK_INVALID: {
290 this->nextToken();
291 this->error(this->peek(), "invalid token");
292 done = true;
293 break;
294 }
295 default:
296 this->declaration();
297 done = fEncounteredFatalError;
298 break;
299 }
300 }
301 }
302
303 /* DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
directive()304 void DSLParser::directive() {
305 Token start;
306 if (!this->expect(Token::Kind::TK_DIRECTIVE, "a directive", &start)) {
307 return;
308 }
309 std::string_view text = this->text(start);
310 const bool allowExtensions = !ProgramConfig::IsRuntimeEffect(fKind);
311 if (text == "#extension" && allowExtensions) {
312 Token name;
313 if (!this->expectIdentifier(&name)) {
314 return;
315 }
316 if (!this->expect(Token::Kind::TK_COLON, "':'")) {
317 return;
318 }
319 Token behavior;
320 if (!this->expect(Token::Kind::TK_IDENTIFIER, "an identifier", &behavior)) {
321 return;
322 }
323 std::string_view behaviorText = this->text(behavior);
324 if (behaviorText == "disable") {
325 return;
326 }
327 if (behaviorText != "require" && behaviorText != "enable" && behaviorText != "warn") {
328 this->error(behavior, "expected 'require', 'enable', 'warn', or 'disable'");
329 }
330 // We don't currently do anything different between require, enable, and warn
331 dsl::AddExtension(this->text(name));
332 } else {
333 this->error(start, "unsupported directive '" + std::string(this->text(start)) + "'");
334 }
335 }
336
337 /* modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter (COMMA parameter)* RPAREN
338 (block | SEMICOLON)) | SEMICOLON) | interfaceBlock) */
declaration()339 bool DSLParser::declaration() {
340 Token lookahead = this->peek();
341 if (lookahead.fKind == Token::Kind::TK_SEMICOLON) {
342 this->nextToken();
343 this->error(lookahead, "expected a declaration, but found ';'");
344 return false;
345 }
346 DSLModifiers modifiers = this->modifiers();
347 lookahead = this->peek();
348 if (lookahead.fKind == Token::Kind::TK_IDENTIFIER && !IsType(this->text(lookahead))) {
349 // we have an identifier that's not a type, could be the start of an interface block
350 return this->interfaceBlock(modifiers);
351 }
352 if (lookahead.fKind == Token::Kind::TK_SEMICOLON) {
353 this->nextToken();
354 Declare(modifiers, position(lookahead));
355 return true;
356 }
357 if (lookahead.fKind == Token::Kind::TK_STRUCT) {
358 this->structVarDeclaration(modifiers);
359 return true;
360 }
361 std::optional<DSLType> type = this->type(&modifiers);
362 if (!type) {
363 return false;
364 }
365 Token name;
366 if (!this->expectIdentifier(&name)) {
367 return false;
368 }
369 if (this->checkNext(Token::Kind::TK_LPAREN)) {
370 return this->functionDeclarationEnd(modifiers, *type, name);
371 } else {
372 this->globalVarDeclarationEnd(this->position(name), modifiers, *type, this->text(name));
373 return true;
374 }
375 }
376
377 /* (RPAREN | VOID RPAREN | parameter (COMMA parameter)* RPAREN) (block | SEMICOLON) */
functionDeclarationEnd(const DSLModifiers & modifiers,DSLType type,const Token & name)378 bool DSLParser::functionDeclarationEnd(const DSLModifiers& modifiers,
379 DSLType type,
380 const Token& name) {
381 SkTArray<DSLWrapper<DSLParameter>> parameters;
382 Token lookahead = this->peek();
383 if (lookahead.fKind == Token::Kind::TK_RPAREN) {
384 // `()` means no parameters at all.
385 } else if (lookahead.fKind == Token::Kind::TK_IDENTIFIER && this->text(lookahead) == "void") {
386 // `(void)` also means no parameters at all.
387 this->nextToken();
388 } else {
389 for (;;) {
390 size_t paramIndex = parameters.size();
391 std::optional<DSLWrapper<DSLParameter>> parameter = this->parameter(paramIndex);
392 if (!parameter) {
393 return false;
394 }
395 parameters.push_back(std::move(*parameter));
396 if (!this->checkNext(Token::Kind::TK_COMMA)) {
397 break;
398 }
399 }
400 }
401 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
402 return false;
403 }
404 SkTArray<DSLParameter*> parameterPointers;
405 for (DSLWrapper<DSLParameter>& param : parameters) {
406 parameterPointers.push_back(¶m.get());
407 }
408 DSLFunction result(modifiers, type, this->text(name), parameterPointers, this->position(name));
409 if (!this->checkNext(Token::Kind::TK_SEMICOLON)) {
410 AutoDSLSymbolTable symbols;
411 for (DSLParameter* var : parameterPointers) {
412 AddToSymbolTable(*var);
413 }
414 std::optional<DSLBlock> body = this->block();
415 if (!body) {
416 return false;
417 }
418 result.define(std::move(*body), this->position(name));
419 }
420 return true;
421 }
422
arraySize()423 SKSL_INT DSLParser::arraySize() {
424 DSLExpression sizeExpr = this->expression();
425 if (!sizeExpr.isValid()) {
426 return 1;
427 }
428 std::unique_ptr<SkSL::Expression> sizeLiteral = sizeExpr.release();
429 SKSL_INT size;
430 if (!ConstantFolder::GetConstantInt(*sizeLiteral, &size)) {
431 this->error(sizeLiteral->fLine, "array size must be an integer");
432 return 1;
433 }
434 if (size > INT32_MAX) {
435 this->error(sizeLiteral->fLine, "array size out of bounds");
436 return 1;
437 }
438 if (size <= 0) {
439 this->error(sizeLiteral->fLine, "array size must be positive");
440 return 1;
441 }
442 return size;
443 }
444
parseArrayDimensions(int line,DSLType * type)445 bool DSLParser::parseArrayDimensions(int line, DSLType* type) {
446 while (this->checkNext(Token::Kind::TK_LBRACKET)) {
447 if (this->checkNext(Token::Kind::TK_RBRACKET)) {
448 this->error(line, "expected array dimension");
449 } else {
450 *type = Array(*type, this->arraySize(), this->position(line));
451 if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
452 return false;
453 }
454 }
455 }
456 return true;
457 }
458
parseInitializer(int line,DSLExpression * initializer)459 bool DSLParser::parseInitializer(int line, DSLExpression* initializer) {
460 if (this->checkNext(Token::Kind::TK_EQ)) {
461 DSLExpression value = this->assignmentExpression();
462 if (!value.hasValue()) {
463 return false;
464 }
465 initializer->swap(value);
466 }
467 return true;
468 }
469
470 /* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
471 (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */
globalVarDeclarationEnd(PositionInfo pos,const dsl::DSLModifiers & mods,dsl::DSLType baseType,std::string_view name)472 void DSLParser::globalVarDeclarationEnd(PositionInfo pos, const dsl::DSLModifiers& mods,
473 dsl::DSLType baseType, std::string_view name) {
474 using namespace dsl;
475 int line = this->peek().fLine;
476 DSLType type = baseType;
477 DSLExpression initializer;
478 if (!this->parseArrayDimensions(line, &type)) {
479 return;
480 }
481 if (!this->parseInitializer(line, &initializer)) {
482 return;
483 }
484 DSLGlobalVar first(mods, type, name, std::move(initializer), pos);
485 Declare(first);
486 AddToSymbolTable(first);
487
488 while (this->checkNext(Token::Kind::TK_COMMA)) {
489 type = baseType;
490 Token identifierName;
491 if (!this->expectIdentifier(&identifierName)) {
492 return;
493 }
494 if (!this->parseArrayDimensions(line, &type)) {
495 return;
496 }
497 DSLExpression anotherInitializer;
498 if (!this->parseInitializer(line, &anotherInitializer)) {
499 return;
500 }
501 DSLGlobalVar next(mods, type, this->text(identifierName), std::move(anotherInitializer),
502 this->position(line));
503 Declare(next);
504 AddToSymbolTable(next, this->position(identifierName));
505 }
506 this->expect(Token::Kind::TK_SEMICOLON, "';'");
507 }
508
509 /* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
510 (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */
localVarDeclarationEnd(PositionInfo pos,const dsl::DSLModifiers & mods,dsl::DSLType baseType,std::string_view name)511 DSLStatement DSLParser::localVarDeclarationEnd(PositionInfo pos, const dsl::DSLModifiers& mods,
512 dsl::DSLType baseType, std::string_view name) {
513 using namespace dsl;
514 int line = this->peek().fLine;
515 DSLType type = baseType;
516 DSLExpression initializer;
517 if (!this->parseArrayDimensions(line, &type)) {
518 return {};
519 }
520 if (!this->parseInitializer(line, &initializer)) {
521 return {};
522 }
523 DSLVar first(mods, type, name, std::move(initializer), pos);
524 DSLStatement result = Declare(first);
525 AddToSymbolTable(first);
526
527 while (this->checkNext(Token::Kind::TK_COMMA)) {
528 type = baseType;
529 Token identifierName;
530 if (!this->expectIdentifier(&identifierName)) {
531 return result;
532 }
533 if (!this->parseArrayDimensions(line, &type)) {
534 return result;
535 }
536 DSLExpression anotherInitializer;
537 if (!this->parseInitializer(line, &anotherInitializer)) {
538 return result;
539 }
540 DSLVar next(mods, type, this->text(identifierName), std::move(anotherInitializer),
541 this->position(line));
542 DSLWriter::AddVarDeclaration(result, next);
543 AddToSymbolTable(next, this->position(identifierName));
544 }
545 this->expect(Token::Kind::TK_SEMICOLON, "';'");
546 return result;
547 }
548
549 /* (varDeclarations | expressionStatement) */
varDeclarationsOrExpressionStatement()550 DSLStatement DSLParser::varDeclarationsOrExpressionStatement() {
551 Token nextToken = this->peek();
552 if (nextToken.fKind == Token::Kind::TK_CONST) {
553 // Statements that begin with `const` might be variable declarations, but can't be legal
554 // SkSL expression-statements. (SkSL constructors don't take a `const` modifier.)
555 return this->varDeclarations();
556 }
557
558 if (nextToken.fKind == Token::Kind::TK_HIGHP ||
559 nextToken.fKind == Token::Kind::TK_MEDIUMP ||
560 nextToken.fKind == Token::Kind::TK_LOWP ||
561 IsType(this->text(nextToken))) {
562 // Statements that begin with a typename are most often variable declarations, but
563 // occasionally the type is part of a constructor, and these are actually expression-
564 // statements in disguise. First, attempt the common case: parse it as a vardecl.
565 Checkpoint checkpoint(this);
566 VarDeclarationsPrefix prefix;
567 if (this->varDeclarationsPrefix(&prefix)) {
568 checkpoint.accept();
569 return this->localVarDeclarationEnd(prefix.fPosition, prefix.fModifiers, prefix.fType,
570 this->text(prefix.fName));
571 }
572
573 // If this statement wasn't actually a vardecl after all, rewind and try parsing it as an
574 // expression-statement instead.
575 checkpoint.rewind();
576 }
577 return this->expressionStatement();
578 }
579
580 // Helper function for varDeclarations(). If this function succeeds, we assume that the rest of the
581 // statement is a variable-declaration statement, not an expression-statement.
varDeclarationsPrefix(VarDeclarationsPrefix * prefixData)582 bool DSLParser::varDeclarationsPrefix(VarDeclarationsPrefix* prefixData) {
583 prefixData->fPosition = this->position(this->peek());
584 prefixData->fModifiers = this->modifiers();
585 std::optional<DSLType> type = this->type(&prefixData->fModifiers);
586 if (!type) {
587 return false;
588 }
589 prefixData->fType = *type;
590 return this->expectIdentifier(&prefixData->fName);
591 }
592
593 /* modifiers type IDENTIFIER varDeclarationEnd */
varDeclarations()594 DSLStatement DSLParser::varDeclarations() {
595 VarDeclarationsPrefix prefix;
596 if (!this->varDeclarationsPrefix(&prefix)) {
597 return {};
598 }
599 return this->localVarDeclarationEnd(prefix.fPosition, prefix.fModifiers, prefix.fType,
600 this->text(prefix.fName));
601 }
602
603 /* STRUCT IDENTIFIER LBRACE varDeclaration* RBRACE */
structDeclaration()604 std::optional<DSLType> DSLParser::structDeclaration() {
605 if (!this->expect(Token::Kind::TK_STRUCT, "'struct'")) {
606 return std::nullopt;
607 }
608 Token name;
609 if (!this->expectIdentifier(&name)) {
610 return std::nullopt;
611 }
612 if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) {
613 return std::nullopt;
614 }
615 AutoDSLDepth depth(this);
616 if (!depth.increase()) {
617 return std::nullopt;
618 }
619 SkTArray<DSLField> fields;
620 std::unordered_set<std::string> field_names;
621 while (!this->checkNext(Token::Kind::TK_RBRACE)) {
622 DSLModifiers modifiers = this->modifiers();
623 std::optional<DSLType> type = this->type(&modifiers);
624 if (!type) {
625 return std::nullopt;
626 }
627
628 do {
629 DSLType actualType = *type;
630 Token memberName;
631 if (!this->expectIdentifier(&memberName)) {
632 return std::nullopt;
633 }
634
635 while (this->checkNext(Token::Kind::TK_LBRACKET)) {
636 actualType = dsl::Array(actualType, this->arraySize(), this->position(memberName));
637 if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
638 return std::nullopt;
639 }
640 }
641
642 std::string key(this->text(memberName));
643 auto found = field_names.find(key);
644 if (found == field_names.end()) {
645 fields.push_back(DSLField(modifiers,
646 std::move(actualType),
647 this->text(memberName),
648 this->position(memberName)));
649 field_names.emplace(key);
650 } else {
651 this->error(name, "field '" + key + "' was already defined in the same struct ('" +
652 std::string(this->text(name)) + "')");
653 }
654 } while (this->checkNext(Token::Kind::TK_COMMA));
655 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
656 return std::nullopt;
657 }
658 }
659 if (fields.empty()) {
660 this->error(name, "struct '" + std::string(this->text(name)) +
661 "' must contain at least one field");
662 }
663 return dsl::Struct(this->text(name), SkMakeSpan(fields), this->position(name));
664 }
665
666 /* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */
structVarDeclaration(const DSLModifiers & modifiers)667 SkTArray<dsl::DSLGlobalVar> DSLParser::structVarDeclaration(const DSLModifiers& modifiers) {
668 std::optional<DSLType> type = this->structDeclaration();
669 if (!type) {
670 return {};
671 }
672 Token name;
673 if (this->checkIdentifier(&name)) {
674 this->globalVarDeclarationEnd(this->position(name), modifiers, std::move(*type),
675 this->text(name));
676 } else {
677 this->expect(Token::Kind::TK_SEMICOLON, "';'");
678 }
679 return {};
680 }
681
682 /* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */
parameter(size_t paramIndex)683 std::optional<DSLWrapper<DSLParameter>> DSLParser::parameter(size_t paramIndex) {
684 DSLModifiers modifiers = this->modifiers();
685 std::optional<DSLType> type = this->type(&modifiers);
686 if (!type) {
687 return std::nullopt;
688 }
689 Token name;
690 std::string_view paramText;
691 PositionInfo paramPos;
692 if (this->checkIdentifier(&name)) {
693 paramPos = this->position(name);
694 paramText = this->text(name);
695 } else {
696 std::string anonymousName = String::printf("_skAnonymousParam%zu", paramIndex);
697 paramPos = this->position(this->peek());
698 paramText = *CurrentSymbolTable()->takeOwnershipOfString(std::move(anonymousName));
699 }
700 if (!this->parseArrayDimensions(name.fLine, &type.value())) {
701 return std::nullopt;
702 }
703 return {{DSLParameter(modifiers, *type, paramText, paramPos)}};
704 }
705
706 /** EQ INT_LITERAL */
layoutInt()707 int DSLParser::layoutInt() {
708 if (!this->expect(Token::Kind::TK_EQ, "'='")) {
709 return -1;
710 }
711 Token resultToken;
712 if (!this->expect(Token::Kind::TK_INT_LITERAL, "a non-negative integer", &resultToken)) {
713 return -1;
714 }
715 std::string_view resultFrag = this->text(resultToken);
716 SKSL_INT resultValue;
717 if (!SkSL::stoi(resultFrag, &resultValue)) {
718 this->error(resultToken, "value in layout is too large: " + std::string(resultFrag));
719 return -1;
720 }
721 return resultValue;
722 }
723
724 /** EQ IDENTIFIER */
layoutIdentifier()725 std::string_view DSLParser::layoutIdentifier() {
726 if (!this->expect(Token::Kind::TK_EQ, "'='")) {
727 return {};
728 }
729 Token resultToken;
730 if (!this->expectIdentifier(&resultToken)) {
731 return {};
732 }
733 return this->text(resultToken);
734 }
735
736 /* LAYOUT LPAREN IDENTIFIER (EQ INT_LITERAL)? (COMMA IDENTIFIER (EQ INT_LITERAL)?)* RPAREN */
layout()737 DSLLayout DSLParser::layout() {
738 DSLLayout result;
739 if (this->checkNext(Token::Kind::TK_LAYOUT)) {
740 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
741 return result;
742 }
743 for (;;) {
744 Token t = this->nextToken();
745 std::string text(this->text(t));
746 auto found = layoutTokens->find(text);
747 if (found != layoutTokens->end()) {
748 switch (found->second) {
749 case LayoutToken::ORIGIN_UPPER_LEFT:
750 result.originUpperLeft(this->position(t));
751 break;
752 case LayoutToken::PUSH_CONSTANT:
753 result.pushConstant(this->position(t));
754 break;
755 case LayoutToken::BLEND_SUPPORT_ALL_EQUATIONS:
756 result.blendSupportAllEquations(this->position(t));
757 break;
758 case LayoutToken::COLOR:
759 result.color(this->position(t));
760 break;
761 case LayoutToken::LOCATION:
762 result.location(this->layoutInt(), this->position(t));
763 break;
764 case LayoutToken::OFFSET:
765 result.offset(this->layoutInt(), this->position(t));
766 break;
767 case LayoutToken::BINDING:
768 result.binding(this->layoutInt(), this->position(t));
769 break;
770 case LayoutToken::INDEX:
771 result.index(this->layoutInt(), this->position(t));
772 break;
773 case LayoutToken::SET:
774 result.set(this->layoutInt(), this->position(t));
775 break;
776 case LayoutToken::BUILTIN:
777 result.builtin(this->layoutInt(), this->position(t));
778 break;
779 case LayoutToken::INPUT_ATTACHMENT_INDEX:
780 result.inputAttachmentIndex(this->layoutInt(), this->position(t));
781 break;
782 default:
783 this->error(t, "'" + text + "' is not a valid layout qualifier");
784 break;
785 }
786 } else {
787 this->error(t, "'" + text + "' is not a valid layout qualifier");
788 }
789 if (this->checkNext(Token::Kind::TK_RPAREN)) {
790 break;
791 }
792 if (!this->expect(Token::Kind::TK_COMMA, "','")) {
793 break;
794 }
795 }
796 }
797 return result;
798 }
799
800 /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
801 VARYING | INLINE)* */
modifiers()802 DSLModifiers DSLParser::modifiers() {
803 DSLLayout layout = this->layout();
804 int flags = 0;
805 for (;;) {
806 // TODO(ethannicholas): handle duplicate / incompatible flags
807 int tokenFlag = parse_modifier_token(peek().fKind);
808 if (!tokenFlag) {
809 break;
810 }
811 flags |= tokenFlag;
812 this->nextToken();
813 }
814 return DSLModifiers(std::move(layout), flags);
815 }
816
817 /* ifStatement | forStatement | doStatement | whileStatement | block | expression */
statement()818 DSLStatement DSLParser::statement() {
819 Token start = this->nextToken();
820 AutoDSLDepth depth(this);
821 if (!depth.increase()) {
822 return {};
823 }
824 this->pushback(start);
825 switch (start.fKind) {
826 case Token::Kind::TK_IF: // fall through
827 case Token::Kind::TK_STATIC_IF:
828 return this->ifStatement();
829 case Token::Kind::TK_FOR:
830 return this->forStatement();
831 case Token::Kind::TK_DO:
832 return this->doStatement();
833 case Token::Kind::TK_WHILE:
834 return this->whileStatement();
835 case Token::Kind::TK_SWITCH: // fall through
836 case Token::Kind::TK_STATIC_SWITCH:
837 return this->switchStatement();
838 case Token::Kind::TK_RETURN:
839 return this->returnStatement();
840 case Token::Kind::TK_BREAK:
841 return this->breakStatement();
842 case Token::Kind::TK_CONTINUE:
843 return this->continueStatement();
844 case Token::Kind::TK_DISCARD:
845 return this->discardStatement();
846 case Token::Kind::TK_LBRACE: {
847 std::optional<DSLBlock> result = this->block();
848 return result ? DSLStatement(std::move(*result)) : DSLStatement();
849 }
850 case Token::Kind::TK_SEMICOLON:
851 this->nextToken();
852 return dsl::Block();
853 case Token::Kind::TK_HIGHP:
854 case Token::Kind::TK_MEDIUMP:
855 case Token::Kind::TK_LOWP:
856 case Token::Kind::TK_CONST:
857 case Token::Kind::TK_IDENTIFIER:
858 return this->varDeclarationsOrExpressionStatement();
859 default:
860 return this->expressionStatement();
861 }
862 }
863
864 /* IDENTIFIER(type) (LBRACKET intLiteral? RBRACKET)* QUESTION? */
type(DSLModifiers * modifiers)865 std::optional<DSLType> DSLParser::type(DSLModifiers* modifiers) {
866 Token type;
867 if (!this->expect(Token::Kind::TK_IDENTIFIER, "a type", &type)) {
868 return std::nullopt;
869 }
870 if (!IsType(this->text(type))) {
871 this->error(type, "no type named '" + std::string(this->text(type)) + "'");
872 return std::nullopt;
873 }
874 DSLType result(this->text(type), modifiers, this->position(type));
875 while (this->checkNext(Token::Kind::TK_LBRACKET)) {
876 if (this->peek().fKind != Token::Kind::TK_RBRACKET) {
877 result = Array(result, this->arraySize(), this->position(type));
878 } else {
879 this->error(this->peek(), "expected array dimension");
880 }
881 this->expect(Token::Kind::TK_RBRACKET, "']'");
882 }
883 return result;
884 }
885
886 /* IDENTIFIER LBRACE
887 varDeclaration+
888 RBRACE (IDENTIFIER (LBRACKET expression? RBRACKET)*)? SEMICOLON */
interfaceBlock(const dsl::DSLModifiers & modifiers)889 bool DSLParser::interfaceBlock(const dsl::DSLModifiers& modifiers) {
890 Token typeName;
891 if (!this->expectIdentifier(&typeName)) {
892 return false;
893 }
894 if (peek().fKind != Token::Kind::TK_LBRACE) {
895 // we only get into interfaceBlock if we found a top-level identifier which was not a type.
896 // 99% of the time, the user was not actually intending to create an interface block, so
897 // it's better to report it as an unknown type
898 this->error(typeName, "no type named '" + std::string(this->text(typeName)) + "'");
899 return false;
900 }
901 this->nextToken();
902 SkTArray<dsl::Field> fields;
903 std::unordered_set<std::string> field_names;
904 while (!this->checkNext(Token::Kind::TK_RBRACE)) {
905 DSLModifiers fieldModifiers = this->modifiers();
906 std::optional<dsl::DSLType> type = this->type(&fieldModifiers);
907 if (!type) {
908 return false;
909 }
910 do {
911 Token fieldName;
912 if (!this->expectIdentifier(&fieldName)) {
913 return false;
914 }
915 DSLType actualType = *type;
916 if (this->checkNext(Token::Kind::TK_LBRACKET)) {
917 Token sizeToken = this->peek();
918 if (sizeToken.fKind != Token::Kind::TK_RBRACKET) {
919 actualType = Array(std::move(actualType), this->arraySize(),
920 this->position(typeName));
921 } else {
922 this->error(sizeToken, "unsized arrays are not permitted");
923 }
924 this->expect(Token::Kind::TK_RBRACKET, "']'");
925 }
926 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
927 return false;
928 }
929
930 std::string key(this->text(fieldName));
931 if (field_names.find(key) == field_names.end()) {
932 fields.push_back(dsl::Field(fieldModifiers,
933 std::move(actualType),
934 this->text(fieldName),
935 this->position(fieldName)));
936 field_names.emplace(key);
937 } else {
938 this->error(typeName,
939 "field '" + key +
940 "' was already defined in the same interface block ('" +
941 std::string(this->text(typeName)) + "')");
942 }
943 }
944 while (this->checkNext(Token::Kind::TK_COMMA));
945 }
946 std::string_view instanceName;
947 Token instanceNameToken;
948 SKSL_INT arraySize = 0;
949 if (this->checkIdentifier(&instanceNameToken)) {
950 instanceName = this->text(instanceNameToken);
951 if (this->checkNext(Token::Kind::TK_LBRACKET)) {
952 arraySize = this->arraySize();
953 this->expect(Token::Kind::TK_RBRACKET, "']'");
954 }
955 }
956 this->expect(Token::Kind::TK_SEMICOLON, "';'");
957 if (fields.empty()) {
958 this->error(typeName, "interface block '" + std::string(this->text(typeName)) +
959 "' must contain at least one member");
960 } else {
961 dsl::InterfaceBlock(modifiers, this->text(typeName), std::move(fields), instanceName,
962 arraySize, this->position(typeName));
963 }
964 return true;
965 }
966
967 /* IF LPAREN expression RPAREN statement (ELSE statement)? */
ifStatement()968 DSLStatement DSLParser::ifStatement() {
969 Token start;
970 bool isStatic = this->checkNext(Token::Kind::TK_STATIC_IF, &start);
971 if (!isStatic && !this->expect(Token::Kind::TK_IF, "'if'", &start)) {
972 return {};
973 }
974 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
975 return {};
976 }
977 DSLExpression test = this->expression();
978 if (!test.hasValue()) {
979 return {};
980 }
981 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
982 return {};
983 }
984 DSLStatement ifTrue = this->statement();
985 if (!ifTrue.hasValue()) {
986 return {};
987 }
988 DSLStatement ifFalse;
989 if (this->checkNext(Token::Kind::TK_ELSE)) {
990 ifFalse = this->statement();
991 if (!ifFalse.hasValue()) {
992 return {};
993 }
994 }
995 if (isStatic) {
996 return StaticIf(std::move(test), std::move(ifTrue),
997 ifFalse.hasValue() ? std::move(ifFalse) : DSLStatement(), this->position(start));
998 } else {
999 return If(std::move(test), std::move(ifTrue),
1000 ifFalse.hasValue() ? std::move(ifFalse) : DSLStatement(), this->position(start));
1001 }
1002 }
1003
1004 /* DO statement WHILE LPAREN expression RPAREN SEMICOLON */
doStatement()1005 DSLStatement DSLParser::doStatement() {
1006 Token start;
1007 if (!this->expect(Token::Kind::TK_DO, "'do'", &start)) {
1008 return {};
1009 }
1010 DSLStatement statement = this->statement();
1011 if (!statement.hasValue()) {
1012 return {};
1013 }
1014 if (!this->expect(Token::Kind::TK_WHILE, "'while'")) {
1015 return {};
1016 }
1017 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
1018 return {};
1019 }
1020 DSLExpression test = this->expression();
1021 if (!test.hasValue()) {
1022 return {};
1023 }
1024 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
1025 return {};
1026 }
1027 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1028 return {};
1029 }
1030 return Do(std::move(statement), std::move(test), this->position(start));
1031 }
1032
1033 /* WHILE LPAREN expression RPAREN STATEMENT */
whileStatement()1034 DSLStatement DSLParser::whileStatement() {
1035 Token start;
1036 if (!this->expect(Token::Kind::TK_WHILE, "'while'", &start)) {
1037 return {};
1038 }
1039 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
1040 return {};
1041 }
1042 DSLExpression test = this->expression();
1043 if (!test.hasValue()) {
1044 return {};
1045 }
1046 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
1047 return {};
1048 }
1049 DSLStatement statement = this->statement();
1050 if (!statement.hasValue()) {
1051 return {};
1052 }
1053 return While(std::move(test), std::move(statement), this->position(start));
1054 }
1055
1056 /* CASE expression COLON statement* */
switchCase()1057 std::optional<DSLCase> DSLParser::switchCase() {
1058 Token start;
1059 if (!this->expect(Token::Kind::TK_CASE, "'case'", &start)) {
1060 return {};
1061 }
1062 DSLExpression value = this->expression();
1063 if (!value.hasValue()) {
1064 return {};
1065 }
1066 if (!this->expect(Token::Kind::TK_COLON, "':'")) {
1067 return {};
1068 }
1069 SkTArray<DSLStatement> statements;
1070 while (this->peek().fKind != Token::Kind::TK_RBRACE &&
1071 this->peek().fKind != Token::Kind::TK_CASE &&
1072 this->peek().fKind != Token::Kind::TK_DEFAULT) {
1073 DSLStatement s = this->statement();
1074 if (!s.hasValue()) {
1075 return {};
1076 }
1077 statements.push_back(std::move(s));
1078 }
1079 return DSLCase(std::move(value), std::move(statements));
1080 }
1081
1082 /* SWITCH LPAREN expression RPAREN LBRACE switchCase* (DEFAULT COLON statement*)? RBRACE */
switchStatement()1083 DSLStatement DSLParser::switchStatement() {
1084 Token start;
1085 bool isStatic = this->checkNext(Token::Kind::TK_STATIC_SWITCH, &start);
1086 if (!isStatic && !this->expect(Token::Kind::TK_SWITCH, "'switch'", &start)) {
1087 return {};
1088 }
1089 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
1090 return {};
1091 }
1092 DSLExpression value = this->expression();
1093 if (!value.hasValue()) {
1094 return {};
1095 }
1096 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
1097 return {};
1098 }
1099 if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) {
1100 return {};
1101 }
1102 SkTArray<DSLCase> cases;
1103 while (this->peek().fKind == Token::Kind::TK_CASE) {
1104 std::optional<DSLCase> c = this->switchCase();
1105 if (!c) {
1106 return {};
1107 }
1108 cases.push_back(std::move(*c));
1109 }
1110 // Requiring default: to be last (in defiance of C and GLSL) was a deliberate decision. Other
1111 // parts of the compiler may rely upon this assumption.
1112 if (this->peek().fKind == Token::Kind::TK_DEFAULT) {
1113 SkTArray<DSLStatement> statements;
1114 Token defaultStart;
1115 SkAssertResult(this->expect(Token::Kind::TK_DEFAULT, "'default'", &defaultStart));
1116 if (!this->expect(Token::Kind::TK_COLON, "':'")) {
1117 return {};
1118 }
1119 while (this->peek().fKind != Token::Kind::TK_RBRACE) {
1120 DSLStatement s = this->statement();
1121 if (!s.hasValue()) {
1122 return {};
1123 }
1124 statements.push_back(std::move(s));
1125 }
1126 cases.push_back(DSLCase(DSLExpression(), std::move(statements), this->position(start)));
1127 }
1128 if (!this->expect(Token::Kind::TK_RBRACE, "'}'")) {
1129 return {};
1130 }
1131 if (isStatic) {
1132 return StaticSwitch(std::move(value), std::move(cases), this->position(start));
1133 } else {
1134 return Switch(std::move(value), std::move(cases), this->position(start));
1135 }
1136 }
1137
1138 /* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN
1139 STATEMENT */
forStatement()1140 dsl::DSLStatement DSLParser::forStatement() {
1141 Token start;
1142 if (!this->expect(Token::Kind::TK_FOR, "'for'", &start)) {
1143 return {};
1144 }
1145 if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
1146 return {};
1147 }
1148 AutoDSLSymbolTable symbols;
1149 dsl::DSLStatement initializer;
1150 Token nextToken = this->peek();
1151 if (nextToken.fKind == Token::Kind::TK_SEMICOLON) {
1152 // An empty init-statement.
1153 this->nextToken();
1154 } else {
1155 // The init-statement must be an expression or variable declaration.
1156 initializer = this->varDeclarationsOrExpressionStatement();
1157 if (!initializer.hasValue()) {
1158 return {};
1159 }
1160 }
1161 dsl::DSLExpression test;
1162 if (this->peek().fKind != Token::Kind::TK_SEMICOLON) {
1163 dsl::DSLExpression testValue = this->expression();
1164 if (!testValue.hasValue()) {
1165 return {};
1166 }
1167 test.swap(testValue);
1168 }
1169 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1170 return {};
1171 }
1172 dsl::DSLExpression next;
1173 if (this->peek().fKind != Token::Kind::TK_RPAREN) {
1174 dsl::DSLExpression nextValue = this->expression();
1175 if (!nextValue.hasValue()) {
1176 return {};
1177 }
1178 next.swap(nextValue);
1179 }
1180 if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
1181 return {};
1182 }
1183 dsl::DSLStatement statement = this->statement();
1184 if (!statement.hasValue()) {
1185 return {};
1186 }
1187 return For(initializer.hasValue() ? std::move(initializer) : DSLStatement(),
1188 test.hasValue() ? std::move(test) : DSLExpression(),
1189 next.hasValue() ? std::move(next) : DSLExpression(),
1190 std::move(statement),
1191 this->position(start));
1192 }
1193
1194 /* RETURN expression? SEMICOLON */
returnStatement()1195 DSLStatement DSLParser::returnStatement() {
1196 Token start;
1197 if (!this->expect(Token::Kind::TK_RETURN, "'return'", &start)) {
1198 return {};
1199 }
1200 DSLExpression expression;
1201 if (this->peek().fKind != Token::Kind::TK_SEMICOLON) {
1202 DSLExpression next = this->expression();
1203 if (!next.hasValue()) {
1204 return {};
1205 }
1206 expression.swap(next);
1207 }
1208 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1209 return {};
1210 }
1211 return Return(expression.hasValue() ? std::move(expression) : DSLExpression(),
1212 this->position(start));
1213 }
1214
1215 /* BREAK SEMICOLON */
breakStatement()1216 DSLStatement DSLParser::breakStatement() {
1217 Token start;
1218 if (!this->expect(Token::Kind::TK_BREAK, "'break'", &start)) {
1219 return {};
1220 }
1221 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1222 return {};
1223 }
1224 return Break(this->position(start));
1225 }
1226
1227 /* CONTINUE SEMICOLON */
continueStatement()1228 DSLStatement DSLParser::continueStatement() {
1229 Token start;
1230 if (!this->expect(Token::Kind::TK_CONTINUE, "'continue'", &start)) {
1231 return {};
1232 }
1233 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1234 return {};
1235 }
1236 return Continue(this->position(start));
1237 }
1238
1239 /* DISCARD SEMICOLON */
discardStatement()1240 DSLStatement DSLParser::discardStatement() {
1241 Token start;
1242 if (!this->expect(Token::Kind::TK_DISCARD, "'continue'", &start)) {
1243 return {};
1244 }
1245 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1246 return {};
1247 }
1248 return Discard(this->position(start));
1249 }
1250
1251 /* LBRACE statement* RBRACE */
block()1252 std::optional<DSLBlock> DSLParser::block() {
1253 Token start;
1254 if (!this->expect(Token::Kind::TK_LBRACE, "'{'", &start)) {
1255 return std::nullopt;
1256 }
1257 AutoDSLDepth depth(this);
1258 if (!depth.increase()) {
1259 return std::nullopt;
1260 }
1261 AutoDSLSymbolTable symbols;
1262 StatementArray statements;
1263 for (;;) {
1264 switch (this->peek().fKind) {
1265 case Token::Kind::TK_RBRACE:
1266 this->nextToken();
1267 return DSLBlock(std::move(statements), CurrentSymbolTable());
1268 case Token::Kind::TK_END_OF_FILE:
1269 this->error(this->peek(), "expected '}', but found end of file");
1270 return std::nullopt;
1271 default: {
1272 DSLStatement statement = this->statement();
1273 if (fEncounteredFatalError) {
1274 return std::nullopt;
1275 }
1276 if (statement.hasValue()) {
1277 statements.push_back(statement.release());
1278 }
1279 break;
1280 }
1281 }
1282 }
1283 }
1284
1285 /* expression SEMICOLON */
expressionStatement()1286 DSLStatement DSLParser::expressionStatement() {
1287 DSLExpression expr = this->expression();
1288 if (expr.hasValue()) {
1289 if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
1290 return {};
1291 }
1292 return DSLStatement(std::move(expr));
1293 }
1294 return {};
1295 }
1296
1297 /* assignmentExpression (COMMA assignmentExpression)* */
expression()1298 DSLExpression DSLParser::expression() {
1299 DSLExpression result = this->assignmentExpression();
1300 if (!result.hasValue()) {
1301 return {};
1302 }
1303 Token t;
1304 AutoDSLDepth depth(this);
1305 while (this->checkNext(Token::Kind::TK_COMMA, &t)) {
1306 if (!depth.increase()) {
1307 return {};
1308 }
1309 DSLExpression right = this->assignmentExpression();
1310 if (!right.hasValue()) {
1311 return {};
1312 }
1313 DSLExpression next = dsl::operator,(std::move(result), std::move(right));
1314 result.swap(next);
1315 }
1316 return result;
1317 }
1318
1319 #define OPERATOR_RIGHT(op, exprType) \
1320 do { \
1321 this->nextToken(); \
1322 if (!depth.increase()) { \
1323 return {}; \
1324 } \
1325 DSLExpression right = this->exprType(); \
1326 if (!right.hasValue()) { \
1327 return {}; \
1328 } \
1329 DSLExpression next = std::move(result) op std::move(right); \
1330 result.swap(next); \
1331 } while (false)
1332
1333 /* ternaryExpression ((EQEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSEQ | MINUSEQ | SHLEQ | SHREQ |
1334 BITWISEANDEQ | BITWISEXOREQ | BITWISEOREQ | LOGICALANDEQ | LOGICALXOREQ | LOGICALOREQ)
1335 assignmentExpression)*
1336 */
assignmentExpression()1337 DSLExpression DSLParser::assignmentExpression() {
1338 AutoDSLDepth depth(this);
1339 DSLExpression result = this->ternaryExpression();
1340 if (!result.hasValue()) {
1341 return {};
1342 }
1343 for (;;) {
1344 switch (this->peek().fKind) {
1345 case Token::Kind::TK_EQ: OPERATOR_RIGHT(=, assignmentExpression); break;
1346 case Token::Kind::TK_STAREQ: OPERATOR_RIGHT(*=, assignmentExpression); break;
1347 case Token::Kind::TK_SLASHEQ: OPERATOR_RIGHT(/=, assignmentExpression); break;
1348 case Token::Kind::TK_PERCENTEQ: OPERATOR_RIGHT(%=, assignmentExpression); break;
1349 case Token::Kind::TK_PLUSEQ: OPERATOR_RIGHT(+=, assignmentExpression); break;
1350 case Token::Kind::TK_MINUSEQ: OPERATOR_RIGHT(-=, assignmentExpression); break;
1351 case Token::Kind::TK_SHLEQ: OPERATOR_RIGHT(<<=, assignmentExpression); break;
1352 case Token::Kind::TK_SHREQ: OPERATOR_RIGHT(>>=, assignmentExpression); break;
1353 case Token::Kind::TK_BITWISEANDEQ: OPERATOR_RIGHT(&=, assignmentExpression); break;
1354 case Token::Kind::TK_BITWISEXOREQ: OPERATOR_RIGHT(^=, assignmentExpression); break;
1355 case Token::Kind::TK_BITWISEOREQ: OPERATOR_RIGHT(|=, assignmentExpression); break;
1356 default:
1357 return result;
1358 }
1359 }
1360 }
1361
1362 /* logicalOrExpression ('?' expression ':' assignmentExpression)? */
ternaryExpression()1363 DSLExpression DSLParser::ternaryExpression() {
1364 DSLExpression base = this->logicalOrExpression();
1365 if (!base.hasValue()) {
1366 return {};
1367 }
1368 if (!this->checkNext(Token::Kind::TK_QUESTION)) {
1369 return base;
1370 }
1371 AutoDSLDepth depth(this);
1372 if (!depth.increase()) {
1373 return {};
1374 }
1375 DSLExpression trueExpr = this->expression();
1376 if (!trueExpr.hasValue()) {
1377 return {};
1378 }
1379 if (!this->expect(Token::Kind::TK_COLON, "':'")) {
1380 return {};
1381 }
1382 DSLExpression falseExpr = this->assignmentExpression();
1383 if (!falseExpr.hasValue()) {
1384 return {};
1385 }
1386 return Select(std::move(base), std::move(trueExpr), std::move(falseExpr));
1387 }
1388
1389 /* logicalXorExpression (LOGICALOR logicalXorExpression)* */
logicalOrExpression()1390 DSLExpression DSLParser::logicalOrExpression() {
1391 AutoDSLDepth depth(this);
1392 DSLExpression result = this->logicalXorExpression();
1393 if (!result.hasValue()) {
1394 return {};
1395 }
1396 while (this->peek().fKind == Token::Kind::TK_LOGICALOR) {
1397 OPERATOR_RIGHT(||, logicalXorExpression);
1398 }
1399 return result;
1400 }
1401
1402 /* logicalAndExpression (LOGICALXOR logicalAndExpression)* */
logicalXorExpression()1403 DSLExpression DSLParser::logicalXorExpression() {
1404 AutoDSLDepth depth(this);
1405 DSLExpression result = this->logicalAndExpression();
1406 if (!result.hasValue()) {
1407 return {};
1408 }
1409 while (this->checkNext(Token::Kind::TK_LOGICALXOR)) {
1410 if (!depth.increase()) {
1411 return {};
1412 }
1413 DSLExpression right = this->logicalAndExpression();
1414 if (!right.hasValue()) {
1415 return {};
1416 }
1417 DSLExpression next = LogicalXor(std::move(result), std::move(right));
1418 result.swap(next);
1419 }
1420 return result;
1421 }
1422
1423 /* bitwiseOrExpression (LOGICALAND bitwiseOrExpression)* */
logicalAndExpression()1424 DSLExpression DSLParser::logicalAndExpression() {
1425 AutoDSLDepth depth(this);
1426 DSLExpression result = this->bitwiseOrExpression();
1427 if (!result.hasValue()) {
1428 return {};
1429 }
1430 while (this->peek().fKind == Token::Kind::TK_LOGICALAND) {
1431 OPERATOR_RIGHT(&&, bitwiseOrExpression);
1432 }
1433 return result;
1434 }
1435
1436 /* bitwiseXorExpression (BITWISEOR bitwiseXorExpression)* */
bitwiseOrExpression()1437 DSLExpression DSLParser::bitwiseOrExpression() {
1438 AutoDSLDepth depth(this);
1439 DSLExpression result = this->bitwiseXorExpression();
1440 if (!result.hasValue()) {
1441 return {};
1442 }
1443 while (this->peek().fKind == Token::Kind::TK_BITWISEOR) {
1444 OPERATOR_RIGHT(|, bitwiseXorExpression);
1445 }
1446 return result;
1447 }
1448
1449 /* bitwiseAndExpression (BITWISEXOR bitwiseAndExpression)* */
bitwiseXorExpression()1450 DSLExpression DSLParser::bitwiseXorExpression() {
1451 AutoDSLDepth depth(this);
1452 DSLExpression result = this->bitwiseAndExpression();
1453 if (!result.hasValue()) {
1454 return {};
1455 }
1456 while (this->peek().fKind == Token::Kind::TK_BITWISEXOR) {
1457 OPERATOR_RIGHT(^, bitwiseAndExpression);
1458 }
1459 return result;
1460 }
1461
1462 /* equalityExpression (BITWISEAND equalityExpression)* */
bitwiseAndExpression()1463 DSLExpression DSLParser::bitwiseAndExpression() {
1464 AutoDSLDepth depth(this);
1465 DSLExpression result = this->equalityExpression();
1466 if (!result.hasValue()) {
1467 return {};
1468 }
1469 while (this->peek().fKind == Token::Kind::TK_BITWISEAND) {
1470 OPERATOR_RIGHT(&, equalityExpression);
1471 }
1472 return result;
1473 }
1474
1475 /* relationalExpression ((EQEQ | NEQ) relationalExpression)* */
equalityExpression()1476 DSLExpression DSLParser::equalityExpression() {
1477 AutoDSLDepth depth(this);
1478 DSLExpression result = this->relationalExpression();
1479 if (!result.hasValue()) {
1480 return {};
1481 }
1482 for (;;) {
1483 switch (this->peek().fKind) {
1484 case Token::Kind::TK_EQEQ: OPERATOR_RIGHT(==, relationalExpression); break;
1485 case Token::Kind::TK_NEQ: OPERATOR_RIGHT(!=, relationalExpression); break;
1486 default: return result;
1487 }
1488 }
1489 }
1490
1491 /* shiftExpression ((LT | GT | LTEQ | GTEQ) shiftExpression)* */
relationalExpression()1492 DSLExpression DSLParser::relationalExpression() {
1493 AutoDSLDepth depth(this);
1494 DSLExpression result = this->shiftExpression();
1495 if (!result.hasValue()) {
1496 return {};
1497 }
1498 for (;;) {
1499 switch (this->peek().fKind) {
1500 case Token::Kind::TK_LT: OPERATOR_RIGHT(<, shiftExpression); break;
1501 case Token::Kind::TK_GT: OPERATOR_RIGHT(>, shiftExpression); break;
1502 case Token::Kind::TK_LTEQ: OPERATOR_RIGHT(<=, shiftExpression); break;
1503 case Token::Kind::TK_GTEQ: OPERATOR_RIGHT(>=, shiftExpression); break;
1504 default: return result;
1505 }
1506 }
1507 }
1508
1509 /* additiveExpression ((SHL | SHR) additiveExpression)* */
shiftExpression()1510 DSLExpression DSLParser::shiftExpression() {
1511 AutoDSLDepth depth(this);
1512 DSLExpression result = this->additiveExpression();
1513 if (!result.hasValue()) {
1514 return {};
1515 }
1516 for (;;) {
1517 switch (this->peek().fKind) {
1518 case Token::Kind::TK_SHL: OPERATOR_RIGHT(<<, additiveExpression); break;
1519 case Token::Kind::TK_SHR: OPERATOR_RIGHT(>>, additiveExpression); break;
1520 default: return result;
1521 }
1522 }
1523 }
1524
1525 /* multiplicativeExpression ((PLUS | MINUS) multiplicativeExpression)* */
additiveExpression()1526 DSLExpression DSLParser::additiveExpression() {
1527 AutoDSLDepth depth(this);
1528 DSLExpression result = this->multiplicativeExpression();
1529 if (!result.hasValue()) {
1530 return {};
1531 }
1532 for (;;) {
1533 switch (this->peek().fKind) {
1534 case Token::Kind::TK_PLUS: OPERATOR_RIGHT(+, multiplicativeExpression); break;
1535 case Token::Kind::TK_MINUS: OPERATOR_RIGHT(-, multiplicativeExpression); break;
1536 default: return result;
1537 }
1538 }
1539 }
1540
1541 /* unaryExpression ((STAR | SLASH | PERCENT) unaryExpression)* */
multiplicativeExpression()1542 DSLExpression DSLParser::multiplicativeExpression() {
1543 AutoDSLDepth depth(this);
1544 DSLExpression result = this->unaryExpression();
1545 if (!result.hasValue()) {
1546 return {};
1547 }
1548 for (;;) {
1549 switch (this->peek().fKind) {
1550 case Token::Kind::TK_STAR: OPERATOR_RIGHT(*, unaryExpression); break;
1551 case Token::Kind::TK_SLASH: OPERATOR_RIGHT(/, unaryExpression); break;
1552 case Token::Kind::TK_PERCENT: OPERATOR_RIGHT(%, unaryExpression); break;
1553 default: return result;
1554 }
1555 }
1556 }
1557
1558 /* postfixExpression | (PLUS | MINUS | NOT | PLUSPLUS | MINUSMINUS) unaryExpression */
unaryExpression()1559 DSLExpression DSLParser::unaryExpression() {
1560 AutoDSLDepth depth(this);
1561 Token next = this->peek();
1562 switch (next.fKind) {
1563 case Token::Kind::TK_PLUS:
1564 case Token::Kind::TK_MINUS:
1565 case Token::Kind::TK_LOGICALNOT:
1566 case Token::Kind::TK_BITWISENOT:
1567 case Token::Kind::TK_PLUSPLUS:
1568 case Token::Kind::TK_MINUSMINUS: {
1569 this->nextToken();
1570 if (!depth.increase()) {
1571 return {};
1572 }
1573 DSLExpression expr = this->unaryExpression();
1574 if (!expr.hasValue()) {
1575 return {};
1576 }
1577 switch (next.fKind) {
1578 case Token::Kind::TK_PLUS: return {{ +std::move(expr)}};
1579 case Token::Kind::TK_MINUS: return {{ -std::move(expr)}};
1580 case Token::Kind::TK_LOGICALNOT: return {{ !std::move(expr)}};
1581 case Token::Kind::TK_BITWISENOT: return {{ ~std::move(expr)}};
1582 case Token::Kind::TK_PLUSPLUS: return {{++std::move(expr)}};
1583 case Token::Kind::TK_MINUSMINUS: return {{--std::move(expr)}};
1584 default: SkUNREACHABLE;
1585 }
1586 }
1587 default:
1588 return this->postfixExpression();
1589 }
1590 }
1591
1592 /* term suffix* */
postfixExpression()1593 DSLExpression DSLParser::postfixExpression() {
1594 AutoDSLDepth depth(this);
1595 DSLExpression result = this->term();
1596 if (!result.hasValue()) {
1597 return {};
1598 }
1599 for (;;) {
1600 Token t = this->peek();
1601 switch (t.fKind) {
1602 case Token::Kind::TK_FLOAT_LITERAL:
1603 if (this->text(t)[0] != '.') {
1604 return result;
1605 }
1606 [[fallthrough]];
1607 case Token::Kind::TK_LBRACKET:
1608 case Token::Kind::TK_DOT:
1609 case Token::Kind::TK_LPAREN:
1610 case Token::Kind::TK_PLUSPLUS:
1611 case Token::Kind::TK_MINUSMINUS: {
1612 if (!depth.increase()) {
1613 return {};
1614 }
1615 DSLExpression next = this->suffix(std::move(result));
1616 if (!next.hasValue()) {
1617 return {};
1618 }
1619 result.swap(next);
1620 break;
1621 }
1622 default:
1623 return result;
1624 }
1625 }
1626 }
1627
swizzle(int line,DSLExpression base,std::string_view swizzleMask)1628 DSLExpression DSLParser::swizzle(int line, DSLExpression base, std::string_view swizzleMask) {
1629 SkASSERT(swizzleMask.length() > 0);
1630 if (!base.type().isVector() && !base.type().isScalar()) {
1631 return base.field(swizzleMask, this->position(line));
1632 }
1633 int length = swizzleMask.length();
1634 SkSL::SwizzleComponent::Type components[4];
1635 for (int i = 0; i < length; ++i) {
1636 if (i >= 4) {
1637 this->error(line, "too many components in swizzle mask");
1638 return DSLExpression::Poison();
1639 }
1640 switch (swizzleMask[i]) {
1641 case '0': components[i] = SwizzleComponent::ZERO; break;
1642 case '1': components[i] = SwizzleComponent::ONE; break;
1643 case 'r': components[i] = SwizzleComponent::R; break;
1644 case 'x': components[i] = SwizzleComponent::X; break;
1645 case 's': components[i] = SwizzleComponent::S; break;
1646 case 'L': components[i] = SwizzleComponent::UL; break;
1647 case 'g': components[i] = SwizzleComponent::G; break;
1648 case 'y': components[i] = SwizzleComponent::Y; break;
1649 case 't': components[i] = SwizzleComponent::T; break;
1650 case 'T': components[i] = SwizzleComponent::UT; break;
1651 case 'b': components[i] = SwizzleComponent::B; break;
1652 case 'z': components[i] = SwizzleComponent::Z; break;
1653 case 'p': components[i] = SwizzleComponent::P; break;
1654 case 'R': components[i] = SwizzleComponent::UR; break;
1655 case 'a': components[i] = SwizzleComponent::A; break;
1656 case 'w': components[i] = SwizzleComponent::W; break;
1657 case 'q': components[i] = SwizzleComponent::Q; break;
1658 case 'B': components[i] = SwizzleComponent::UB; break;
1659 default:
1660 this->error(line, String::printf("invalid swizzle component '%c'",
1661 swizzleMask[i]).c_str());
1662 return DSLExpression::Poison();
1663 }
1664 }
1665 switch (length) {
1666 case 1: return dsl::Swizzle(std::move(base), components[0]);
1667 case 2: return dsl::Swizzle(std::move(base), components[0], components[1]);
1668 case 3: return dsl::Swizzle(std::move(base), components[0], components[1], components[2]);
1669 case 4: return dsl::Swizzle(std::move(base), components[0], components[1], components[2],
1670 components[3]);
1671 default: SkUNREACHABLE;
1672 }
1673 }
1674
call(int line,dsl::DSLExpression base,ExpressionArray args)1675 dsl::DSLExpression DSLParser::call(int line, dsl::DSLExpression base, ExpressionArray args) {
1676 return DSLExpression(base(std::move(args), this->position(line)), this->position(line));
1677 }
1678
1679 /* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN arguments RPAREN |
1680 PLUSPLUS | MINUSMINUS | COLONCOLON IDENTIFIER | FLOAT_LITERAL [IDENTIFIER] */
suffix(DSLExpression base)1681 DSLExpression DSLParser::suffix(DSLExpression base) {
1682 Token next = this->nextToken();
1683 AutoDSLDepth depth(this);
1684 if (!depth.increase()) {
1685 return {};
1686 }
1687 switch (next.fKind) {
1688 case Token::Kind::TK_LBRACKET: {
1689 if (this->checkNext(Token::Kind::TK_RBRACKET)) {
1690 this->error(next, "missing index in '[]'");
1691 return DSLExpression::Poison();
1692 }
1693 DSLExpression index = this->expression();
1694 if (!index.hasValue()) {
1695 return {};
1696 }
1697 this->expect(Token::Kind::TK_RBRACKET, "']' to complete array access expression");
1698 DSLPossibleExpression result = base[std::move(index)];
1699 if (!result.valid()) {
1700 result.reportErrors(this->position(next));
1701 }
1702 return std::move(result);
1703 }
1704 case Token::Kind::TK_DOT: {
1705 int line = this->peek().fLine;
1706 std::string_view text;
1707 if (this->identifier(&text)) {
1708 return this->swizzle(line, std::move(base), text);
1709 }
1710 [[fallthrough]];
1711 }
1712 case Token::Kind::TK_FLOAT_LITERAL: {
1713 // Swizzles that start with a constant number, e.g. '.000r', will be tokenized as
1714 // floating point literals, possibly followed by an identifier. Handle that here.
1715 std::string_view field = this->text(next);
1716 SkASSERT(field[0] == '.');
1717 field.remove_prefix(1);
1718 // use the next *raw* token so we don't ignore whitespace - we only care about
1719 // identifiers that directly follow the float
1720 Token id = this->nextRawToken();
1721 if (id.fKind == Token::Kind::TK_IDENTIFIER) {
1722 return this->swizzle(next.fLine, std::move(base),
1723 std::string(field) + std::string(this->text(id)));
1724 } else if (field.empty()) {
1725 this->error(next, "expected field name or swizzle mask after '.'");
1726 return {{DSLExpression::Poison()}};
1727 }
1728 this->pushback(id);
1729 return this->swizzle(next.fLine, std::move(base), field);
1730 }
1731 case Token::Kind::TK_LPAREN: {
1732 ExpressionArray args;
1733 if (this->peek().fKind != Token::Kind::TK_RPAREN) {
1734 for (;;) {
1735 DSLExpression expr = this->assignmentExpression();
1736 if (!expr.hasValue()) {
1737 return {};
1738 }
1739 args.push_back(expr.release());
1740 if (!this->checkNext(Token::Kind::TK_COMMA)) {
1741 break;
1742 }
1743 }
1744 }
1745 this->expect(Token::Kind::TK_RPAREN, "')' to complete function arguments");
1746 return this->call(next.fLine, std::move(base), std::move(args));
1747 }
1748 case Token::Kind::TK_PLUSPLUS:
1749 return std::move(base)++;
1750 case Token::Kind::TK_MINUSMINUS:
1751 return std::move(base)--;
1752 default: {
1753 this->error(next, "expected expression suffix, but found '" +
1754 std::string(this->text(next)) + "'");
1755 return {};
1756 }
1757 }
1758 }
1759
1760 /* IDENTIFIER | intLiteral | floatLiteral | boolLiteral | '(' expression ')' */
term()1761 DSLExpression DSLParser::term() {
1762 Token t = this->peek();
1763 switch (t.fKind) {
1764 case Token::Kind::TK_IDENTIFIER: {
1765 std::string_view text;
1766 if (this->identifier(&text)) {
1767 return dsl::Symbol(text, this->position(t));
1768 }
1769 break;
1770 }
1771 case Token::Kind::TK_INT_LITERAL: {
1772 SKSL_INT i;
1773 if (!this->intLiteral(&i)) {
1774 i = 0;
1775 }
1776 return DSLExpression(i, this->position(t));
1777 }
1778 case Token::Kind::TK_FLOAT_LITERAL: {
1779 SKSL_FLOAT f;
1780 if (!this->floatLiteral(&f)) {
1781 f = 0.0f;
1782 }
1783 return DSLExpression(f, this->position(t));
1784 }
1785 case Token::Kind::TK_TRUE_LITERAL: // fall through
1786 case Token::Kind::TK_FALSE_LITERAL: {
1787 bool b;
1788 SkAssertResult(this->boolLiteral(&b));
1789 return DSLExpression(b, this->position(t));
1790 }
1791 case Token::Kind::TK_LPAREN: {
1792 this->nextToken();
1793 AutoDSLDepth depth(this);
1794 if (!depth.increase()) {
1795 return {};
1796 }
1797 DSLExpression result = this->expression();
1798 if (result.hasValue()) {
1799 this->expect(Token::Kind::TK_RPAREN, "')' to complete expression");
1800 return result;
1801 }
1802 break;
1803 }
1804 default:
1805 this->nextToken();
1806 this->error(t, "expected expression, but found '" + std::string(this->text(t)) + "'");
1807 fEncounteredFatalError = true;
1808 break;
1809 }
1810 return {};
1811 }
1812
1813 /* INT_LITERAL */
intLiteral(SKSL_INT * dest)1814 bool DSLParser::intLiteral(SKSL_INT* dest) {
1815 Token t;
1816 if (!this->expect(Token::Kind::TK_INT_LITERAL, "integer literal", &t)) {
1817 return false;
1818 }
1819 std::string_view s = this->text(t);
1820 if (!SkSL::stoi(s, dest)) {
1821 this->error(t, "integer is too large: " + std::string(s));
1822 return false;
1823 }
1824 return true;
1825 }
1826
1827 /* FLOAT_LITERAL */
floatLiteral(SKSL_FLOAT * dest)1828 bool DSLParser::floatLiteral(SKSL_FLOAT* dest) {
1829 Token t;
1830 if (!this->expect(Token::Kind::TK_FLOAT_LITERAL, "float literal", &t)) {
1831 return false;
1832 }
1833 std::string_view s = this->text(t);
1834 if (!SkSL::stod(s, dest)) {
1835 this->error(t, "floating-point value is too large: " + std::string(s));
1836 return false;
1837 }
1838 return true;
1839 }
1840
1841 /* TRUE_LITERAL | FALSE_LITERAL */
boolLiteral(bool * dest)1842 bool DSLParser::boolLiteral(bool* dest) {
1843 Token t = this->nextToken();
1844 switch (t.fKind) {
1845 case Token::Kind::TK_TRUE_LITERAL:
1846 *dest = true;
1847 return true;
1848 case Token::Kind::TK_FALSE_LITERAL:
1849 *dest = false;
1850 return true;
1851 default:
1852 this->error(t, "expected 'true' or 'false', but found '" +
1853 std::string(this->text(t)) + "'");
1854 return false;
1855 }
1856 }
1857
1858 /* IDENTIFIER */
identifier(std::string_view * dest)1859 bool DSLParser::identifier(std::string_view* dest) {
1860 Token t;
1861 if (this->expect(Token::Kind::TK_IDENTIFIER, "identifier", &t)) {
1862 *dest = this->text(t);
1863 return true;
1864 }
1865 return false;
1866 }
1867
1868 } // namespace SkSL
1869