Index: src/sksl/SkSLParser.cpp |
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3526c6ee66706cf9e4d57502b26b9fc4b5120eb2 |
--- /dev/null |
+++ b/src/sksl/SkSLParser.cpp |
@@ -0,0 +1,1389 @@ |
+/* |
+ * 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" |
+#endif |
+#include "lex.sksl.c" |
+#ifdef __clang__ |
+#pragma clang diagnostic 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/SkSLASTParameter.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" |
+ |
+namespace SkSL { |
+ |
+Parser::Parser(std::string text, SymbolTable& types, ErrorReporter& errors) |
+: fPushback(Position(-1, -1), Token::INVALID_TOKEN, "") |
+, 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); |
+ } |
+} |
+ |
+Parser::~Parser() { |
+ sksl_delete_buffer(fBuffer, fScanner); |
+} |
+ |
+/* (precision | directive | declaration)* END_OF_FILE */ |
+std::vector<std::unique_ptr<ASTDeclaration>> Parser::file() { |
+ std::vector<std::unique_ptr<ASTDeclaration>> result; |
+ for (;;) { |
+ switch (this->peek().fKind) { |
+ case Token::END_OF_FILE: |
+ return result; |
+ case Token::PRECISION: |
+ this->precision(); |
+ break; |
+ case Token::DIRECTIVE: { |
+ std::unique_ptr<ASTDeclaration> decl = this->directive(); |
+ if (decl) { |
+ result.push_back(std::move(decl)); |
+ } |
+ break; |
+ } |
+ default: { |
+ std::unique_ptr<ASTDeclaration> 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); |
+ return Token(Position(skslget_lineno(fScanner), -1), (Token::Kind) token, |
+ token == Token::END_OF_FILE ? "<end of file>" : |
+ std::string(skslget_text(fScanner))); |
+} |
+ |
+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, std::string expected, Token* result) { |
+ Token next = this->nextToken(); |
+ if (next.fKind == kind) { |
+ if (result) { |
+ *result = next; |
+ } |
+ return true; |
+ } else { |
+ this->error(next.fPosition, "expected " + expected + ", but found '" + next.fText + "'"); |
+ return false; |
+ } |
+} |
+ |
+void Parser::error(Position p, std::string msg) { |
+ fErrors.error(p, msg); |
+} |
+ |
+bool Parser::isType(std::string name) { |
+ return nullptr != fTypes[name]; |
+} |
+ |
+/* PRECISION (LOWP | MEDIUMP | HIGHP) type SEMICOLON */ |
+void Parser::precision() { |
+ if (!this->expect(Token::PRECISION, "'precision'")) { |
+ return; |
+ } |
+ Token p = this->nextToken(); |
+ switch (p.fKind) { |
+ case Token::LOWP: // fall through |
+ case Token::MEDIUMP: // fall through |
+ case Token::HIGHP: |
+ // ignored for now |
+ break; |
+ default: |
+ this->error(p.fPosition, "expected 'lowp', 'mediump', or 'highp', but found '" + |
+ p.fText + "'"); |
+ return; |
+ } |
+ if (!this->type()) { |
+ return; |
+ } |
+ this->expect(Token::SEMICOLON, "';'"); |
+} |
+ |
+/* DIRECTIVE(#version) INT_LITERAL | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */ |
+std::unique_ptr<ASTDeclaration> 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"); |
+ // ignored for now |
+ 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<ASTDeclaration>(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<ASTDeclaration> Parser::declaration() { |
+ ASTModifiers 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); |
+ } |
+ std::unique_ptr<ASTType> 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<std::unique_ptr<ASTParameter>> parameters; |
+ while (this->peek().fKind != Token::RPAREN) { |
+ if (parameters.size() > 0) { |
+ if (!this->expect(Token::COMMA, "','")) { |
+ return nullptr; |
+ } |
+ } |
+ std::unique_ptr<ASTParameter> parameter = this->parameter(); |
+ if (!parameter) { |
+ return nullptr; |
+ } |
+ parameters.push_back(std::move(parameter)); |
+ } |
+ this->nextToken(); |
+ std::unique_ptr<ASTBlock> body; |
+ if (this->peek().fKind == Token::SEMICOLON) { |
+ this->nextToken(); |
+ } else { |
+ body = this->block(); |
+ if (!body) { |
+ return nullptr; |
+ } |
+ } |
+ return std::unique_ptr<ASTDeclaration>(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<ASTVarDeclaration> Parser::varDeclaration() { |
+ ASTModifiers modifiers = this->modifiers(); |
+ std::unique_ptr<ASTType> 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<ASTType> 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<Type::Field> fields; |
+ while (this->peek().fKind != Token::RBRACE) { |
+ std::unique_ptr<ASTVarDeclaration> decl = this->varDeclaration(); |
+ if (!decl) { |
+ return nullptr; |
+ } |
+ for (size_t i = 0; i < decl->fNames.size(); i++) { |
+ auto type = std::static_pointer_cast<Type>(fTypes[decl->fType->fName]); |
+ for (int j = (int) decl->fSizes[i].size() - 1; j >= 0; j--) { |
+ if (decl->fSizes[i][j]->fKind == ASTExpression::kInt_Kind) { |
+ this->error(decl->fPosition, "array size in struct field must be a constant"); |
+ } |
+ uint64_t columns = ((ASTIntLiteral&) *decl->fSizes[i][j]).fValue; |
+ std::string name = type->name() + "[" + to_string(columns) + "]"; |
+ type = std::shared_ptr<Type>(new Type(name, Type::kArray_Kind, std::move(type), |
+ (int) columns)); |
+ } |
+ fields.push_back(Type::Field(decl->fModifiers, decl->fNames[i], std::move(type))); |
+ if (decl->fValues[i]) { |
+ this->error(decl->fPosition, "initializers are not permitted on struct fields"); |
+ } |
+ } |
+ } |
+ if (!this->expect(Token::RBRACE, "'}'")) { |
+ return nullptr; |
+ } |
+ std::shared_ptr<Type> type(new Type(name.fText, fields)); |
+ fTypes.add(type->fName, type); |
+ return std::unique_ptr<ASTType>(new ASTType(name.fPosition, type->fName, |
+ ASTType::kStruct_Kind)); |
+} |
+ |
+/* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */ |
+std::unique_ptr<ASTVarDeclaration> Parser::structVarDeclaration(ASTModifiers modifiers) { |
+ std::unique_ptr<ASTType> type = this->structDeclaration(); |
+ if (!type) { |
+ return nullptr; |
+ } |
+ if (peek().fKind == Token::IDENTIFIER) { |
+ Token name = this->nextToken(); |
+ std::unique_ptr<ASTVarDeclaration> result = this->varDeclarationEnd(modifiers, |
+ std::move(type), |
+ std::move(name.fText)); |
+ if (result) { |
+ for (size_t i = 0; i < result->fValues.size(); i++) { |
+ if (result->fValues[i]) { |
+ this->error(result->fValues[i]->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<ASTVarDeclaration> Parser::varDeclarationEnd(ASTModifiers mods, |
+ std::unique_ptr<ASTType> type, |
+ std::string name) { |
+ std::vector<std::string> names; |
+ std::vector<std::vector<std::unique_ptr<ASTExpression>>> sizes; |
+ names.push_back(name); |
+ std::vector<std::unique_ptr<ASTExpression>> 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<ASTExpression> size(this->expression()); |
+ if (!size) { |
+ return nullptr; |
+ } |
+ currentVarSizes.push_back(std::move(size)); |
+ if (!this->expect(Token::RBRACKET, "']'")) { |
+ return nullptr; |
+ } |
+ } |
+ } |
+ sizes.push_back(std::move(currentVarSizes)); |
+ std::vector<std::unique_ptr<ASTExpression>> values; |
+ if (this->peek().fKind == Token::EQ) { |
+ this->nextToken(); |
+ std::unique_ptr<ASTExpression> value(this->expression()); |
+ if (!value) { |
+ return nullptr; |
+ } |
+ values.push_back(std::move(value)); |
+ } else { |
+ values.push_back(nullptr); |
+ } |
+ while (this->peek().fKind == Token::COMMA) { |
+ this->nextToken(); |
+ Token name; |
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { |
+ return nullptr; |
+ } |
+ names.push_back(name.fText); |
+ currentVarSizes.clear(); |
+ while (this->peek().fKind == Token::LBRACKET) { |
+ this->nextToken(); |
+ if (this->peek().fKind == Token::RBRACKET) { |
+ this->nextToken(); |
+ currentVarSizes.push_back(nullptr); |
+ } else { |
+ std::unique_ptr<ASTExpression> size(this->expression()); |
+ if (!size) { |
+ return nullptr; |
+ } |
+ currentVarSizes.push_back(std::move(size)); |
+ if (!this->expect(Token::RBRACKET, "']'")) { |
+ return nullptr; |
+ } |
+ } |
+ } |
+ sizes.push_back(std::move(currentVarSizes)); |
+ if (this->peek().fKind == Token::EQ) { |
+ this->nextToken(); |
+ std::unique_ptr<ASTExpression> value(this->expression()); |
+ if (!value) { |
+ return nullptr; |
+ } |
+ values.push_back(std::move(value)); |
+ } else { |
+ values.push_back(nullptr); |
+ } |
+ } |
+ if (!this->expect(Token::SEMICOLON, "';'")) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTVarDeclaration>(new ASTVarDeclaration(std::move(mods), |
+ std::move(type), |
+ std::move(names), |
+ std::move(sizes), |
+ std::move(values))); |
+} |
+ |
+/* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */ |
+std::unique_ptr<ASTParameter> Parser::parameter() { |
+ ASTModifiers modifiers = this->modifiersWithDefaults(ASTModifiers::kIn_Flag); |
+ std::unique_ptr<ASTType> type = this->type(); |
+ if (!type) { |
+ return nullptr; |
+ } |
+ Token name; |
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) { |
+ return nullptr; |
+ } |
+ std::vector<int> 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<ASTParameter>(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 */ |
+ASTLayout Parser::layout() { |
+ int location = -1; |
+ int binding = -1; |
+ int index = -1; |
+ int set = -1; |
+ int builtin = -1; |
+ if (this->peek().fKind == Token::LAYOUT) { |
+ this->nextToken(); |
+ if (!this->expect(Token::LPAREN, "'('")) { |
+ return ASTLayout(location, binding, index, set, builtin); |
+ } |
+ for (;;) { |
+ Token t = this->nextToken(); |
+ if (t.fText == "location") { |
+ location = this->layoutInt(); |
+ } else if (t.fText == "binding") { |
+ binding = this->layoutInt(); |
+ } else if (t.fText == "index") { |
+ index = this->layoutInt(); |
+ } else if (t.fText == "set") { |
+ set = this->layoutInt(); |
+ } else if (t.fText == "builtin") { |
+ builtin = this->layoutInt(); |
+ } 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 ASTLayout(location, binding, index, set, builtin); |
+} |
+ |
+/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | |
+ MEDIUMP | HIGHP)* */ |
+ASTModifiers Parser::modifiers() { |
+ ASTLayout layout = this->layout(); |
+ int flags = 0; |
+ for (;;) { |
+ // TODO: handle duplicate / incompatible flags |
+ switch (peek().fKind) { |
+ case Token::UNIFORM: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kUniform_Flag; |
+ break; |
+ case Token::CONST: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kConst_Flag; |
+ break; |
+ case Token::IN: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kIn_Flag; |
+ break; |
+ case Token::OUT: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kOut_Flag; |
+ break; |
+ case Token::INOUT: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kIn_Flag; |
+ flags |= ASTModifiers::kOut_Flag; |
+ break; |
+ case Token::LOWP: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kLowp_Flag; |
+ break; |
+ case Token::MEDIUMP: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kMediump_Flag; |
+ break; |
+ case Token::HIGHP: |
+ this->nextToken(); |
+ flags |= ASTModifiers::kHighp_Flag; |
+ break; |
+ default: |
+ return ASTModifiers(layout, flags); |
+ } |
+ } |
+} |
+ |
+ASTModifiers Parser::modifiersWithDefaults(int defaultFlags) { |
+ ASTModifiers result = this->modifiers(); |
+ if (!result.fFlags) { |
+ return ASTModifiers(result.fLayout, defaultFlags); |
+ } |
+ return result; |
+} |
+ |
+/* ifStatement | forStatement | doStatement | whileStatement | block | expression */ |
+std::unique_ptr<ASTStatement> 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<ASTStatement>(new ASTBlock(start.fPosition, {})); |
+ case Token::CONST: // fall through |
+ case Token::HIGHP: // fall through |
+ case Token::MEDIUMP: // fall through |
+ case Token::LOWP: { |
+ auto decl = this->varDeclaration(); |
+ if (!decl) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement(std::move(decl))); |
+ } |
+ case Token::IDENTIFIER: |
+ if (this->isType(start.fText)) { |
+ auto decl = this->varDeclaration(); |
+ if (!decl) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement( |
+ std::move(decl))); |
+ } |
+ // fall through |
+ default: |
+ return this->expressionStatement(); |
+ } |
+} |
+ |
+/* IDENTIFIER(type) */ |
+std::unique_ptr<ASTType> 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; |
+ } |
+ return std::unique_ptr<ASTType>(new ASTType(type.fPosition, std::move(type.fText), |
+ ASTType::kIdentifier_Kind)); |
+} |
+ |
+/* IDENTIFIER LBRACE varDeclaration* RBRACE */ |
+std::unique_ptr<ASTDeclaration> Parser::interfaceBlock(ASTModifiers 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<std::unique_ptr<ASTVarDeclaration>> decls; |
+ while (this->peek().fKind != Token::RBRACE) { |
+ std::unique_ptr<ASTVarDeclaration> decl = this->varDeclaration(); |
+ if (!decl) { |
+ return nullptr; |
+ } |
+ decls.push_back(std::move(decl)); |
+ } |
+ this->nextToken(); |
+ std::string valueName; |
+ if (this->peek().fKind == Token::IDENTIFIER) { |
+ valueName = this->nextToken().fText; |
+ } |
+ this->expect(Token::SEMICOLON, "';'"); |
+ return std::unique_ptr<ASTDeclaration>(new ASTInterfaceBlock(name.fPosition, mods, |
+ name.fText, std::move(valueName), |
+ std::move(decls))); |
+} |
+ |
+/* IF LPAREN expression RPAREN statement (ELSE statement)? */ |
+std::unique_ptr<ASTIfStatement> Parser::ifStatement() { |
+ Token start; |
+ if (!this->expect(Token::IF, "'if'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::LPAREN, "'('")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTExpression> test(this->expression()); |
+ if (!test) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::RPAREN, "')'")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> ifTrue(this->statement()); |
+ if (!ifTrue) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> ifFalse; |
+ if (this->peek().fKind == Token::ELSE) { |
+ this->nextToken(); |
+ ifFalse = this->statement(); |
+ if (!ifFalse) { |
+ return nullptr; |
+ } |
+ } |
+ return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition, std::move(test), |
+ std::move(ifTrue), |
+ std::move(ifFalse))); |
+} |
+ |
+/* DO statement WHILE LPAREN expression RPAREN SEMICOLON */ |
+std::unique_ptr<ASTDoStatement> Parser::doStatement() { |
+ Token start; |
+ if (!this->expect(Token::DO, "'do'", &start)) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> 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<ASTExpression> 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<ASTDoStatement>(new ASTDoStatement(start.fPosition, |
+ std::move(statement), |
+ std::move(test))); |
+} |
+ |
+/* WHILE LPAREN expression RPAREN STATEMENT */ |
+std::unique_ptr<ASTWhileStatement> Parser::whileStatement() { |
+ Token start; |
+ if (!this->expect(Token::WHILE, "'while'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::LPAREN, "'('")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTExpression> test(this->expression()); |
+ if (!test) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::RPAREN, "')'")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> statement(this->statement()); |
+ if (!statement) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTWhileStatement>(new ASTWhileStatement(start.fPosition, |
+ std::move(test), |
+ std::move(statement))); |
+} |
+ |
+/* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN |
+ STATEMENT */ |
+std::unique_ptr<ASTForStatement> Parser::forStatement() { |
+ Token start; |
+ if (!this->expect(Token::FOR, "'for'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::LPAREN, "'('")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> initializer; |
+ Token nextToken = this->peek(); |
+ switch (nextToken.fKind) { |
+ case Token::SEMICOLON: |
+ break; |
+ case Token::CONST: |
+ initializer = std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement( |
+ this->varDeclaration())); |
+ break; |
+ case Token::IDENTIFIER: |
+ if (this->isType(nextToken.fText)) { |
+ initializer = std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement( |
+ this->varDeclaration())); |
+ break; |
+ } |
+ // fall through |
+ default: |
+ initializer = this->expressionStatement(); |
+ } |
+ std::unique_ptr<ASTExpression> test; |
+ if (this->peek().fKind != Token::SEMICOLON) { |
+ test = this->expression(); |
+ if (!test) { |
+ return nullptr; |
+ } |
+ } |
+ if (!this->expect(Token::SEMICOLON, "';'")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTExpression> next; |
+ if (this->peek().fKind != Token::SEMICOLON) { |
+ next = this->expression(); |
+ if (!next) { |
+ return nullptr; |
+ } |
+ } |
+ if (!this->expect(Token::RPAREN, "')'")) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTStatement> statement(this->statement()); |
+ if (!statement) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTForStatement>(new ASTForStatement(start.fPosition, |
+ std::move(initializer), |
+ std::move(test), std::move(next), |
+ std::move(statement))); |
+} |
+ |
+/* RETURN expression? SEMICOLON */ |
+std::unique_ptr<ASTReturnStatement> Parser::returnStatement() { |
+ Token start; |
+ if (!this->expect(Token::RETURN, "'return'", &start)) { |
+ return nullptr; |
+ } |
+ std::unique_ptr<ASTExpression> 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<ASTReturnStatement>(new ASTReturnStatement(start.fPosition, |
+ std::move(expression))); |
+} |
+ |
+/* BREAK SEMICOLON */ |
+std::unique_ptr<ASTBreakStatement> Parser::breakStatement() { |
+ Token start; |
+ if (!this->expect(Token::BREAK, "'break'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::SEMICOLON, "';'")) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTBreakStatement>(new ASTBreakStatement(start.fPosition)); |
+} |
+ |
+/* CONTINUE SEMICOLON */ |
+std::unique_ptr<ASTContinueStatement> Parser::continueStatement() { |
+ Token start; |
+ if (!this->expect(Token::CONTINUE, "'continue'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::SEMICOLON, "';'")) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTContinueStatement>(new ASTContinueStatement(start.fPosition)); |
+} |
+ |
+/* DISCARD SEMICOLON */ |
+std::unique_ptr<ASTDiscardStatement> Parser::discardStatement() { |
+ Token start; |
+ if (!this->expect(Token::DISCARD, "'continue'", &start)) { |
+ return nullptr; |
+ } |
+ if (!this->expect(Token::SEMICOLON, "';'")) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTDiscardStatement>(new ASTDiscardStatement(start.fPosition)); |
+} |
+ |
+/* LBRACE statement* RBRACE */ |
+std::unique_ptr<ASTBlock> Parser::block() { |
+ Token start; |
+ if (!this->expect(Token::LBRACE, "'{'", &start)) { |
+ return nullptr; |
+ } |
+ std::vector<std::unique_ptr<ASTStatement>> statements; |
+ for (;;) { |
+ switch (this->peek().fKind) { |
+ case Token::RBRACE: |
+ this->nextToken(); |
+ return std::unique_ptr<ASTBlock>(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<ASTStatement> statement = this->statement(); |
+ if (!statement) { |
+ return nullptr; |
+ } |
+ statements.push_back(std::move(statement)); |
+ } |
+ } |
+ } |
+} |
+ |
+/* expression SEMICOLON */ |
+std::unique_ptr<ASTExpressionStatement> Parser::expressionStatement() { |
+ std::unique_ptr<ASTExpression> expr = this->expression(); |
+ if (expr) { |
+ if (this->expect(Token::SEMICOLON, "';'")) { |
+ ASTExpressionStatement* result = new ASTExpressionStatement(std::move(expr)); |
+ return std::unique_ptr<ASTExpressionStatement>(result); |
+ } |
+ } |
+ return nullptr; |
+} |
+ |
+/* assignmentExpression */ |
+std::unique_ptr<ASTExpression> Parser::expression() { |
+ return this->assignmentExpression(); |
+} |
+ |
+/* ternaryExpression ((EQEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSEQ | MINUSEQ | SHLEQ | SHREQ | |
+ BITWISEANDEQ | BITWISEXOREQ | BITWISEOREQ | LOGICALANDEQ | LOGICALXOREQ | LOGICALOREQ) |
+ assignmentExpression)* |
+ */ |
+std::unique_ptr<ASTExpression> Parser::assignmentExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> right = this->assignmentExpression(); |
+ if (!right) { |
+ return nullptr; |
+ } |
+ result = std::unique_ptr<ASTExpression>(new ASTBinaryExpression(std::move(result), |
+ t, |
+ std::move(right))); |
+ } |
+ default: |
+ return result; |
+ } |
+ } |
+} |
+ |
+/* logicalOrExpression ('?' expression ':' assignmentExpression)? */ |
+std::unique_ptr<ASTExpression> Parser::ternaryExpression() { |
+ std::unique_ptr<ASTExpression> result = this->logicalOrExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ if (this->peek().fKind == Token::QUESTION) { |
+ Token question = this->nextToken(); |
+ std::unique_ptr<ASTExpression> trueExpr = this->expression(); |
+ if (!trueExpr) { |
+ return nullptr; |
+ } |
+ if (this->expect(Token::COLON, "':'")) { |
+ std::unique_ptr<ASTExpression> falseExpr = this->assignmentExpression(); |
+ return std::unique_ptr<ASTExpression>(new ASTTernaryExpression(std::move(result), |
+ std::move(trueExpr), |
+ std::move(falseExpr))); |
+ } |
+ return nullptr; |
+ } |
+ return result; |
+} |
+ |
+/* logicalXorExpression (LOGICALOR logicalXorExpression)* */ |
+std::unique_ptr<ASTExpression> Parser::logicalOrExpression() { |
+ std::unique_ptr<ASTExpression> result = this->logicalXorExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::LOGICALOR) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::logicalXorExpression() { |
+ std::unique_ptr<ASTExpression> result = this->logicalAndExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::LOGICALXOR) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::logicalAndExpression() { |
+ std::unique_ptr<ASTExpression> result = this->bitwiseOrExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::LOGICALAND) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::bitwiseOrExpression() { |
+ std::unique_ptr<ASTExpression> result = this->bitwiseXorExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::BITWISEOR) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::bitwiseXorExpression() { |
+ std::unique_ptr<ASTExpression> result = this->bitwiseAndExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::BITWISEXOR) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::bitwiseAndExpression() { |
+ std::unique_ptr<ASTExpression> result = this->equalityExpression(); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ while (this->peek().fKind == Token::BITWISEAND) { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> Parser::equalityExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> 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<ASTExpression> Parser::relationalExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> 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<ASTExpression> Parser::shiftExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> 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<ASTExpression> Parser::additiveExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> 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<ASTExpression> Parser::multiplicativeExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTExpression> 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<ASTExpression> Parser::unaryExpression() { |
+ switch (this->peek().fKind) { |
+ case Token::PLUS: // fall through |
+ case Token::MINUS: // fall through |
+ case Token::NOT: // fall through |
+ case Token::PLUSPLUS: // fall through |
+ case Token::MINUSMINUS: { |
+ Token t = this->nextToken(); |
+ std::unique_ptr<ASTExpression> expr = this->unaryExpression(); |
+ if (!expr) { |
+ return nullptr; |
+ } |
+ return std::unique_ptr<ASTExpression>(new ASTPrefixExpression(t, std::move(expr))); |
+ } |
+ default: |
+ return this->postfixExpression(); |
+ } |
+} |
+ |
+/* term suffix* */ |
+std::unique_ptr<ASTExpression> Parser::postfixExpression() { |
+ std::unique_ptr<ASTExpression> 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<ASTSuffix> 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<ASTSuffix> Parser::suffix() { |
+ Token next = this->nextToken(); |
+ switch (next.fKind) { |
+ case Token::LBRACKET: { |
+ std::unique_ptr<ASTExpression> e = this->expression(); |
+ if (!e) { |
+ return nullptr; |
+ } |
+ this->expect(Token::RBRACKET, "']' to complete array access expression"); |
+ return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(std::move(e))); |
+ } |
+ case Token::DOT: { |
+ Position pos = this->peek().fPosition; |
+ std::string text; |
+ if (this->identifier(&text)) { |
+ return std::unique_ptr<ASTSuffix>(new ASTFieldSuffix(pos, std::move(text))); |
+ } |
+ return nullptr; |
+ } |
+ case Token::LPAREN: { |
+ std::vector<std::unique_ptr<ASTExpression>> parameters; |
+ if (this->peek().fKind != Token::RPAREN) { |
+ for (;;) { |
+ std::unique_ptr<ASTExpression> 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<ASTSuffix>(new ASTCallSuffix(next.fPosition, |
+ std::move(parameters))); |
+ } |
+ case Token::PLUSPLUS: |
+ return std::unique_ptr<ASTSuffix>(new ASTSuffix(next.fPosition, |
+ ASTSuffix::kPostIncrement_Kind)); |
+ case Token::MINUSMINUS: |
+ return std::unique_ptr<ASTSuffix>(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<ASTExpression> Parser::term() { |
+ std::unique_ptr<ASTExpression> result; |
+ Token t = this->peek(); |
+ switch (t.fKind) { |
+ case Token::IDENTIFIER: { |
+ std::string 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(std::string* dest) { |
+ Token t; |
+ if (this->expect(Token::IDENTIFIER, "identifier", &t)) { |
+ *dest = t.fText; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+} // namespace |