/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "stdio.h" #include "SkSLParser.h" #include "SkSLToken.h" #define register #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunneeded-internal-declaration" #pragma clang diagnostic ignored "-Wnull-conversion" #pragma clang diagnostic ignored "-Wsign-compare" #endif #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4018) #endif #include "lex.sksl.c" #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #ifdef _MSC_VER #pragma warning(pop) #endif #undef register #include "ast/SkSLASTBinaryExpression.h" #include "ast/SkSLASTBlock.h" #include "ast/SkSLASTBoolLiteral.h" #include "ast/SkSLASTBreakStatement.h" #include "ast/SkSLASTCallSuffix.h" #include "ast/SkSLASTContinueStatement.h" #include "ast/SkSLASTDiscardStatement.h" #include "ast/SkSLASTDoStatement.h" #include "ast/SkSLASTExpression.h" #include "ast/SkSLASTExpressionStatement.h" #include "ast/SkSLASTExtension.h" #include "ast/SkSLASTFieldSuffix.h" #include "ast/SkSLASTFloatLiteral.h" #include "ast/SkSLASTForStatement.h" #include "ast/SkSLASTFunction.h" #include "ast/SkSLASTIdentifier.h" #include "ast/SkSLASTIfStatement.h" #include "ast/SkSLASTIndexSuffix.h" #include "ast/SkSLASTInterfaceBlock.h" #include "ast/SkSLASTIntLiteral.h" #include "ast/SkSLASTModifiersDeclaration.h" #include "ast/SkSLASTParameter.h" #include "ast/SkSLASTPrecision.h" #include "ast/SkSLASTPrefixExpression.h" #include "ast/SkSLASTReturnStatement.h" #include "ast/SkSLASTStatement.h" #include "ast/SkSLASTSuffixExpression.h" #include "ast/SkSLASTTernaryExpression.h" #include "ast/SkSLASTType.h" #include "ast/SkSLASTVarDeclaration.h" #include "ast/SkSLASTVarDeclarationStatement.h" #include "ast/SkSLASTWhileStatement.h" #include "ir/SkSLSymbolTable.h" #include "ir/SkSLModifiers.h" #include "ir/SkSLType.h" namespace SkSL { #define MAX_PARSE_DEPTH 50 class AutoDepth { public: AutoDepth(Parser* p) : fParser(p) { fParser->fDepth++; } ~AutoDepth() { fParser->fDepth--; } bool checkValid() { if (fParser->fDepth > MAX_PARSE_DEPTH) { fParser->error(fParser->peek().fPosition, SkString("exceeded max parse depth")); return false; } return true; } private: Parser* fParser; }; Parser::Parser(SkString text, SymbolTable& types, ErrorReporter& errors) : fPushback(Position(-1, -1), Token::INVALID_TOKEN, SkString()) , fTypes(types) , fErrors(errors) { sksllex_init(&fScanner); fBuffer = sksl_scan_string(text.c_str(), fScanner); skslset_lineno(1, fScanner); if (false) { // avoid unused warning yyunput(0, nullptr, fScanner); } fLayoutKeys[SkString("location")] = kLocation_LayoutKey; fLayoutKeys[SkString("offset")] = kOffset_LayoutKey; fLayoutKeys[SkString("binding")] = kBinding_LayoutKey; fLayoutKeys[SkString("index")] = kIndex_LayoutKey; fLayoutKeys[SkString("set")] = kSet_LayoutKey; fLayoutKeys[SkString("builtin")] = kBuiltin_LayoutKey; fLayoutKeys[SkString("input_attachment_index")] = kInputAttachmentIndex_LayoutKey; fLayoutKeys[SkString("origin_upper_left")] = kOriginUpperLeft_LayoutKey; fLayoutKeys[SkString("override_coverage")] = kOverrideCoverage_LayoutKey; fLayoutKeys[SkString("blend_support_all_equations")] = kBlendSupportAllEquations_LayoutKey; fLayoutKeys[SkString("push_constant")] = kPushConstant_LayoutKey; fLayoutKeys[SkString("points")] = kPoints_LayoutKey; fLayoutKeys[SkString("lines")] = kLines_LayoutKey; fLayoutKeys[SkString("line_strip")] = kLineStrip_LayoutKey; fLayoutKeys[SkString("lines_adjacency")] = kLinesAdjacency_LayoutKey; fLayoutKeys[SkString("triangles")] = kTriangles_LayoutKey; fLayoutKeys[SkString("triangle_strip")] = kTriangleStrip_LayoutKey; fLayoutKeys[SkString("triangles_adjacency")] = kTrianglesAdjacency_LayoutKey; fLayoutKeys[SkString("max_vertices")] = kMaxVertices_LayoutKey; fLayoutKeys[SkString("invocations")] = kInvocations_LayoutKey; } Parser::~Parser() { sksl_delete_buffer(fBuffer, fScanner); sksllex_destroy(fScanner); } /* (precision | directive | declaration)* END_OF_FILE */ std::vector> Parser::file() { std::vector> result; for (;;) { switch (this->peek().fKind) { case Token::END_OF_FILE: return result; case Token::PRECISION: { std::unique_ptr precision = this->precision(); if (precision) { result.push_back(std::move(precision)); } break; } case Token::DIRECTIVE: { std::unique_ptr decl = this->directive(); if (decl) { result.push_back(std::move(decl)); } break; } default: { std::unique_ptr decl = this->declaration(); if (!decl) { continue; } result.push_back(std::move(decl)); } } } } Token Parser::nextToken() { if (fPushback.fKind != Token::INVALID_TOKEN) { Token result = fPushback; fPushback.fKind = Token::INVALID_TOKEN; fPushback.fText = ""; return result; } int token = sksllex(fScanner); SkString text; switch ((Token::Kind) token) { case Token::IDENTIFIER: // fall through case Token::INT_LITERAL: // fall through case Token::FLOAT_LITERAL: // fall through case Token::DIRECTIVE: text = SkString(skslget_text(fScanner)); break; default: #ifdef SK_DEBUG text = SkString(skslget_text(fScanner)); #endif break; } return Token(Position(skslget_lineno(fScanner), -1), (Token::Kind) token, text); } void Parser::pushback(Token t) { ASSERT(fPushback.fKind == Token::INVALID_TOKEN); fPushback = t; } Token Parser::peek() { fPushback = this->nextToken(); return fPushback; } bool Parser::expect(Token::Kind kind, const char* expected, Token* result) { return this->expect(kind, SkString(expected), result); } bool Parser::expect(Token::Kind kind, SkString expected, Token* result) { Token next = this->nextToken(); if (next.fKind == kind) { if (result) { *result = next; } return true; } else { if (next.fText.size()) { this->error(next.fPosition, "expected " + expected + ", but found '" + next.fText + "'"); } else { this->error(next.fPosition, "parse error, recompile in debug mode for details"); } return false; } } void Parser::error(Position p, const char* msg) { this->error(p, SkString(msg)); } void Parser::error(Position p, SkString msg) { fErrors.error(p, msg); } bool Parser::isType(SkString name) { return nullptr != fTypes[name]; } /* PRECISION (LOWP | MEDIUMP | HIGHP) type SEMICOLON */ std::unique_ptr Parser::precision() { if (!this->expect(Token::PRECISION, "'precision'")) { return nullptr; } Modifiers::Flag result; Token p = this->nextToken(); switch (p.fKind) { case Token::LOWP: result = Modifiers::kLowp_Flag; break; case Token::MEDIUMP: result = Modifiers::kMediump_Flag; break; case Token::HIGHP: result = Modifiers::kHighp_Flag; break; default: this->error(p.fPosition, "expected 'lowp', 'mediump', or 'highp', but found '" + p.fText + "'"); return nullptr; } // FIXME handle the type if (!this->type()) { return nullptr; } this->expect(Token::SEMICOLON, "';'"); return std::unique_ptr(new ASTPrecision(p.fPosition, result)); } /* DIRECTIVE(#version) INT_LITERAL ("es" | "compatibility")? | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */ std::unique_ptr Parser::directive() { Token start; if (!this->expect(Token::DIRECTIVE, "a directive", &start)) { return nullptr; } if (start.fText == "#version") { this->expect(Token::INT_LITERAL, "a version number"); Token next = this->peek(); if (next.fText == "es" || next.fText == "compatibility") { this->nextToken(); } // version is ignored for now; it will eventually become an error when we stop pretending // to be GLSL return nullptr; } else if (start.fText == "#extension") { Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } if (!this->expect(Token::COLON, "':'")) { return nullptr; } // FIXME: need to start paying attention to this token if (!this->expect(Token::IDENTIFIER, "an identifier")) { return nullptr; } return std::unique_ptr(new ASTExtension(start.fPosition, std::move(name.fText))); } else { this->error(start.fPosition, "unsupported directive '" + start.fText + "'"); return nullptr; } } /* modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter (COMMA parameter)* RPAREN (block | SEMICOLON)) | SEMICOLON) | interfaceBlock) */ std::unique_ptr Parser::declaration() { Modifiers modifiers = this->modifiers(); Token lookahead = this->peek(); if (lookahead.fKind == Token::IDENTIFIER && !this->isType(lookahead.fText)) { // 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::STRUCT) { return this->structVarDeclaration(modifiers); } if (lookahead.fKind == Token::SEMICOLON) { this->nextToken(); return std::unique_ptr(new ASTModifiersDeclaration(modifiers)); } std::unique_ptr type(this->type()); if (!type) { return nullptr; } if (type->fKind == ASTType::kStruct_Kind && peek().fKind == Token::SEMICOLON) { this->nextToken(); return nullptr; } Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } if (!modifiers.fFlags && this->peek().fKind == Token::LPAREN) { this->nextToken(); std::vector> parameters; while (this->peek().fKind != Token::RPAREN) { if (parameters.size() > 0) { if (!this->expect(Token::COMMA, "','")) { return nullptr; } } std::unique_ptr parameter = this->parameter(); if (!parameter) { return nullptr; } parameters.push_back(std::move(parameter)); } this->nextToken(); std::unique_ptr body; if (this->peek().fKind == Token::SEMICOLON) { this->nextToken(); } else { body = this->block(); if (!body) { return nullptr; } } return std::unique_ptr(new ASTFunction(name.fPosition, std::move(type), std::move(name.fText), std::move(parameters), std::move(body))); } else { return this->varDeclarationEnd(modifiers, std::move(type), name.fText); } } /* modifiers type IDENTIFIER varDeclarationEnd */ std::unique_ptr Parser::varDeclarations() { Modifiers modifiers = this->modifiers(); std::unique_ptr type(this->type()); if (!type) { return nullptr; } Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } return this->varDeclarationEnd(modifiers, std::move(type), std::move(name.fText)); } /* STRUCT IDENTIFIER LBRACE varDeclaration* RBRACE */ std::unique_ptr Parser::structDeclaration() { if (!this->expect(Token::STRUCT, "'struct'")) { return nullptr; } Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } if (!this->expect(Token::LBRACE, "'{'")) { return nullptr; } std::vector fields; while (this->peek().fKind != Token::RBRACE) { std::unique_ptr decl = this->varDeclarations(); if (!decl) { return nullptr; } for (const auto& var : decl->fVars) { auto type = (const Type*) fTypes[decl->fType->fName]; for (int i = (int) var.fSizes.size() - 1; i >= 0; i--) { if (!var.fSizes[i] || var.fSizes[i]->fKind != ASTExpression::kInt_Kind) { this->error(decl->fPosition, "array size in struct field must be a constant"); return nullptr; } uint64_t columns = ((ASTIntLiteral&) *var.fSizes[i]).fValue; SkString name = type->name() + "[" + to_string(columns) + "]"; type = new Type(name, Type::kArray_Kind, *type, (int) columns); fTypes.takeOwnership((Type*) type); } fields.push_back(Type::Field(decl->fModifiers, var.fName, type)); if (var.fValue) { this->error(decl->fPosition, "initializers are not permitted on struct fields"); } } } if (!this->expect(Token::RBRACE, "'}'")) { return nullptr; } fTypes.add(name.fText, std::unique_ptr(new Type(name.fPosition, name.fText, fields))); return std::unique_ptr(new ASTType(name.fPosition, name.fText, ASTType::kStruct_Kind, std::vector())); } /* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */ std::unique_ptr Parser::structVarDeclaration(Modifiers modifiers) { std::unique_ptr type = this->structDeclaration(); if (!type) { return nullptr; } if (peek().fKind == Token::IDENTIFIER) { Token name = this->nextToken(); std::unique_ptr result = this->varDeclarationEnd(modifiers, std::move(type), std::move(name.fText)); if (result) { for (const auto& var : result->fVars) { if (var.fValue) { this->error(var.fValue->fPosition, "struct variables cannot be initialized"); } } } return result; } this->expect(Token::SEMICOLON, "';'"); return nullptr; } /* (LBRACKET expression? RBRACKET)* (EQ expression)? (COMMA IDENTIFER (LBRACKET expression? RBRACKET)* (EQ expression)?)* SEMICOLON */ std::unique_ptr Parser::varDeclarationEnd(Modifiers mods, std::unique_ptr type, SkString name) { std::vector vars; std::vector> currentVarSizes; while (this->peek().fKind == Token::LBRACKET) { this->nextToken(); if (this->peek().fKind == Token::RBRACKET) { this->nextToken(); currentVarSizes.push_back(nullptr); } else { std::unique_ptr size(this->expression()); if (!size) { return nullptr; } currentVarSizes.push_back(std::move(size)); if (!this->expect(Token::RBRACKET, "']'")) { return nullptr; } } } std::unique_ptr value; if (this->peek().fKind == Token::EQ) { this->nextToken(); value = this->expression(); if (!value) { return nullptr; } } vars.emplace_back(std::move(name), std::move(currentVarSizes), std::move(value)); while (this->peek().fKind == Token::COMMA) { this->nextToken(); Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } currentVarSizes.clear(); value.reset(); while (this->peek().fKind == Token::LBRACKET) { this->nextToken(); if (this->peek().fKind == Token::RBRACKET) { this->nextToken(); currentVarSizes.push_back(nullptr); } else { std::unique_ptr size(this->expression()); if (!size) { return nullptr; } currentVarSizes.push_back(std::move(size)); if (!this->expect(Token::RBRACKET, "']'")) { return nullptr; } } } if (this->peek().fKind == Token::EQ) { this->nextToken(); value = this->expression(); if (!value) { return nullptr; } } vars.emplace_back(std::move(name.fText), std::move(currentVarSizes), std::move(value)); } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTVarDeclarations(std::move(mods), std::move(type), std::move(vars))); } /* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */ std::unique_ptr Parser::parameter() { Modifiers modifiers = this->modifiersWithDefaults(Modifiers::kIn_Flag); std::unique_ptr type = this->type(); if (!type) { return nullptr; } Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } std::vector sizes; while (this->peek().fKind == Token::LBRACKET) { this->nextToken(); Token sizeToken; if (!this->expect(Token::INT_LITERAL, "a positive integer", &sizeToken)) { return nullptr; } sizes.push_back(SkSL::stoi(sizeToken.fText)); if (!this->expect(Token::RBRACKET, "']'")) { return nullptr; } } return std::unique_ptr(new ASTParameter(name.fPosition, modifiers, std::move(type), name.fText, std::move(sizes))); } /** (EQ INT_LITERAL)? */ int Parser::layoutInt() { if (!this->expect(Token::EQ, "'='")) { return -1; } Token resultToken; if (this->expect(Token::INT_LITERAL, "a non-negative integer", &resultToken)) { return SkSL::stoi(resultToken.fText); } return -1; } /* LAYOUT LPAREN IDENTIFIER (EQ INT_LITERAL)? (COMMA IDENTIFIER (EQ INT_LITERAL)?)* RPAREN */ Layout Parser::layout() { int location = -1; int offset = -1; int binding = -1; int index = -1; int set = -1; int builtin = -1; int inputAttachmentIndex = -1; bool originUpperLeft = false; bool overrideCoverage = false; bool blendSupportAllEquations = false; Layout::Format format = Layout::Format::kUnspecified; bool pushConstant = false; Layout::Primitive primitive = Layout::kUnspecified_Primitive; int maxVertices = -1; int invocations = -1; if (this->peek().fKind == Token::LAYOUT) { this->nextToken(); if (!this->expect(Token::LPAREN, "'('")) { return Layout(location, offset, binding, index, set, builtin, inputAttachmentIndex, originUpperLeft, overrideCoverage, blendSupportAllEquations, format, pushConstant, primitive, maxVertices, invocations); } for (;;) { Token t = this->nextToken(); auto found = fLayoutKeys.find(t.fText); if (found != fLayoutKeys.end()) { switch (found->second) { case kLocation_LayoutKey: location = this->layoutInt(); break; case kOffset_LayoutKey: offset = this->layoutInt(); break; case kBinding_LayoutKey: binding = this->layoutInt(); break; case kIndex_LayoutKey: index = this->layoutInt(); break; case kSet_LayoutKey: set = this->layoutInt(); break; case kBuiltin_LayoutKey: builtin = this->layoutInt(); break; case kInputAttachmentIndex_LayoutKey: inputAttachmentIndex = this->layoutInt(); break; case kOriginUpperLeft_LayoutKey: originUpperLeft = true; break; case kOverrideCoverage_LayoutKey: overrideCoverage = true; break; case kBlendSupportAllEquations_LayoutKey: blendSupportAllEquations = true; break; case kPushConstant_LayoutKey: pushConstant = true; break; case kPoints_LayoutKey: primitive = Layout::kPoints_Primitive; break; case kLines_LayoutKey: primitive = Layout::kLines_Primitive; break; case kLineStrip_LayoutKey: primitive = Layout::kLineStrip_Primitive; break; case kLinesAdjacency_LayoutKey: primitive = Layout::kLinesAdjacency_Primitive; break; case kTriangles_LayoutKey: primitive = Layout::kTriangles_Primitive; break; case kTriangleStrip_LayoutKey: primitive = Layout::kTriangleStrip_Primitive; break; case kTrianglesAdjacency_LayoutKey: primitive = Layout::kTrianglesAdjacency_Primitive; break; case kMaxVertices_LayoutKey: maxVertices = this->layoutInt(); break; case kInvocations_LayoutKey: invocations = this->layoutInt(); break; } } else if (Layout::ReadFormat(t.fText, &format)) { // AST::ReadFormat stored the result in 'format'. } else { this->error(t.fPosition, ("'" + t.fText + "' is not a valid layout qualifier").c_str()); } if (this->peek().fKind == Token::RPAREN) { this->nextToken(); break; } if (!this->expect(Token::COMMA, "','")) { break; } } } return Layout(location, offset, binding, index, set, builtin, inputAttachmentIndex, originUpperLeft, overrideCoverage, blendSupportAllEquations, format, pushConstant, primitive, maxVertices, invocations); } /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE | READONLY | WRITEONLY | COHERENT | VOLATILE | RESTRICT)* */ Modifiers Parser::modifiers() { Layout layout = this->layout(); int flags = 0; for (;;) { // TODO: handle duplicate / incompatible flags switch (peek().fKind) { case Token::UNIFORM: this->nextToken(); flags |= Modifiers::kUniform_Flag; break; case Token::CONST: this->nextToken(); flags |= Modifiers::kConst_Flag; break; case Token::IN: this->nextToken(); flags |= Modifiers::kIn_Flag; break; case Token::OUT: this->nextToken(); flags |= Modifiers::kOut_Flag; break; case Token::INOUT: this->nextToken(); flags |= Modifiers::kIn_Flag; flags |= Modifiers::kOut_Flag; break; case Token::LOWP: this->nextToken(); flags |= Modifiers::kLowp_Flag; break; case Token::MEDIUMP: this->nextToken(); flags |= Modifiers::kMediump_Flag; break; case Token::HIGHP: this->nextToken(); flags |= Modifiers::kHighp_Flag; break; case Token::FLAT: this->nextToken(); flags |= Modifiers::kFlat_Flag; break; case Token::NOPERSPECTIVE: this->nextToken(); flags |= Modifiers::kNoPerspective_Flag; break; case Token::READONLY: this->nextToken(); flags |= Modifiers::kReadOnly_Flag; break; case Token::WRITEONLY: this->nextToken(); flags |= Modifiers::kWriteOnly_Flag; break; case Token::COHERENT: this->nextToken(); flags |= Modifiers::kCoherent_Flag; break; case Token::VOLATILE: this->nextToken(); flags |= Modifiers::kVolatile_Flag; break; case Token::RESTRICT: this->nextToken(); flags |= Modifiers::kRestrict_Flag; break; default: return Modifiers(layout, flags); } } } Modifiers Parser::modifiersWithDefaults(int defaultFlags) { Modifiers result = this->modifiers(); if (!result.fFlags) { return Modifiers(result.fLayout, defaultFlags); } return result; } /* ifStatement | forStatement | doStatement | whileStatement | block | expression */ std::unique_ptr Parser::statement() { Token start = this->peek(); switch (start.fKind) { case Token::IF: return this->ifStatement(); case Token::FOR: return this->forStatement(); case Token::DO: return this->doStatement(); case Token::WHILE: return this->whileStatement(); case Token::RETURN: return this->returnStatement(); case Token::BREAK: return this->breakStatement(); case Token::CONTINUE: return this->continueStatement(); case Token::DISCARD: return this->discardStatement(); case Token::LBRACE: return this->block(); case Token::SEMICOLON: this->nextToken(); return std::unique_ptr(new ASTBlock(start.fPosition, std::vector>())); case Token::CONST: // fall through case Token::HIGHP: // fall through case Token::MEDIUMP: // fall through case Token::LOWP: { auto decl = this->varDeclarations(); if (!decl) { return nullptr; } return std::unique_ptr(new ASTVarDeclarationStatement(std::move(decl))); } case Token::IDENTIFIER: if (this->isType(start.fText)) { auto decl = this->varDeclarations(); if (!decl) { return nullptr; } return std::unique_ptr(new ASTVarDeclarationStatement( std::move(decl))); } // fall through default: return this->expressionStatement(); } } /* IDENTIFIER(type) (LBRACKET intLiteral? RBRACKET)* */ std::unique_ptr Parser::type() { Token type; if (!this->expect(Token::IDENTIFIER, "a type", &type)) { return nullptr; } if (!this->isType(type.fText)) { this->error(type.fPosition, ("no type named '" + type.fText + "'").c_str()); return nullptr; } std::vector sizes; while (this->peek().fKind == Token::LBRACKET) { this->expect(Token::LBRACKET, "'['"); if (this->peek().fKind != Token::RBRACKET) { int64_t i; if (this->intLiteral(&i)) { sizes.push_back(i); } else { return nullptr; } } else { sizes.push_back(-1); } this->expect(Token::RBRACKET, "']'"); } return std::unique_ptr(new ASTType(type.fPosition, std::move(type.fText), ASTType::kIdentifier_Kind, sizes)); } /* IDENTIFIER LBRACE varDeclaration* RBRACE (IDENTIFIER (LBRACKET expression? RBRACKET)*)? */ std::unique_ptr Parser::interfaceBlock(Modifiers mods) { Token name; if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { return nullptr; } if (peek().fKind != Token::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(name.fPosition, "no type named '" + name.fText + "'"); return nullptr; } this->nextToken(); std::vector> decls; while (this->peek().fKind != Token::RBRACE) { std::unique_ptr decl = this->varDeclarations(); if (!decl) { return nullptr; } decls.push_back(std::move(decl)); } this->nextToken(); SkString instanceName; std::vector> sizes; if (this->peek().fKind == Token::IDENTIFIER) { instanceName = this->nextToken().fText; while (this->peek().fKind == Token::LBRACKET) { this->expect(Token::LBRACKET, "'['"); if (this->peek().fKind != Token::RBRACKET) { std::unique_ptr size = this->expression(); if (!size) { return nullptr; } sizes.push_back(std::move(size)); } else { sizes.push_back(nullptr); } this->expect(Token::RBRACKET, "']'"); } } this->expect(Token::SEMICOLON, "';'"); return std::unique_ptr(new ASTInterfaceBlock(name.fPosition, mods, name.fText, std::move(decls), std::move(instanceName), std::move(sizes))); } /* IF LPAREN expression RPAREN statement (ELSE statement)? */ std::unique_ptr Parser::ifStatement() { Token start; if (!this->expect(Token::IF, "'if'", &start)) { return nullptr; } if (!this->expect(Token::LPAREN, "'('")) { return nullptr; } std::unique_ptr test(this->expression()); if (!test) { return nullptr; } if (!this->expect(Token::RPAREN, "')'")) { return nullptr; } std::unique_ptr ifTrue(this->statement()); if (!ifTrue) { return nullptr; } std::unique_ptr ifFalse; if (this->peek().fKind == Token::ELSE) { this->nextToken(); ifFalse = this->statement(); if (!ifFalse) { return nullptr; } } return std::unique_ptr(new ASTIfStatement(start.fPosition, std::move(test), std::move(ifTrue), std::move(ifFalse))); } /* DO statement WHILE LPAREN expression RPAREN SEMICOLON */ std::unique_ptr Parser::doStatement() { Token start; if (!this->expect(Token::DO, "'do'", &start)) { return nullptr; } std::unique_ptr statement(this->statement()); if (!statement) { return nullptr; } if (!this->expect(Token::WHILE, "'while'")) { return nullptr; } if (!this->expect(Token::LPAREN, "'('")) { return nullptr; } std::unique_ptr test(this->expression()); if (!test) { return nullptr; } if (!this->expect(Token::RPAREN, "')'")) { return nullptr; } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTDoStatement(start.fPosition, std::move(statement), std::move(test))); } /* WHILE LPAREN expression RPAREN STATEMENT */ std::unique_ptr Parser::whileStatement() { Token start; if (!this->expect(Token::WHILE, "'while'", &start)) { return nullptr; } if (!this->expect(Token::LPAREN, "'('")) { return nullptr; } std::unique_ptr test(this->expression()); if (!test) { return nullptr; } if (!this->expect(Token::RPAREN, "')'")) { return nullptr; } std::unique_ptr statement(this->statement()); if (!statement) { return nullptr; } return std::unique_ptr(new ASTWhileStatement(start.fPosition, std::move(test), std::move(statement))); } /* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN STATEMENT */ std::unique_ptr Parser::forStatement() { Token start; if (!this->expect(Token::FOR, "'for'", &start)) { return nullptr; } if (!this->expect(Token::LPAREN, "'('")) { return nullptr; } std::unique_ptr initializer; Token nextToken = this->peek(); switch (nextToken.fKind) { case Token::SEMICOLON: this->nextToken(); break; case Token::CONST: { std::unique_ptr vd = this->varDeclarations(); if (!vd) { return nullptr; } initializer = std::unique_ptr(new ASTVarDeclarationStatement( std::move(vd))); break; } case Token::IDENTIFIER: { if (this->isType(nextToken.fText)) { std::unique_ptr vd = this->varDeclarations(); if (!vd) { return nullptr; } initializer = std::unique_ptr(new ASTVarDeclarationStatement( std::move(vd))); break; } } // fall through default: initializer = this->expressionStatement(); } std::unique_ptr test; if (this->peek().fKind != Token::SEMICOLON) { test = this->expression(); if (!test) { return nullptr; } } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } std::unique_ptr next; if (this->peek().fKind != Token::RPAREN) { next = this->expression(); if (!next) { return nullptr; } } if (!this->expect(Token::RPAREN, "')'")) { return nullptr; } std::unique_ptr statement(this->statement()); if (!statement) { return nullptr; } return std::unique_ptr(new ASTForStatement(start.fPosition, std::move(initializer), std::move(test), std::move(next), std::move(statement))); } /* RETURN expression? SEMICOLON */ std::unique_ptr Parser::returnStatement() { Token start; if (!this->expect(Token::RETURN, "'return'", &start)) { return nullptr; } std::unique_ptr expression; if (this->peek().fKind != Token::SEMICOLON) { expression = this->expression(); if (!expression) { return nullptr; } } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTReturnStatement(start.fPosition, std::move(expression))); } /* BREAK SEMICOLON */ std::unique_ptr Parser::breakStatement() { Token start; if (!this->expect(Token::BREAK, "'break'", &start)) { return nullptr; } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTBreakStatement(start.fPosition)); } /* CONTINUE SEMICOLON */ std::unique_ptr Parser::continueStatement() { Token start; if (!this->expect(Token::CONTINUE, "'continue'", &start)) { return nullptr; } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTContinueStatement(start.fPosition)); } /* DISCARD SEMICOLON */ std::unique_ptr Parser::discardStatement() { Token start; if (!this->expect(Token::DISCARD, "'continue'", &start)) { return nullptr; } if (!this->expect(Token::SEMICOLON, "';'")) { return nullptr; } return std::unique_ptr(new ASTDiscardStatement(start.fPosition)); } /* LBRACE statement* RBRACE */ std::unique_ptr Parser::block() { AutoDepth depth(this); if (!depth.checkValid()) { return nullptr; } Token start; if (!this->expect(Token::LBRACE, "'{'", &start)) { return nullptr; } std::vector> statements; for (;;) { switch (this->peek().fKind) { case Token::RBRACE: this->nextToken(); return std::unique_ptr(new ASTBlock(start.fPosition, std::move(statements))); case Token::END_OF_FILE: this->error(this->peek().fPosition, "expected '}', but found end of file"); return nullptr; default: { std::unique_ptr statement = this->statement(); if (!statement) { return nullptr; } statements.push_back(std::move(statement)); } } } } /* expression SEMICOLON */ std::unique_ptr Parser::expressionStatement() { std::unique_ptr expr = this->expression(); if (expr) { if (this->expect(Token::SEMICOLON, "';'")) { ASTExpressionStatement* result = new ASTExpressionStatement(std::move(expr)); return std::unique_ptr(result); } } return nullptr; } /* assignmentExpression */ std::unique_ptr Parser::expression() { AutoDepth depth(this); if (!depth.checkValid()) { return nullptr; } return this->assignmentExpression(); } /* ternaryExpression ((EQEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSEQ | MINUSEQ | SHLEQ | SHREQ | BITWISEANDEQ | BITWISEXOREQ | BITWISEOREQ | LOGICALANDEQ | LOGICALXOREQ | LOGICALOREQ) assignmentExpression)* */ std::unique_ptr Parser::assignmentExpression() { std::unique_ptr result = this->ternaryExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::EQ: // fall through case Token::STAREQ: // fall through case Token::SLASHEQ: // fall through case Token::PERCENTEQ: // fall through case Token::PLUSEQ: // fall through case Token::MINUSEQ: // fall through case Token::SHLEQ: // fall through case Token::SHREQ: // fall through case Token::BITWISEANDEQ: // fall through case Token::BITWISEXOREQ: // fall through case Token::BITWISEOREQ: // fall through case Token::LOGICALANDEQ: // fall through case Token::LOGICALXOREQ: // fall through case Token::LOGICALOREQ: { Token t = this->nextToken(); std::unique_ptr right = this->assignmentExpression(); if (!right) { return nullptr; } result = std::unique_ptr(new ASTBinaryExpression(std::move(result), t, std::move(right))); } default: return result; } } } /* logicalOrExpression ('?' expression ':' assignmentExpression)? */ std::unique_ptr Parser::ternaryExpression() { std::unique_ptr result = this->logicalOrExpression(); if (!result) { return nullptr; } if (this->peek().fKind == Token::QUESTION) { Token question = this->nextToken(); std::unique_ptr trueExpr = this->expression(); if (!trueExpr) { return nullptr; } if (this->expect(Token::COLON, "':'")) { std::unique_ptr falseExpr = this->assignmentExpression(); return std::unique_ptr(new ASTTernaryExpression(std::move(result), std::move(trueExpr), std::move(falseExpr))); } return nullptr; } return result; } /* logicalXorExpression (LOGICALOR logicalXorExpression)* */ std::unique_ptr Parser::logicalOrExpression() { std::unique_ptr result = this->logicalXorExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::LOGICALOR) { Token t = this->nextToken(); std::unique_ptr right = this->logicalXorExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* logicalAndExpression (LOGICALXOR logicalAndExpression)* */ std::unique_ptr Parser::logicalXorExpression() { std::unique_ptr result = this->logicalAndExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::LOGICALXOR) { Token t = this->nextToken(); std::unique_ptr right = this->logicalAndExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* bitwiseOrExpression (LOGICALAND bitwiseOrExpression)* */ std::unique_ptr Parser::logicalAndExpression() { std::unique_ptr result = this->bitwiseOrExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::LOGICALAND) { Token t = this->nextToken(); std::unique_ptr right = this->bitwiseOrExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* bitwiseXorExpression (BITWISEOR bitwiseXorExpression)* */ std::unique_ptr Parser::bitwiseOrExpression() { std::unique_ptr result = this->bitwiseXorExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::BITWISEOR) { Token t = this->nextToken(); std::unique_ptr right = this->bitwiseXorExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* bitwiseAndExpression (BITWISEXOR bitwiseAndExpression)* */ std::unique_ptr Parser::bitwiseXorExpression() { std::unique_ptr result = this->bitwiseAndExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::BITWISEXOR) { Token t = this->nextToken(); std::unique_ptr right = this->bitwiseAndExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* equalityExpression (BITWISEAND equalityExpression)* */ std::unique_ptr Parser::bitwiseAndExpression() { std::unique_ptr result = this->equalityExpression(); if (!result) { return nullptr; } while (this->peek().fKind == Token::BITWISEAND) { Token t = this->nextToken(); std::unique_ptr right = this->equalityExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); } return result; } /* relationalExpression ((EQEQ | NEQ) relationalExpression)* */ std::unique_ptr Parser::equalityExpression() { std::unique_ptr result = this->relationalExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::EQEQ: // fall through case Token::NEQ: { Token t = this->nextToken(); std::unique_ptr right = this->relationalExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); break; } default: return result; } } } /* shiftExpression ((LT | GT | LTEQ | GTEQ) shiftExpression)* */ std::unique_ptr Parser::relationalExpression() { std::unique_ptr result = this->shiftExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::LT: // fall through case Token::GT: // fall through case Token::LTEQ: // fall through case Token::GTEQ: { Token t = this->nextToken(); std::unique_ptr right = this->shiftExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); break; } default: return result; } } } /* additiveExpression ((SHL | SHR) additiveExpression)* */ std::unique_ptr Parser::shiftExpression() { std::unique_ptr result = this->additiveExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::SHL: // fall through case Token::SHR: { Token t = this->nextToken(); std::unique_ptr right = this->additiveExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); break; } default: return result; } } } /* multiplicativeExpression ((PLUS | MINUS) multiplicativeExpression)* */ std::unique_ptr Parser::additiveExpression() { std::unique_ptr result = this->multiplicativeExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::PLUS: // fall through case Token::MINUS: { Token t = this->nextToken(); std::unique_ptr right = this->multiplicativeExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); break; } default: return result; } } } /* unaryExpression ((STAR | SLASH | PERCENT) unaryExpression)* */ std::unique_ptr Parser::multiplicativeExpression() { std::unique_ptr result = this->unaryExpression(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::STAR: // fall through case Token::SLASH: // fall through case Token::PERCENT: { Token t = this->nextToken(); std::unique_ptr right = this->unaryExpression(); if (!right) { return nullptr; } result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right))); break; } default: return result; } } } /* postfixExpression | (PLUS | MINUS | NOT | PLUSPLUS | MINUSMINUS) unaryExpression */ std::unique_ptr Parser::unaryExpression() { switch (this->peek().fKind) { case Token::PLUS: // fall through case Token::MINUS: // fall through case Token::LOGICALNOT: // fall through case Token::BITWISENOT: // fall through case Token::PLUSPLUS: // fall through case Token::MINUSMINUS: { Token t = this->nextToken(); std::unique_ptr expr = this->unaryExpression(); if (!expr) { return nullptr; } return std::unique_ptr(new ASTPrefixExpression(t, std::move(expr))); } default: return this->postfixExpression(); } } /* term suffix* */ std::unique_ptr Parser::postfixExpression() { std::unique_ptr result = this->term(); if (!result) { return nullptr; } for (;;) { switch (this->peek().fKind) { case Token::LBRACKET: // fall through case Token::DOT: // fall through case Token::LPAREN: // fall through case Token::PLUSPLUS: // fall through case Token::MINUSMINUS: { std::unique_ptr s = this->suffix(); if (!s) { return nullptr; } result.reset(new ASTSuffixExpression(std::move(result), std::move(s))); break; } default: return result; } } } /* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN | PLUSPLUS | MINUSMINUS */ std::unique_ptr Parser::suffix() { Token next = this->nextToken(); switch (next.fKind) { case Token::LBRACKET: { if (this->peek().fKind == Token::RBRACKET) { this->nextToken(); return std::unique_ptr(new ASTIndexSuffix(next.fPosition)); } std::unique_ptr e = this->expression(); if (!e) { return nullptr; } this->expect(Token::RBRACKET, "']' to complete array access expression"); return std::unique_ptr(new ASTIndexSuffix(std::move(e))); } case Token::DOT: { Position pos = this->peek().fPosition; SkString text; if (this->identifier(&text)) { return std::unique_ptr(new ASTFieldSuffix(pos, std::move(text))); } return nullptr; } case Token::LPAREN: { std::vector> parameters; if (this->peek().fKind != Token::RPAREN) { for (;;) { std::unique_ptr expr = this->expression(); if (!expr) { return nullptr; } parameters.push_back(std::move(expr)); if (this->peek().fKind != Token::COMMA) { break; } this->nextToken(); } } this->expect(Token::RPAREN, "')' to complete function parameters"); return std::unique_ptr(new ASTCallSuffix(next.fPosition, std::move(parameters))); } case Token::PLUSPLUS: return std::unique_ptr(new ASTSuffix(next.fPosition, ASTSuffix::kPostIncrement_Kind)); case Token::MINUSMINUS: return std::unique_ptr(new ASTSuffix(next.fPosition, ASTSuffix::kPostDecrement_Kind)); default: { this->error(next.fPosition, "expected expression suffix, but found '" + next.fText + "'\n"); return nullptr; } } } /* IDENTIFIER | intLiteral | floatLiteral | boolLiteral | '(' expression ')' */ std::unique_ptr Parser::term() { std::unique_ptr result; Token t = this->peek(); switch (t.fKind) { case Token::IDENTIFIER: { SkString text; if (this->identifier(&text)) { result.reset(new ASTIdentifier(t.fPosition, std::move(text))); } break; } case Token::INT_LITERAL: { int64_t i; if (this->intLiteral(&i)) { result.reset(new ASTIntLiteral(t.fPosition, i)); } break; } case Token::FLOAT_LITERAL: { double f; if (this->floatLiteral(&f)) { result.reset(new ASTFloatLiteral(t.fPosition, f)); } break; } case Token::TRUE_LITERAL: // fall through case Token::FALSE_LITERAL: { bool b; if (this->boolLiteral(&b)) { result.reset(new ASTBoolLiteral(t.fPosition, b)); } break; } case Token::LPAREN: { this->nextToken(); result = this->expression(); if (result) { this->expect(Token::RPAREN, "')' to complete expression"); } break; } default: this->nextToken(); this->error(t.fPosition, "expected expression, but found '" + t.fText + "'\n"); result = nullptr; } return result; } /* INT_LITERAL */ bool Parser::intLiteral(int64_t* dest) { Token t; if (this->expect(Token::INT_LITERAL, "integer literal", &t)) { *dest = SkSL::stol(t.fText); return true; } return false; } /* FLOAT_LITERAL */ bool Parser::floatLiteral(double* dest) { Token t; if (this->expect(Token::FLOAT_LITERAL, "float literal", &t)) { *dest = SkSL::stod(t.fText); return true; } return false; } /* TRUE_LITERAL | FALSE_LITERAL */ bool Parser::boolLiteral(bool* dest) { Token t = this->nextToken(); switch (t.fKind) { case Token::TRUE_LITERAL: *dest = true; return true; case Token::FALSE_LITERAL: *dest = false; return true; default: this->error(t.fPosition, "expected 'true' or 'false', but found '" + t.fText + "'\n"); return false; } } /* IDENTIFIER */ bool Parser::identifier(SkString* dest) { Token t; if (this->expect(Token::IDENTIFIER, "identifier", &t)) { *dest = t.fText; return true; } return false; } } // namespace