static ModifierFlags parse_modifier_token(Token::Kind token) { switch (token) { case Token::Kind::TK_UNIFORM: return ModifierFlag::kUniform; case Token::Kind::TK_CONST: return ModifierFlag::kConst; case Token::Kind::TK_IN: return ModifierFlag::kIn; case Token::Kind::TK_OUT: return ModifierFlag::kOut; case Token::Kind::TK_INOUT: return ModifierFlag::kIn | ModifierFlag::kOut; case Token::Kind::TK_FLAT: return ModifierFlag::kFlat; case Token::Kind::TK_NOPERSPECTIVE: return ModifierFlag::kNoPerspective; case Token::Kind::TK_PURE: return ModifierFlag::kPure; case Token::Kind::TK_INLINE: return ModifierFlag::kInline; case Token::Kind::TK_NOINLINE: return ModifierFlag::kNoInline; case Token::Kind::TK_HIGHP: return ModifierFlag::kHighp; case Token::Kind::TK_MEDIUMP: return ModifierFlag::kMediump; case Token::Kind::TK_LOWP: return ModifierFlag::kLowp; case Token::Kind::TK_EXPORT: return ModifierFlag::kExport; case Token::Kind::TK_ES3: return ModifierFlag::kES3; case Token::Kind::TK_WORKGROUP: return ModifierFlag::kWorkgroup; case Token::Kind::TK_READONLY: return ModifierFlag::kReadOnly; case Token::Kind::TK_WRITEONLY: return ModifierFlag::kWriteOnly; case Token::Kind::TK_BUFFER: return ModifierFlag::kBuffer; case Token::Kind::TK_PIXELLOCAL: return ModifierFlag::kPixelLocal; default: return ModifierFlag::kNone;
}
}
class Parser::AutoDepth { public:
AutoDepth(Parser* p) : fParser(p), fDepth(0) {}
~Checkpoint() {
SkASSERTF(!fOldErrorReporter, "Checkpoint was not accepted or rewound before destruction");
}
void accept() {
this->restoreErrorReporter(); // Parser errors should have been fatal, but we can encounter other errors like type // mismatches despite accepting the parse. Forward those messages to the actual error // handler now.
fErrorReporter.forwardErrors(fParser);
}
Token Parser::nextRawToken() {
Token token; if (fPushback.fKind != Token::Kind::TK_NONE) { // Retrieve the token from the pushback buffer.
token = fPushback;
fPushback.fKind = Token::Kind::TK_NONE;
} else { // Fetch a token from the lexer.
token = fLexer.next();
// Some tokens are always invalid, so we detect and report them here. switch (token.fKind) { case Token::Kind::TK_PRIVATE_IDENTIFIER: if (ProgramConfig::AllowsPrivateIdentifiers(fKind)) {
token.fKind = Token::Kind::TK_IDENTIFIER; break;
}
[[fallthrough]];
case Token::Kind::TK_RESERVED:
this->error(token, "name '" + std::string(this->text(token)) + "' is reserved");
token.fKind = Token::Kind::TK_IDENTIFIER; // reduces additional follow-up errors break;
case Token::Kind::TK_BAD_OCTAL:
this->error(token, "'" + std::string(this->text(token)) + "' is not a valid octal number"); break;
default: break;
}
}
return token;
}
staticbool is_whitespace(Token::Kind kind) { switch (kind) { case Token::Kind::TK_WHITESPACE: case Token::Kind::TK_LINE_COMMENT: case Token::Kind::TK_BLOCK_COMMENT: returntrue;
default: returnfalse;
}
}
bool Parser::expectNewline() {
Token token = this->nextRawToken(); if (token.fKind == Token::Kind::TK_WHITESPACE) { // The lexer doesn't distinguish newlines from other forms of whitespace, so we check // for newlines by searching through the token text.
std::string_view tokenText = this->text(token); if (tokenText.find_first_of('\r') != std::string_view::npos ||
tokenText.find_first_of('\n') != std::string_view::npos) { returntrue;
}
} // We didn't find a newline.
this->pushback(token); returnfalse;
}
Token Parser::nextToken() { for (;;) {
Token token = this->nextRawToken(); if (!is_whitespace(token.fKind)) { return token;
}
}
}
// If the program is 8MB or longer (Position::kMaxOffset), error reporting goes off the rails. // At any rate, there's no good reason for a program to be this long. if (fText->size() >= Position::kMaxOffset) {
this->error(Position(), "program is too large"); return;
}
// Any #version directive must appear as the first thing in a file if (this->peek().fKind == Token::Kind::TK_DIRECTIVE) {
this->directive(/*allowVersion=*/true);
}
while (!fEncounteredFatalError) { // We should always be at global scope when processing top-level declarations.
SkASSERT(fCompiler.context().fSymbolTable == fCompiler.globalSymbols());
switch (this->peek().fKind) { case Token::Kind::TK_END_OF_FILE: return;
case Token::Kind::TK_INVALID:
this->error(this->peek(), "invalid token"); return;
case Token::Kind::TK_DIRECTIVE:
this->directive(/*allowVersion=*/false); break;
default:
this->declaration(); break;
}
}
}
/* DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER NEWLINE */ void Parser::extensionDirective(Position start) {
Token name; if (!this->expectIdentifier(&name)) { return;
} if (!this->expect(Token::Kind::TK_COLON, "':'")) { return;
}
Token behavior; if (!this->expect(Token::Kind::TK_IDENTIFIER, "an identifier", &behavior)) { return;
} // We expect a newline immediately after `#extension name : behavior`. if (this->expectNewline()) {
std::unique_ptr<SkSL::Extension> ext = Extension::Convert(fCompiler.context(),
this->rangeFrom(start),
this->text(name),
this->text(behavior)); if (ext) {
fProgramElements.push_back(std::move(ext));
}
} else {
this->error(start, "invalid #extension directive");
}
}
/* DIRECTIVE(#version) INTLITERAL NEWLINE */ void Parser::versionDirective(Position start, bool allowVersion) { if (!allowVersion) {
this->error(start, "#version directive must appear before anything else"); return;
}
SKSL_INT version; if (!this->intLiteral(&version)) { return;
} switch (version) { case 100:
fCompiler.context().fConfig->fRequiredSkSLVersion = Version::k100; break; case 300:
fCompiler.context().fConfig->fRequiredSkSLVersion = Version::k300; break; default:
this->error(start, "unsupported version number"); return;
} // We expect a newline after a #version directive. if (!this->expectNewline()) {
this->error(start, "invalid #version directive");
}
}
std::unique_ptr<SymbolTable> symbolTable;
std::unique_ptr<Statement> body;
{ // Create a symbol table for the function which includes the parameters.
AutoSymbolTable symbols(this, &symbolTable); if (decl) { for (Variable* param : decl->parameters()) {
symbolTable->addWithoutOwnership(fCompiler.context(), param);
}
}
// Parse the function body.
body = this->block(/*introduceNewScope=*/false, /*adoptExistingSymbolTable=*/&symbolTable);
}
// If there was a problem with the declarations or body, don't actually create a definition. if (!decl || !body) { returnfalse;
}
std::unique_ptr<FunctionDefinition> function = FunctionDefinition::Convert(context,
pos,
*decl,
std::move(block)); if (!function) { returnfalse;
}
decl->setDefinition(function.get());
fProgramElements.push_back(std::move(function)); returntrue;
}
bool Parser::arraySize(SKSL_INT* outResult) { // Start out with a safe value that won't generate any errors downstream
*outResult = 1;
Token next = this->peek(); if (next.fKind == Token::Kind::TK_RBRACKET) {
this->error(this->position(next), "unsized arrays are not permitted here"); returntrue;
}
std::unique_ptr<Expression> sizeLiteral = this->expression(); if (!sizeLiteral) { returnfalse;
} if (!sizeLiteral->is<Poison>()) {
SKSL_INT size; if (!ConstantFolder::GetConstantInt(*sizeLiteral, &size)) {
this->error(sizeLiteral->fPosition, "array size must be an integer"); returntrue;
} if (size > INT32_MAX) {
this->error(sizeLiteral->fPosition, "array size out of bounds"); returntrue;
} if (size <= 0) {
this->error(sizeLiteral->fPosition, "array size must be positive"); returntrue;
} // Now that we've validated it, output the real value
*outResult = size;
} returntrue;
}
/* (varDeclarations | expressionStatement) */
std::unique_ptr<Statement> Parser::varDeclarationsOrExpressionStatement() {
Token nextToken = this->peek(); if (nextToken.fKind == Token::Kind::TK_CONST) { // Statements that begin with `const` might be variable declarations, but can't be legal // SkSL expression-statements. (SkSL constructors don't take a `const` modifier.) return this->varDeclarations();
}
if (nextToken.fKind == Token::Kind::TK_HIGHP ||
nextToken.fKind == Token::Kind::TK_MEDIUMP ||
nextToken.fKind == Token::Kind::TK_LOWP ||
this->symbolTable()->isType(this->text(nextToken))) { // Statements that begin with a typename are most often variable declarations, but // occasionally the type is part of a constructor, and these are actually expression- // statements in disguise. First, attempt the common case: parse it as a vardecl.
Checkpoint checkpoint(this);
VarDeclarationsPrefix prefix; if (this->varDeclarationsPrefix(&prefix)) {
checkpoint.accept(); return this->localVarDeclarationEnd(prefix.fPosition, prefix.fModifiers, prefix.fType,
prefix.fName);
}
// If this statement wasn't actually a vardecl after all, rewind and try parsing it as an // expression-statement instead.
checkpoint.rewind();
} return this->expressionStatement();
}
// Helper function for varDeclarations(). If this function succeeds, we assume that the rest of the // statement is a variable-declaration statement, not an expression-statement. bool Parser::varDeclarationsPrefix(VarDeclarationsPrefix* prefixData) {
prefixData->fPosition = this->position(this->peek());
prefixData->fModifiers = this->modifiers();
prefixData->fType = this->type(&prefixData->fModifiers); if (!prefixData->fType) { returnfalse;
} return this->expectIdentifier(&prefixData->fName);
}
/* ifStatement | forStatement | doStatement | whileStatement | block | expression */
std::unique_ptr<Statement> Parser::statement(bool bracesIntroduceNewScope) {
AutoDepth depth(this); if (!depth.increase()) { return nullptr;
} switch (this->peek().fKind) { case Token::Kind::TK_IF: return this->ifStatement(); case Token::Kind::TK_FOR: return this->forStatement(); case Token::Kind::TK_DO: return this->doStatement(); case Token::Kind::TK_WHILE: return this->whileStatement(); case Token::Kind::TK_SWITCH: return this->switchStatement(); case Token::Kind::TK_RETURN: return this->returnStatement(); case Token::Kind::TK_BREAK: return this->breakStatement(); case Token::Kind::TK_CONTINUE: return this->continueStatement(); case Token::Kind::TK_DISCARD: return this->discardStatement(); case Token::Kind::TK_LBRACE: return this->block(bracesIntroduceNewScope, /*adoptExistingSymbolTable=*/nullptr); case Token::Kind::TK_SEMICOLON:
this->nextToken(); return Nop::Make(); case Token::Kind::TK_CONST: return this->varDeclarations(); case Token::Kind::TK_HIGHP: case Token::Kind::TK_MEDIUMP: case Token::Kind::TK_LOWP: case Token::Kind::TK_IDENTIFIER: return this->varDeclarationsOrExpressionStatement(); default: return this->expressionStatement();
}
}
const Type* Parser::findType(Position pos,
Modifiers* modifiers,
std::string_view name) { const Context& context = fCompiler.context(); const Symbol* symbol = this->symbolTable()->find(name); if (!symbol) {
this->error(pos, "no symbol named '" + std::string(name) + "'"); return context.fTypes.fPoison.get();
} if (!symbol->is<Type>()) {
this->error(pos, "symbol '" + std::string(name) + "' is not a type"); return context.fTypes.fPoison.get();
} const SkSL::Type* type = &symbol->as<Type>(); if (!context.fConfig->isBuiltinCode()) { if (!TypeReference::VerifyType(context, type, pos)) { return context.fTypes.fPoison.get();
}
}
Position qualifierRange = modifiers->fPosition; if (qualifierRange.startOffset() == qualifierRange.endOffset()) {
qualifierRange = this->rangeFrom(qualifierRange);
} return modifiers ? type->applyQualifiers(context, &modifiers->fFlags, qualifierRange)
: type;
}
/* IDENTIFIER(type) (LBRACKET intLiteral? RBRACKET)* QUESTION? */ const Type* Parser::type(Modifiers* modifiers) {
Token type; if (!this->expect(Token::Kind::TK_IDENTIFIER, "a type", &type)) { return nullptr;
} if (!this->symbolTable()->isType(this->text(type))) {
this->error(type, "no type named '" + std::string(this->text(type)) + "'"); return fCompiler.context().fTypes.fInvalid.get();
} const Type* result = this->findType(this->position(type), modifiers, this->text(type)); if (result->isInterfaceBlock()) { // SkSL puts interface blocks into the symbol table, but they aren't general-purpose types; // you can't use them to declare a variable type or a function return type.
this->error(type, "expected a type, found '" + std::string(this->text(type)) + "'"); return fCompiler.context().fTypes.fInvalid.get();
}
Token bracket; while (this->checkNext(Token::Kind::TK_LBRACKET, &bracket)) { if (this->checkNext(Token::Kind::TK_RBRACKET)) { if (this->allowUnsizedArrays()) {
result = this->unsizedArrayType(result, this->rangeFrom(type));
} else {
this->error(this->rangeFrom(bracket), "unsized arrays are not permitted here");
}
} else {
SKSL_INT size; if (!this->arraySize(&size)) { return nullptr;
}
this->expect(Token::Kind::TK_RBRACKET, "']'");
result = this->arrayType(result, size, this->rangeFrom(type));
}
} return result;
}
/* IDENTIFIER LBRACE varDeclaration+
RBRACE (IDENTIFIER (LBRACKET expression RBRACKET)*)? SEMICOLON */ bool Parser::interfaceBlock(const Modifiers& modifiers) {
Token typeName; if (!this->expectIdentifier(&typeName)) { returnfalse;
} if (this->peek().fKind != Token::Kind::TK_LBRACE) { // we only get into interfaceBlock if we found a top-level identifier which was not a type. // 99% of the time, the user was not actually intending to create an interface block, so // it's better to report it as an unknown type
this->error(typeName, "no type named '" + std::string(this->text(typeName)) + "'"); returnfalse;
}
this->nextToken();
TArray<SkSL::Field> fields; while (!this->checkNext(Token::Kind::TK_RBRACE)) {
Position fieldPos = this->position(this->peek());
Modifiers fieldModifiers = this->modifiers(); const Type* type = this->type(&fieldModifiers); if (!type) { returnfalse;
} do {
Token fieldName; if (!this->expectIdentifier(&fieldName)) { returnfalse;
} const Type* actualType = type; if (this->checkNext(Token::Kind::TK_LBRACKET)) {
Token sizeToken = this->peek(); if (sizeToken.fKind != Token::Kind::TK_RBRACKET) {
SKSL_INT size; if (!this->arraySize(&size)) { returnfalse;
}
actualType = this->arrayType(actualType, size, this->position(typeName));
} elseif (this->allowUnsizedArrays()) {
actualType = this->unsizedArrayType(actualType, this->position(typeName));
} else {
this->error(sizeToken, "unsized arrays are not permitted here");
}
this->expect(Token::Kind::TK_RBRACKET, "']'");
} if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) { returnfalse;
}
fields.push_back(SkSL::Field(this->rangeFrom(fieldPos),
fieldModifiers.fLayout,
fieldModifiers.fFlags,
this->text(fieldName),
actualType));
} while (this->checkNext(Token::Kind::TK_COMMA));
}
std::string_view instanceName;
Token instanceNameToken;
SKSL_INT size = 0; if (this->checkIdentifier(&instanceNameToken)) {
instanceName = this->text(instanceNameToken); if (this->checkNext(Token::Kind::TK_LBRACKET)) { if (!this->arraySize(&size)) { returnfalse;
}
this->expect(Token::Kind::TK_RBRACKET, "']'");
}
}
this->expect(Token::Kind::TK_SEMICOLON, "';'");
/* SWITCH LPAREN expression RPAREN LBRACE switchCase* (DEFAULT COLON statement*)? RBRACE */
std::unique_ptr<Statement> Parser::switchStatement() {
Token start; if (!this->expect(Token::Kind::TK_SWITCH, "'switch'", &start)) { return nullptr;
} if (!this->expect(Token::Kind::TK_LPAREN, "'('")) { return nullptr;
}
std::unique_ptr<Expression> value = this->expression(); if (!value) { return nullptr;
} if (!this->expect(Token::Kind::TK_RPAREN, "')'")) { return nullptr;
} if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) { return nullptr;
}
std::unique_ptr<SymbolTable> symbolTable;
ExpressionArray values;
StatementArray caseBlocks;
{ // Keeping a tight scope around AutoSymbolTable is important here. SwitchStatement::Convert // may end up creating a new symbol table if the HoistSwitchVarDeclarationsAtTopLevel // transform is used. We want ~AutoSymbolTable to happen first, so it can restore the // context's active symbol table to the enclosing block instead of the switch's inner block.
AutoSymbolTable symbols(this, &symbolTable);
while (this->peek().fKind == Token::Kind::TK_CASE) { if (!this->switchCase(&values, &caseBlocks)) { return nullptr;
}
} // Requiring `default:` to be last (in defiance of C and GLSL) was a deliberate decision. // Other parts of the compiler are allowed to rely upon this assumption. if (this->checkNext(Token::Kind::TK_DEFAULT)) { if (!this->switchCaseBody(&values, &caseBlocks, /*value=*/nullptr)) { return nullptr;
}
} if (!this->expect(Token::Kind::TK_RBRACE, "'}'")) { return nullptr;
}
}
Token nextToken = this->peek(); if (nextToken.fKind == Token::Kind::TK_SEMICOLON) { // An empty init-statement.
firstSemicolonOffset = this->nextToken().fOffset;
} else { // The init-statement must be an expression or variable declaration.
initializer = this->varDeclarationsOrExpressionStatement(); if (!initializer) { return nullptr;
}
firstSemicolonOffset = fLexer.getCheckpoint().fOffset - 1;
} if (this->peek().fKind != Token::Kind::TK_SEMICOLON) {
test = this->expression(); if (!test) { return nullptr;
}
} if (!this->expect(Token::Kind::TK_SEMICOLON, "';'", &secondSemicolon)) { return nullptr;
} if (this->peek().fKind != Token::Kind::TK_RPAREN) {
next = this->expression(); if (!next) { return nullptr;
}
} if (!this->expect(Token::Kind::TK_RPAREN, "')'", &rparen)) { return nullptr;
}
statement = this->statement(/*bracesIntroduceNewScope=*/false); if (!statement) { return nullptr;
}
}
Position pos = this->rangeFrom(start);
ForLoopPositions loopPositions{
range_of_at_least_one_char(lparen.fOffset + 1, firstSemicolonOffset),
range_of_at_least_one_char(firstSemicolonOffset + 1, secondSemicolon.fOffset),
range_of_at_least_one_char(secondSemicolon.fOffset + 1, rparen.fOffset),
}; return this->statementOrNop(pos, ForStatement::Convert(fCompiler.context(), pos, loopPositions,
std::move(initializer),
std::move(test),
std::move(next),
std::move(statement),
std::move(symbolTable)));
}
/* RETURN expression? SEMICOLON */
std::unique_ptr<Statement> Parser::returnStatement() {
Token start; if (!this->expect(Token::Kind::TK_RETURN, "'return'", &start)) { return nullptr;
}
std::unique_ptr<Expression> expression; if (this->peek().fKind != Token::Kind::TK_SEMICOLON) {
expression = this->expression(); if (!expression) { return nullptr;
}
} if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) { return nullptr;
} // We do not check for errors, or coerce the value to the correct type, until the return // statement is actually added to a function. (This is done in FunctionDefinition::Convert.) return ReturnStatement::Make(this->rangeFrom(start), std::move(expression));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.