Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/skia/skia/src/sksl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 89 kB image not shown  

Quelle  SkSLParser.cpp   Sprache: C

 
/*
 * Copyright 2021 Google LLC.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#include "src/sksl/SkSLParser.h"

#include "include/core/SkSpan.h"
#include "include/private/base/SkTArray.h"
#include "include/sksl/SkSLVersion.h"
#include "src/base/SkEnumBitMask.h"
#include "src/base/SkNoDestructor.h"
#include "src/core/SkTHash.h"
#include "src/sksl/SkSLBuiltinTypes.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLConstantFolder.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLModule.h"
#include "src/sksl/SkSLOperator.h"
#include "src/sksl/SkSLString.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLBlock.h"
#include "src/sksl/ir/SkSLBreakStatement.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
#include "src/sksl/ir/SkSLExtension.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLFunctionCall.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLFunctionPrototype.h"
#include "src/sksl/ir/SkSLIfStatement.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLInterfaceBlock.h"
#include "src/sksl/ir/SkSLLayout.h"
#include "src/sksl/ir/SkSLLiteral.h"
#include "src/sksl/ir/SkSLModifierFlags.h"
#include "src/sksl/ir/SkSLModifiersDeclaration.h"
#include "src/sksl/ir/SkSLNop.h"
#include "src/sksl/ir/SkSLPoison.h"
#include "src/sksl/ir/SkSLPostfixExpression.h"
#include "src/sksl/ir/SkSLPrefixExpression.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLProgramElement.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLStatement.h"
#include "src/sksl/ir/SkSLStructDefinition.h"
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLSymbol.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLTypeReference.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"

#include <algorithm>
#include <climits>
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>

using namespace skia_private;

namespace SkSL {

static constexpr int kMaxParseDepth = 50;

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) {}

    ~AutoDepth() {
        fParser->fDepth -= fDepth;
    }

    bool increase() {
        ++fDepth;
        ++fParser->fDepth;
        if (fParser->fDepth > kMaxParseDepth) {
            fParser->error(fParser->peek(), "exceeded max parse depth");
            fParser->fEncounteredFatalError = true;
            return false;
        }
        return true;
    }

private:
    Parser* fParser;
    int fDepth;
};

class Parser::AutoSymbolTable {
public:
    AutoSymbolTable(Parser* p, std::unique_ptr<SymbolTable>* newSymbolTable, bool enable = true) {
        if (enable) {
            fParser = p;
            SymbolTable*& ctxSymbols = this->contextSymbolTable();
            *newSymbolTable = std::make_unique<SymbolTable>(ctxSymbols, ctxSymbols->isBuiltin());
            ctxSymbols = newSymbolTable->get();
        }
    }

    ~AutoSymbolTable() {
        if (fParser) {
            SymbolTable*& ctxSymbols = this->contextSymbolTable();
            ctxSymbols = ctxSymbols->fParent;
        }
    }

private:
    SymbolTable*& contextSymbolTable() { return fParser->fCompiler.context().fSymbolTable; }

    Parser* fParser = nullptr;
};

class Parser::Checkpoint {
public:
    Checkpoint(Parser* p) : fParser(p) {
        Context& context = fParser->fCompiler.context();
        fPushbackCheckpoint = fParser->fPushback;
        fLexerCheckpoint = fParser->fLexer.getCheckpoint();
        fOldErrorReporter = context.fErrors;
        fOldEncounteredFatalError = fParser->fEncounteredFatalError;
        SkASSERT(fOldErrorReporter);
        context.setErrorReporter(&fErrorReporter);
    }

    ~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);
    }

    void rewind() {
        this->restoreErrorReporter();
        fParser->fPushback = fPushbackCheckpoint;
        fParser->fLexer.rewindToCheckpoint(fLexerCheckpoint);
        fParser->fEncounteredFatalError = fOldEncounteredFatalError;
    }

private:
    class ForwardingErrorReporter : public ErrorReporter {
    public:
        void handleError(std::string_view msg, Position pos) override {
            fErrors.push_back({std::string(msg), pos});
        }

        void forwardErrors(Parser* parser) {
            for (const Error& error : fErrors) {
                parser->error(error.fPos, error.fMsg);
            }
        }

    private:
        struct Error {
            std::string fMsg;
            Position fPos;
        };

        skia_private::TArray<Error> fErrors;
    };

    void restoreErrorReporter() {
        SkASSERT(fOldErrorReporter);
        fParser->fCompiler.context().setErrorReporter(fOldErrorReporter);
        fOldErrorReporter = nullptr;
    }

    Parser* fParser;
    Token fPushbackCheckpoint;
    SkSL::Lexer::Checkpoint fLexerCheckpoint;
    ForwardingErrorReporter fErrorReporter;
    ErrorReporter* fOldErrorReporter;
    bool fOldEncounteredFatalError;
};

Parser::Parser(Compiler* compiler,
               const ProgramSettings& settings,
               ProgramKind kind,
               std::unique_ptr<std::string> text)
        : fCompiler(*compiler)
        , fSettings(settings)
        , fKind(kind)
        , fText(std::move(text))
        , fPushback(Token::Kind::TK_NONE, /*offset=*/-1, /*length=*/-1) {
    fLexer.start(*fText);
}

Parser::~Parser() = default;

SymbolTable* Parser::symbolTable() {
    return fCompiler.symbolTable();
}

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;
}

static bool is_whitespace(Token::Kind kind) {
    switch (kind) {
        case Token::Kind::TK_WHITESPACE:
        case Token::Kind::TK_LINE_COMMENT:
        case Token::Kind::TK_BLOCK_COMMENT:
            return true;

        default:
            return false;
    }
}

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) {
            return true;
        }
    }
    // We didn't find a newline.
    this->pushback(token);
    return false;
}

Token Parser::nextToken() {
    for (;;) {
        Token token = this->nextRawToken();
        if (!is_whitespace(token.fKind)) {
            return token;
        }
    }
}

void Parser::pushback(Token t) {
    SkASSERT(fPushback.fKind == Token::Kind::TK_NONE);
    fPushback = t;
}

Token Parser::peek() {
    if (fPushback.fKind == Token::Kind::TK_NONE) {
        fPushback = this->nextToken();
    }
    return fPushback;
}

bool Parser::checkNext(Token::Kind kind, Token* result) {
    if (fPushback.fKind != Token::Kind::TK_NONE && fPushback.fKind != kind) {
        return false;
    }
    Token next = this->nextToken();
    if (next.fKind == kind) {
        if (result) {
            *result = next;
        }
        return true;
    }
    this->pushback(next);
    return false;
}

bool Parser::expect(Token::Kind kind, const char* expected, Token* result) {
    Token next = this->nextToken();
    if (next.fKind == kind) {
        if (result) {
            *result = next;
        }
        return true;
    } else {
        this->error(next, "expected " + std::string(expected) + ", but found '" +
                          std::string(this->text(next)) + "'");
        this->fEncounteredFatalError = true;
        return false;
    }
}

bool Parser::expectIdentifier(Token* result) {
    if (!this->expect(Token::Kind::TK_IDENTIFIER, "an identifier", result)) {
        return false;
    }
    if (this->symbolTable()->isBuiltinType(this->text(*result))) {
        this->error(*result, "expected an identifier, but found type '" +
                             std::string(this->text(*result)) + "'");
        this->fEncounteredFatalError = true;
        return false;
    }
    return true;
}

bool Parser::checkIdentifier(Token* result) {
    if (!this->checkNext(Token::Kind::TK_IDENTIFIER, result)) {
        return false;
    }
    if (this->symbolTable()->isBuiltinType(this->text(*result))) {
        this->pushback(*result);
        return false;
    }
    return true;
}

std::string_view Parser::text(Token token) {
    return std::string_view(fText->data() + token.fOffset, token.fLength);
}

Position Parser::position(Token t) {
    if (t.fOffset >= 0) {
        return Position::Range(t.fOffset, t.fOffset + t.fLength);
    } else {
        return Position();
    }
}

void Parser::error(Token token, std::string_view msg) {
    this->error(this->position(token), msg);
}

void Parser::error(Position position, std::string_view msg) {
    fCompiler.context().fErrors->error(position, msg);
}

Position Parser::rangeFrom(Position start) {
    int offset = fPushback.fKind != Token::Kind::TK_NONE ? fPushback.fOffset
                                                         : fLexer.getCheckpoint().fOffset;
    return Position::Range(start.startOffset(), offset);
}

Position Parser::rangeFrom(Token start) {
    return this->rangeFrom(this->position(start));
}

/* declaration* END_OF_FILE */
std::unique_ptr<Program> Parser::programInheritingFrom(const SkSL::Module* module) {
    this->declarations();
    std::unique_ptr<Program> result;
    if (fCompiler.errorReporter().errorCount() == 0) {
        result = fCompiler.releaseProgram(std::move(fText), std::move(fProgramElements));
    } else {
        fProgramElements.clear();
    }
    return result;
}

std::unique_ptr<SkSL::Module> Parser::moduleInheritingFrom(const SkSL::Module* parentModule) {
    this->declarations();
    this->symbolTable()->takeOwnershipOfString(std::move(*fText));
    auto result = std::make_unique<SkSL::Module>();
    result->fParent = parentModule;
    result->fSymbols = std::move(fCompiler.fGlobalSymbols);
    result->fElements = std::move(fProgramElements);
    result->fModuleType = fCompiler.context().fConfig->fModuleType;
    return result;
}

void Parser::declarations() {
    fEncounteredFatalError = false;

    // 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");
    }
}

/* DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER NEWLINE |
   DIRECTIVE(#version) INTLITERAL NEWLINE */

void Parser::directive(bool allowVersion) {
    Token start;
    if (!this->expect(Token::Kind::TK_DIRECTIVE, "a directive", &start)) {
        return;
    }
    std::string_view text = this->text(start);
    if (text == "#extension") {
        return this->extensionDirective(this->position(start));
    }
    if (text == "#version") {
        return this->versionDirective(this->position(start), allowVersion);
    }
    this->error(start, "unsupported directive '" + std::string(this->text(start)) + "'");
}

bool Parser::modifiersDeclarationEnd(const SkSL::Modifiers& mods) {
    std::unique_ptr<ModifiersDeclaration> decl = ModifiersDeclaration::Convert(fCompiler.context(),
                                                                               mods);
    if (!decl) {
        return false;
    }
    fProgramElements.push_back(std::move(decl));
    return true;
}

/* modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter (COMMA parameter)* RPAREN
   (block | SEMICOLON)) | SEMICOLON) | interfaceBlock) */

bool Parser::declaration() {
    Token start = this->peek();
    if (start.fKind == Token::Kind::TK_SEMICOLON) {
        this->nextToken();
        this->error(start, "expected a declaration, but found ';'");
        return false;
    }
    Modifiers modifiers = this->modifiers();
    Token lookahead = this->peek();
    if (lookahead.fKind == Token::Kind::TK_IDENTIFIER &&
        !this->symbolTable()->isType(this->text(lookahead))) {
        // we have an identifier that's not a type, could be the start of an interface block
        return this->interfaceBlock(modifiers);
    }
    if (lookahead.fKind == Token::Kind::TK_SEMICOLON) {
        this->nextToken();
        return this->modifiersDeclarationEnd(modifiers);
    }
    if (lookahead.fKind == Token::Kind::TK_STRUCT) {
        this->structVarDeclaration(this->position(start), modifiers);
        return true;
    }
    const Type* type = this->type(&modifiers);
    if (!type) {
        return false;
    }
    Token name;
    if (!this->expectIdentifier(&name)) {
        return false;
    }
    if (this->checkNext(Token::Kind::TK_LPAREN)) {
        return this->functionDeclarationEnd(this->position(start), modifiers, type, name);
    } else {
        this->globalVarDeclarationEnd(this->position(start), modifiers, type, name);
        return true;
    }
}

/* (RPAREN | VOID RPAREN | parameter (COMMA parameter)* RPAREN) (block | SEMICOLON) */
bool Parser::functionDeclarationEnd(Position start,
                                    Modifiers& modifiers,
                                    const Type* returnType,
                                    const Token& name) {
    Token lookahead = this->peek();
    bool validParams = true;
    STArray<8, std::unique_ptr<Variable>> parameters;
    if (lookahead.fKind == Token::Kind::TK_RPAREN) {
        // `()` means no parameters at all.
    } else if (lookahead.fKind == Token::Kind::TK_IDENTIFIER && this->text(lookahead) == "void"{
        // `(void)` also means no parameters at all.
        this->nextToken();
    } else {
        for (;;) {
            std::unique_ptr<SkSL::Variable> param;
            if (!this->parameter(¶m)) {
                return false;
            }
            validParams = validParams && param;
            parameters.push_back(std::move(param));
            if (!this->checkNext(Token::Kind::TK_COMMA)) {
                break;
            }
        }
    }
    if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
        return false;
    }

    SkSL::FunctionDeclaration* decl = nullptr;
    if (validParams) {
        decl = SkSL::FunctionDeclaration::Convert(fCompiler.context(),
                                                  this->rangeFrom(start),
                                                  modifiers,
                                                  this->text(name),
                                                  std::move(parameters),
                                                  start,
                                                  returnType);
    }

    if (this->checkNext(Token::Kind::TK_SEMICOLON)) {
        return this->prototypeFunction(decl);
    } else {
        return this->defineFunction(decl);
    }
}

bool Parser::prototypeFunction(SkSL::FunctionDeclaration* decl) {
    if (!decl) {
        return false;
    }
    fProgramElements.push_back(std::make_unique<SkSL::FunctionPrototype>(decl->fPosition, decl));
    return true;
}

bool Parser::defineFunction(SkSL::FunctionDeclaration* decl) {
    const Context& context = fCompiler.context();
    Token bodyStart = this->peek();

    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) {
        return false;
    }

    std::unique_ptr<SkSL::Statement> block = std::move(body);
    SkASSERT(block->is<Block>());
    Position pos = this->rangeFrom(bodyStart);
    block->fPosition = pos;

    std::unique_ptr<FunctionDefinition> function = FunctionDefinition::Convert(context,
                                                                               pos,
                                                                               *decl,
                                                                               std::move(block));
    if (!function) {
        return false;
    }
    decl->setDefinition(function.get());
    fProgramElements.push_back(std::move(function));
    return true;
}

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");
        return true;
    }
    std::unique_ptr<Expression> sizeLiteral = this->expression();
    if (!sizeLiteral) {
        return false;
    }
    if (!sizeLiteral->is<Poison>()) {
        SKSL_INT size;
        if (!ConstantFolder::GetConstantInt(*sizeLiteral, &size)) {
            this->error(sizeLiteral->fPosition, "array size must be an integer");
            return true;
        }
        if (size > INT32_MAX) {
            this->error(sizeLiteral->fPosition, "array size out of bounds");
            return true;
        }
        if (size <= 0) {
            this->error(sizeLiteral->fPosition, "array size must be positive");
            return true;
        }
        // Now that we've validated it, output the real value
        *outResult = size;
    }
    return true;
}

const Type* Parser::arrayType(const Type* base, int count, Position pos) {
    const Context& context = fCompiler.context();
    count = base->convertArraySize(context, pos, pos, count);
    if (!count) {
        return context.fTypes.fPoison.get();
    }
    return this->symbolTable()->addArrayDimension(fCompiler.context(), base, count);
}

const Type* Parser::unsizedArrayType(const Type* base, Position pos) {
    const Context& context = fCompiler.context();
    if (!base->checkIfUsableInArray(context, pos)) {
        return context.fTypes.fPoison.get();
    }
    return this->symbolTable()->addArrayDimension(fCompiler.context(), base,
                                                  SkSL::Type::kUnsizedArray);
}

bool Parser::parseArrayDimensions(Position pos, const Type** type) {
    Token next;
    while (this->checkNext(Token::Kind::TK_LBRACKET, &next)) {
        if (this->checkNext(Token::Kind::TK_RBRACKET)) {
            if (this->allowUnsizedArrays()) {
                *type = this->unsizedArrayType(*type, this->rangeFrom(pos));
            } else {
                this->error(this->rangeFrom(pos), "unsized arrays are not permitted here");
            }
        } else {
            SKSL_INT size;
            if (!this->arraySize(&size)) {
                return false;
            }
            if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
                return false;
            }
            *type = this->arrayType(*type, size, this->rangeFrom(pos));
        }
    }
    return true;
}

bool Parser::parseInitializer(Position pos, std::unique_ptr<Expression>* initializer) {
    if (this->checkNext(Token::Kind::TK_EQ)) {
        *initializer = this->assignmentExpression();
        return *initializer != nullptr;
    }
    return true;
}

void Parser::addGlobalVarDeclaration(std::unique_ptr<VarDeclaration> decl) {
    if (decl) {
        fProgramElements.push_back(std::make_unique<SkSL::GlobalVarDeclaration>(std::move(decl)));
    }
}

/* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
   (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */

void Parser::globalVarDeclarationEnd(Position pos,
                                     const Modifiers& mods,
                                     const Type* baseType,
                                     Token name) {
    const Type* type = baseType;
    std::unique_ptr<Expression> initializer;
    if (!this->parseArrayDimensions(pos, &type)) {
        return;
    }
    if (!this->parseInitializer(pos, &initializer)) {
        return;
    }
    this->addGlobalVarDeclaration(VarDeclaration::Convert(fCompiler.context(),
                                                          this->rangeFrom(pos),
                                                          mods,
                                                          *type,
                                                          this->position(name),
                                                          this->text(name),
                                                          VariableStorage::kGlobal,
                                                          std::move(initializer)));
    while (this->checkNext(Token::Kind::TK_COMMA)) {
        type = baseType;
        Token identifierName;
        if (!this->expectIdentifier(&identifierName)) {
            return;
        }
        if (!this->parseArrayDimensions(pos, &type)) {
            return;
        }
        std::unique_ptr<Expression> anotherInitializer;
        if (!this->parseInitializer(pos, &anotherInitializer)) {
            return;
        }
        this->addGlobalVarDeclaration(VarDeclaration::Convert(fCompiler.context(),
                                                              this->rangeFrom(identifierName),
                                                              mods,
                                                              *type,
                                                              this->position(identifierName),
                                                              this->text(identifierName),
                                                              VariableStorage::kGlobal,
                                                              std::move(anotherInitializer)));
    }
    this->expect(Token::Kind::TK_SEMICOLON, "';'");
}

/* (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)? (COMMA IDENTIFER
   (LBRACKET expression? RBRACKET)* (EQ assignmentExpression)?)* SEMICOLON */

std::unique_ptr<Statement> Parser::localVarDeclarationEnd(Position pos,
                                                          const Modifiers& mods,
                                                          const Type* baseType,
                                                          Token name) {
    const Type* type = baseType;
    std::unique_ptr<Expression> initializer;
    if (!this->parseArrayDimensions(pos, &type)) {
        return nullptr;
    }
    if (!this->parseInitializer(pos, &initializer)) {
        return nullptr;
    }
    std::unique_ptr<Statement> result = VarDeclaration::Convert(fCompiler.context(),
                                                                this->rangeFrom(pos),
                                                                mods,
                                                                *type,
                                                                this->position(name),
                                                                this->text(name),
                                                                VariableStorage::kLocal,
                                                                std::move(initializer));
    for (;;) {
        if (!this->checkNext(Token::Kind::TK_COMMA)) {
            this->expect(Token::Kind::TK_SEMICOLON, "';'");
            break;
        }
        type = baseType;
        Token identifierName;
        if (!this->expectIdentifier(&identifierName)) {
            break;
        }
        if (!this->parseArrayDimensions(pos, &type)) {
            break;
        }
        std::unique_ptr<Expression> anotherInitializer;
        if (!this->parseInitializer(pos, &anotherInitializer)) {
            break;
        }
        std::unique_ptr<Statement> next = VarDeclaration::Convert(fCompiler.context(),
                                                                  this->rangeFrom(identifierName),
                                                                  mods,
                                                                  *type,
                                                                  this->position(identifierName),
                                                                  this->text(identifierName),
                                                                  VariableStorage::kLocal,
                                                                  std::move(anotherInitializer));

        result = Block::MakeCompoundStatement(std::move(result), std::move(next));
    }
    pos = this->rangeFrom(pos);
    return this->statementOrNop(pos, std::move(result));
}

/* (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) {
        return false;
    }
    return this->expectIdentifier(&prefixData->fName);
}

/* modifiers type IDENTIFIER varDeclarationEnd */
std::unique_ptr<Statement> Parser::varDeclarations() {
    VarDeclarationsPrefix prefix;
    if (!this->varDeclarationsPrefix(&prefix)) {
        return nullptr;
    }
    return this->localVarDeclarationEnd(prefix.fPosition, prefix.fModifiers, prefix.fType,
                                        prefix.fName);
}

/* STRUCT IDENTIFIER LBRACE varDeclaration* RBRACE */
const Type* Parser::structDeclaration() {
    AutoDepth depth(this);
    Position start = this->position(this->peek());
    if (!this->expect(Token::Kind::TK_STRUCT, "'struct'")) {
        return nullptr;
    }
    Token name;
    if (!this->expectIdentifier(&name)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) {
        return nullptr;
    }
    if (!depth.increase()) {
        return nullptr;
    }
    TArray<SkSL::Field> fields;
    while (!this->checkNext(Token::Kind::TK_RBRACE)) {
        Token fieldStart = this->peek();
        Modifiers modifiers = this->modifiers();
        const Type* type = this->type(&modifiers);
        if (!type) {
            return nullptr;
        }

        do {
            const Type* actualType = type;
            Token memberName;
            if (!this->expectIdentifier(&memberName)) {
                return nullptr;
            }

            while (this->checkNext(Token::Kind::TK_LBRACKET)) {
                SKSL_INT size;
                if (!this->arraySize(&size)) {
                    return nullptr;
                }
                if (!this->expect(Token::Kind::TK_RBRACKET, "']'")) {
                    return nullptr;
                }
                actualType = this->arrayType(actualType, size,
                                             this->rangeFrom(this->position(fieldStart)));
            }

            fields.push_back(SkSL::Field(this->rangeFrom(fieldStart),
                                         modifiers.fLayout,
                                         modifiers.fFlags,
                                         this->text(memberName),
                                         actualType));
        } while (this->checkNext(Token::Kind::TK_COMMA));

        if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
            return nullptr;
        }
    }
    std::unique_ptr<SkSL::StructDefinition> def = StructDefinition::Convert(fCompiler.context(),
                                                                            this->rangeFrom(start),
                                                                            this->text(name),
                                                                            std::move(fields));
    if (!def) {
        return nullptr;
    }

    const Type* result = &def->type();
    fProgramElements.push_back(std::move(def));
    return result;
}

/* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */
void Parser::structVarDeclaration(Position start, const Modifiers& modifiers) {
    const Type* type = this->structDeclaration();
    if (!type) {
        return;
    }
    Token name;
    if (this->checkIdentifier(&name)) {
        this->globalVarDeclarationEnd(this->rangeFrom(name), modifiers, type, name);
    } else {
        this->expect(Token::Kind::TK_SEMICOLON, "';'");
    }
}

/* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */
bool Parser::parameter(std::unique_ptr<SkSL::Variable>* outParam) {
    Position pos = this->position(this->peek());
    Modifiers modifiers = this->modifiers();
    const Type* type = this->type(&modifiers);
    if (!type) {
        return false;
    }
    Token name;
    std::string_view nameText;
    Position namePos;
    if (this->checkIdentifier(&name)) {
        nameText = this->text(name);
        namePos = this->position(name);
    } else {
        namePos = this->rangeFrom(pos);
    }
    if (!this->parseArrayDimensions(pos, &type)) {
        return false;
    }
    *outParam = SkSL::Variable::Convert(fCompiler.context(),
                                        this->rangeFrom(pos),
                                        modifiers.fPosition,
                                        modifiers.fLayout,
                                        modifiers.fFlags,
                                        type,
                                        namePos,
                                        nameText,
                                        VariableStorage::kParameter);
    return true;
}

/** EQ INT_LITERAL */
int Parser::layoutInt() {
    if (!this->expect(Token::Kind::TK_EQ, "'='")) {
        return -1;
    }
    Token resultToken;
    if (!this->expect(Token::Kind::TK_INT_LITERAL, "a non-negative integer", &resultToken)) {
        return -1;
    }
    std::string_view resultFrag = this->text(resultToken);
    SKSL_INT resultValue;
    if (!SkSL::stoi(resultFrag, &resultValue)) {
        this->error(resultToken, "value in layout is too large: " + std::string(resultFrag));
        return -1;
    }
    return resultValue;
}

/** EQ IDENTIFIER */
std::string_view Parser::layoutIdentifier() {
    if (!this->expect(Token::Kind::TK_EQ, "'='")) {
        return {};
    }
    Token resultToken;
    if (!this->expectIdentifier(&resultToken)) {
        return {};
    }
    return this->text(resultToken);
}

/* LAYOUT LPAREN IDENTIFIER (EQ INT_LITERAL)? (COMMA IDENTIFIER (EQ INT_LITERAL)?)* RPAREN */
SkSL::Layout Parser::layout() {
    using LayoutMap = THashMap<std::string_view, SkSL::LayoutFlag>;
    static SkNoDestructor<LayoutMap> sLayoutTokens(LayoutMap{
            {"location",                    SkSL::LayoutFlag::kLocation},
            {"offset",                      SkSL::LayoutFlag::kOffset},
            {"binding",                     SkSL::LayoutFlag::kBinding},
            {"texture",                     SkSL::LayoutFlag::kTexture},
            {"sampler",                     SkSL::LayoutFlag::kSampler},
            {"index",                       SkSL::LayoutFlag::kIndex},
            {"set",                         SkSL::LayoutFlag::kSet},
            {"builtin",                     SkSL::LayoutFlag::kBuiltin},
            {"input_attachment_index",      SkSL::LayoutFlag::kInputAttachmentIndex},
            {"origin_upper_left",           SkSL::LayoutFlag::kOriginUpperLeft},
            {"blend_support_all_equations", SkSL::LayoutFlag::kBlendSupportAllEquations},
            {"push_constant",               SkSL::LayoutFlag::kPushConstant},
            {"color",                       SkSL::LayoutFlag::kColor},
            {"vulkan",                      SkSL::LayoutFlag::kVulkan},
            {"metal",                       SkSL::LayoutFlag::kMetal},
            {"webgpu",                      SkSL::LayoutFlag::kWebGPU},
            {"direct3d",                    SkSL::LayoutFlag::kDirect3D},
            {"rgba8",                       SkSL::LayoutFlag::kRGBA8},
            {"rgba32f",                     SkSL::LayoutFlag::kRGBA32F},
            {"r32f",                        SkSL::LayoutFlag::kR32F},
            {"local_size_x",                SkSL::LayoutFlag::kLocalSizeX},
            {"local_size_y",                SkSL::LayoutFlag::kLocalSizeY},
            {"local_size_z",                SkSL::LayoutFlag::kLocalSizeZ},
    });

    Layout result;
    if (this->checkNext(Token::Kind::TK_LAYOUT) &&
        this->expect(Token::Kind::TK_LPAREN, "'('")) {

        for (;;) {
            Token t = this->nextToken();
            std::string_view text = this->text(t);
            SkSL::LayoutFlag* found = sLayoutTokens->find(text);

            if (!found) {
                this->error(t, "'" + std::string(text) + "' is not a valid layout qualifier");
            } else {
                if (result.fFlags & *found) {
                    this->error(t, "layout qualifier '" + std::string(text) +
                                   "' appears more than once");
                }

                result.fFlags |= *found;

                switch (*found) {
                    case SkSL::LayoutFlag::kLocation:
                        result.fLocation = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kOffset:
                        result.fOffset = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kBinding:
                        result.fBinding = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kIndex:
                        result.fIndex = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kSet:
                        result.fSet = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kTexture:
                        result.fTexture = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kSampler:
                        result.fSampler = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kBuiltin:
                        result.fBuiltin = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kInputAttachmentIndex:
                        result.fInputAttachmentIndex = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kLocalSizeX:
                        result.fLocalSizeX = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kLocalSizeY:
                        result.fLocalSizeY = this->layoutInt();
                        break;
                    case SkSL::LayoutFlag::kLocalSizeZ:
                        result.fLocalSizeZ = this->layoutInt();
                        break;
                    default:
                        break;
                }
            }
            if (this->checkNext(Token::Kind::TK_RPAREN)) {
                break;
            }
            if (!this->expect(Token::Kind::TK_COMMA, "','")) {
                break;
            }
        }
    }
    return result;
}

/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE |
            VARYING | INLINE | WORKGROUP | READONLY | WRITEONLY | BUFFER)* */

Modifiers Parser::modifiers() {
    int start = this->peek().fOffset;
    SkSL::Layout layout = this->layout();
    Token raw = this->nextRawToken();
    int end = raw.fOffset;
    if (!is_whitespace(raw.fKind)) {
        this->pushback(raw);
    }
    ModifierFlags flags = ModifierFlag::kNone;
    for (;;) {
        ModifierFlags tokenFlag = parse_modifier_token(peek().fKind);
        if (tokenFlag == ModifierFlag::kNone) {
            break;
        }
        Token modifier = this->nextToken();
        if (ModifierFlags duplicateFlags = (tokenFlag & flags)) {
            this->error(modifier, "'" + duplicateFlags.description() + "' appears more than once");
        }
        flags |= tokenFlag;
        end = this->position(modifier).endOffset();
    }
    return Modifiers{Position::Range(start, end), layout, flags};
}

std::unique_ptr<Statement> Parser::statementOrNop(Position pos, std::unique_ptr<Statement> stmt) {
    if (!stmt) {
        stmt = Nop::Make();
    }
    if (pos.valid() && !stmt->position().valid()) {
        stmt->setPosition(pos);
    }
    return stmt;
}

/* 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)) {
        return false;
    }
    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)) + "'");
        return false;
    }
    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) {
            return false;
        }
        do {
            Token fieldName;
            if (!this->expectIdentifier(&fieldName)) {
                return false;
            }
            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)) {
                        return false;
                    }
                    actualType = this->arrayType(actualType, size, this->position(typeName));
                } else if (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, "';'")) {
                return false;
            }

            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)) {
                return false;
            }
            this->expect(Token::Kind::TK_RBRACKET, "']'");
        }
    }
    this->expect(Token::Kind::TK_SEMICOLON, "';'");

    if (std::unique_ptr<SkSL::InterfaceBlock> ib = InterfaceBlock::Convert(fCompiler.context(),
                                                                           this->position(typeName),
                                                                           modifiers,
                                                                           this->text(typeName),
                                                                           std::move(fields),
                                                                           instanceName,
                                                                           size)) {
        fProgramElements.push_back(std::move(ib));
        return true;
    }
    return false;
}

/* IF LPAREN expression RPAREN statement (ELSE statement)? */
std::unique_ptr<Statement> Parser::ifStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_IF, "'if'", &start)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
        return nullptr;
    }
    std::unique_ptr<Expression> test = this->expression();
    if (!test) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
        return nullptr;
    }
    std::unique_ptr<Statement> ifTrue = this->statement();
    if (!ifTrue) {
        return nullptr;
    }
    std::unique_ptr<Statement> ifFalse;
    if (this->checkNext(Token::Kind::TK_ELSE)) {
        ifFalse = this->statement();
        if (!ifFalse) {
            return nullptr;
        }
    }
    Position pos = this->rangeFrom(start);
    return this->statementOrNop(pos, IfStatement::Convert(fCompiler.context(),
                                                          pos,
                                                          std::move(test),
                                                          std::move(ifTrue),
                                                          std::move(ifFalse)));
}

/* DO statement WHILE LPAREN expression RPAREN SEMICOLON */
std::unique_ptr<Statement> Parser::doStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_DO, "'do'", &start)) {
        return nullptr;
    }
    std::unique_ptr<Statement> statement = this->statement();
    if (!statement) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_WHILE, "'while'")) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
        return nullptr;
    }
    std::unique_ptr<Expression> test = this->expression();
    if (!test) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
        return nullptr;
    }
    Position pos = this->rangeFrom(start);
    return this->statementOrNop(pos, DoStatement::Convert(fCompiler.context(), pos,
                                                          std::move(statement), std::move(test)));
}

/* WHILE LPAREN expression RPAREN STATEMENT */
std::unique_ptr<Statement> Parser::whileStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_WHILE, "'while'", &start)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_LPAREN, "'('")) {
        return nullptr;
    }
    std::unique_ptr<Expression> test = this->expression();
    if (!test) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_RPAREN, "')'")) {
        return nullptr;
    }
    std::unique_ptr<Statement> statement = this->statement();
    if (!statement) {
        return nullptr;
    }
    Position pos = this->rangeFrom(start);
    return this->statementOrNop(pos, ForStatement::ConvertWhile(fCompiler.context(), pos,
                                                                std::move(test),
                                                                std::move(statement)));
}

/* COLON statement* */
bool Parser::switchCaseBody(ExpressionArray* values,
                            StatementArray* caseBlocks,
                            std::unique_ptr<Expression> caseValue) {
    if (!this->expect(Token::Kind::TK_COLON, "':'")) {
        return false;
    }
    StatementArray statements;
    while (this->peek().fKind != Token::Kind::TK_RBRACE &&
           this->peek().fKind != Token::Kind::TK_CASE &&
           this->peek().fKind != Token::Kind::TK_DEFAULT) {
        std::unique_ptr<Statement> s = this->statement();
        if (!s) {
            return false;
        }
        statements.push_back(std::move(s));
    }
    values->push_back(std::move(caseValue));
    caseBlocks->push_back(SkSL::Block::Make(Position(), std::move(statements),
                                            Block::Kind::kUnbracedBlock));
    return true;
}

/* CASE expression COLON statement* */
bool Parser::switchCase(ExpressionArray* values, StatementArray* caseBlocks) {
    Token start;
    if (!this->expect(Token::Kind::TK_CASE, "'case'", &start)) {
        return false;
    }
    std::unique_ptr<Expression> caseValue = this->expression();
    if (!caseValue) {
        return false;
    }
    return this->switchCaseBody(values, caseBlocks, std::move(caseValue));
}

/* 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;
        }
    }

    Position pos = this->rangeFrom(start);
    return this->statementOrNop(pos, SwitchStatement::Convert(fCompiler.context(), pos,
                                                              std::move(value),
                                                              std::move(values),
                                                              std::move(caseBlocks),
                                                              std::move(symbolTable)));
}

static Position range_of_at_least_one_char(int start, int end) {
    return Position::Range(start, std::max(end, start + 1));
}

/* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN
   STATEMENT */

std::unique_ptr<Statement> Parser::forStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_FOR, "'for'", &start)) {
        return nullptr;
    }
    Token lparen;
    if (!this->expect(Token::Kind::TK_LPAREN, "'('", &lparen)) {
        return nullptr;
    }
    std::unique_ptr<SymbolTable> symbolTable;
    std::unique_ptr<Statement> initializer;
    std::unique_ptr<Expression> test;
    std::unique_ptr<Expression> next;
    std::unique_ptr<Statement> statement;
    int firstSemicolonOffset;
    Token secondSemicolon;
    Token rparen;
    {
        AutoSymbolTable symbols(this, &symbolTable);

        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));
}

/* BREAK SEMICOLON */
std::unique_ptr<Statement> Parser::breakStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_BREAK, "'break'", &start)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
        return nullptr;
    }
    return SkSL::BreakStatement::Make(this->position(start));
}

/* CONTINUE SEMICOLON */
std::unique_ptr<Statement> Parser::continueStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_CONTINUE, "'continue'", &start)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
        return nullptr;
    }
    return SkSL::ContinueStatement::Make(this->position(start));
}

/* DISCARD SEMICOLON */
std::unique_ptr<Statement> Parser::discardStatement() {
    Token start;
    if (!this->expect(Token::Kind::TK_DISCARD, "'continue'", &start)) {
        return nullptr;
    }
    if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) {
        return nullptr;
    }
    Position pos = this->position(start);
    return this->statementOrNop(pos, SkSL::DiscardStatement::Convert(fCompiler.context(), pos));
}

/* LBRACE statement* RBRACE */
std::unique_ptr<Statement> Parser::block(bool introduceNewScope,
                                         std::unique_ptr<SymbolTable>* adoptExistingSymbolTable) {
    // We can't introduce a new scope _and_ adopt an existing symbol table.
    SkASSERT(!(introduceNewScope && adoptExistingSymbolTable));

    AutoDepth depth(this);
    Token start;
    if (!this->expect(Token::Kind::TK_LBRACE, "'{'", &start)) {
        return nullptr;
    }
    if (!depth.increase()) {
        return nullptr;
    }

    std::unique_ptr<SymbolTable> newSymbolTable;
    std::unique_ptr<SymbolTable>* symbolTableToUse =
            adoptExistingSymbolTable ? adoptExistingSymbolTable : &newSymbolTable;

    StatementArray statements;
    {
        AutoSymbolTable symbols(this, symbolTableToUse, /*enable=*/introduceNewScope);

        // Consume statements until we reach the closing brace.
        for (;;) {
            Token::Kind tokenKind = this->peek().fKind;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=95 G=94

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.