| /* |
| * Copyright (C) 2019 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "WHLSLParser.h" |
| |
| #if ENABLE(WEBGPU) |
| |
| #include "WHLSLAddressSpace.h" |
| #include "WHLSLEntryPointType.h" |
| #include "WHLSLProgram.h" |
| #include <wtf/dtoa.h> |
| #include <wtf/text/StringBuilder.h> |
| #include <wtf/text/StringConcatenate.h> |
| |
| namespace WebCore { |
| |
| namespace WHLSL { |
| |
| #define PARSE(name, element, ...) \ |
| auto name = parse##element(__VA_ARGS__); \ |
| if (!name) \ |
| return makeUnexpected(name.error()); \ |
| |
| #define CONSUME_TYPE(name, type) \ |
| auto name = consumeType(Token::Type::type); \ |
| if (!name) \ |
| return makeUnexpected(name.error()); |
| |
| #define PEEK(name) \ |
| auto name = peek(); \ |
| if (!name) \ |
| return makeUnexpected(name.error()); |
| |
| #define PEEK_FURTHER(name) \ |
| auto name = peekFurther(); \ |
| if (!name) \ |
| return makeUnexpected(name.error()); |
| |
| auto Parser::parse(Program& program, StringView stringView, ParsingMode mode, AST::NameSpace nameSpace) -> Expected<void, Error> |
| { |
| m_lexer = Lexer(stringView, nameSpace); |
| m_mode = mode; |
| |
| while (!m_lexer.isFullyConsumed()) { |
| auto token = m_lexer.peek(); |
| switch (token.type) { |
| case Token::Type::Invalid: |
| return { }; |
| case Token::Type::Semicolon: |
| m_lexer.consumeToken(); |
| continue; |
| case Token::Type::Typedef: { |
| auto typeDefinition = parseTypeDefinition(); |
| if (!typeDefinition) |
| return makeUnexpected(typeDefinition.error()); |
| auto appendResult = program.append(WTFMove(*typeDefinition)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| case Token::Type::Struct: { |
| auto structureDefinition = parseStructureDefinition(); |
| if (!structureDefinition) |
| return makeUnexpected(structureDefinition.error()); |
| auto appendResult = program.append(WTFMove(*structureDefinition)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| case Token::Type::Enum: { |
| auto enumerationDefinition = parseEnumerationDefinition(); |
| if (!enumerationDefinition) |
| return makeUnexpected(enumerationDefinition.error()); |
| auto appendResult = program.append(WTFMove(*enumerationDefinition)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| case Token::Type::Native: { |
| if (m_mode != ParsingMode::StandardLibrary) |
| return fail(makeString("'native' can't exist outside of the standard library.")); |
| auto furtherToken = peekFurther(); |
| if (!furtherToken) |
| return { }; |
| if (furtherToken->type == Token::Type::Typedef) { |
| auto nativeTypeDeclaration = parseNativeTypeDeclaration(); |
| if (!nativeTypeDeclaration) |
| return makeUnexpected(nativeTypeDeclaration.error()); |
| auto appendResult = program.append(WTFMove(*nativeTypeDeclaration)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| auto nativeFunctionDeclaration = parseNativeFunctionDeclaration(); |
| if (!nativeFunctionDeclaration) |
| return makeUnexpected(nativeFunctionDeclaration.error()); |
| auto appendResult = program.append(WTFMove(*nativeFunctionDeclaration)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| default: { |
| auto functionDefinition = parseFunctionDefinition(); |
| if (!functionDefinition) |
| return makeUnexpected(functionDefinition.error()); |
| auto appendResult = program.append(WTFMove(*functionDefinition)); |
| if (!appendResult) |
| return makeUnexpected(appendResult.error()); |
| continue; |
| } |
| } |
| } |
| |
| return { }; |
| } |
| |
| auto Parser::fail(const String& message, TryToPeek tryToPeek) -> Unexpected<Error> |
| { |
| if (tryToPeek == TryToPeek::Yes) { |
| if (auto nextToken = peek()) |
| return makeUnexpected(Error(m_lexer.errorString(*nextToken, message))); |
| } |
| return makeUnexpected(Error(makeString("Cannot lex: ", message))); |
| } |
| |
| auto Parser::peek() -> Expected<Token, Error> |
| { |
| auto token = m_lexer.peek(); |
| if (token.type != Token::Type::Invalid && token.type != Token::Type::EndOfFile) |
| return { token }; |
| return fail("Cannot consume token"_str, TryToPeek::No); |
| } |
| |
| auto Parser::peekFurther() -> Expected<Token, Error> |
| { |
| auto token = m_lexer.peekFurther(); |
| if (token.type != Token::Type::Invalid && token.type != Token::Type::EndOfFile) |
| return { token }; |
| return fail("Cannot consume two tokens"_str, TryToPeek::No); |
| } |
| |
| template <Token::Type t, Token::Type... ts> |
| struct Types { |
| static bool includes(Token::Type type) |
| { |
| return t == type || Types<ts...>::includes(type); |
| } |
| |
| static void appendNameTo(StringBuilder& builder) |
| { |
| builder.append(Token::typeName(t), ", "); |
| Types<ts...>::appendNameTo(builder); |
| } |
| }; |
| template <Token::Type t> |
| struct Types<t> { |
| static bool includes(Token::Type type) |
| { |
| return t == type; |
| } |
| |
| static void appendNameTo(StringBuilder& builder) |
| { |
| builder.append(Token::typeName(t)); |
| } |
| }; |
| |
| |
| bool Parser::peekType(Token::Type type) |
| { |
| auto token = m_lexer.peek(); |
| return token.type == type; |
| } |
| |
| template <Token::Type... types> |
| bool Parser::peekTypes() |
| { |
| auto token = m_lexer.peek(); |
| return Types<types...>::includes(token.type); |
| } |
| |
| Optional<Token> Parser::tryType(Token::Type type) |
| { |
| auto token = m_lexer.peek(); |
| if (token.type == type) |
| return { m_lexer.consumeToken() }; |
| return WTF::nullopt; |
| } |
| |
| template <Token::Type... types> |
| Optional<Token> Parser::tryTypes() |
| { |
| auto token = m_lexer.peek(); |
| if (Types<types...>::includes(token.type)) |
| return { m_lexer.consumeToken() }; |
| return WTF::nullopt; |
| } |
| |
| auto Parser::consumeType(Token::Type type) -> Expected<Token, Error> |
| { |
| auto token = m_lexer.consumeToken(); |
| if (token.type == type) |
| return { token }; |
| return fail(makeString("Unexpected token (expected ", Token::typeName(type), " got ", Token::typeName(token.type), ")")); |
| } |
| |
| template <Token::Type... types> |
| auto Parser::consumeTypes() -> Expected<Token, Error> |
| { |
| auto buildExpectedString = [&]() -> String { |
| StringBuilder builder; |
| builder.append("["); |
| Types<types...>::appendNameTo(builder); |
| builder.append("]"); |
| return builder.toString(); |
| }; |
| |
| auto token = m_lexer.consumeToken(); |
| if (Types<types...>::includes(token.type)) |
| return { token }; |
| return fail(makeString("Unexpected token (expected one of ", buildExpectedString(), " got ", Token::typeName(token.type), ")")); |
| } |
| |
| static int digitValue(UChar character) |
| { |
| if (character >= '0' && character <= '9') |
| return character - '0'; |
| if (character >= 'a' && character <= 'f') |
| return character - 'a' + 10; |
| return character - 'A' + 10; |
| } |
| |
| static Expected<int, Error> intLiteralToInt(StringView text) |
| { |
| bool negate = false; |
| if (text.startsWith("-"_str)) { |
| negate = true; |
| text = text.substring(1); |
| } |
| int base = 10; |
| if (text.startsWith("0x"_str)) { |
| text = text.substring(2); |
| base = 16; |
| } |
| |
| unsigned result = 0; |
| for (auto codePoint : text.codePoints()) { |
| unsigned digit = digitValue(codePoint); |
| auto previous = result; |
| result = result * base + digit; |
| if (result < previous) |
| return makeUnexpected(Error(makeString("int literal ", text, " is out of bounds"))); |
| } |
| if (negate) { |
| static_assert(sizeof(int64_t) > sizeof(unsigned) && sizeof(int64_t) > sizeof(int), "This code would be wrong otherwise"); |
| int64_t intResult = -static_cast<int64_t>(result); |
| if (intResult < static_cast<int64_t>(std::numeric_limits<int>::min())) |
| return makeUnexpected(Error(makeString("int literal ", text, " is out of bounds"))); |
| return { static_cast<int>(intResult) }; |
| } |
| if (result > static_cast<unsigned>(std::numeric_limits<int>::max())) |
| return makeUnexpected(Error(makeString("int literal ", text, " is out of bounds"))); |
| return { static_cast<int>(result) }; |
| } |
| |
| static Expected<unsigned, Error> uintLiteralToUint(StringView text) |
| { |
| unsigned base = 10; |
| if (text.startsWith("0x"_str)) { |
| text = text.substring(2); |
| base = 16; |
| } |
| ASSERT(text.endsWith("u")); |
| text = text.substring(0, text.length() - 1); |
| unsigned result = 0; |
| for (auto codePoint : text.codePoints()) { |
| unsigned digit = digitValue(codePoint); |
| auto previous = result; |
| result = result * base + digit; |
| if (result < previous) |
| return makeUnexpected(Error(makeString("uint literal ", text, " is out of bounds"))); |
| } |
| return { result }; |
| } |
| |
| static Expected<float, Error> floatLiteralToFloat(StringView text) |
| { |
| size_t parsedLength; |
| auto result = parseDouble(text, parsedLength); |
| if (parsedLength != text.length()) |
| return makeUnexpected(Error(makeString("Cannot parse float ", text))); |
| return static_cast<float>(result); |
| } |
| |
| auto Parser::consumeIntegralLiteral() -> Expected<Variant<int, unsigned>, Error> |
| { |
| auto integralLiteralToken = consumeTypes<Token::Type::IntLiteral, Token::Type::UintLiteral>(); |
| if (!integralLiteralToken) |
| return makeUnexpected(integralLiteralToken.error()); |
| |
| switch (integralLiteralToken->type) { |
| case Token::Type::IntLiteral: { |
| auto result = intLiteralToInt(integralLiteralToken->stringView(m_lexer)); |
| if (result) |
| return {{ *result }}; |
| return makeUnexpected(result.error()); |
| } |
| default: { |
| ASSERT(integralLiteralToken->type == Token::Type::UintLiteral); |
| auto result = uintLiteralToUint(integralLiteralToken->stringView(m_lexer)); |
| if (result) |
| return {{ *result }}; |
| return makeUnexpected(result.error()); |
| } |
| } |
| } |
| |
| auto Parser::consumeNonNegativeIntegralLiteral() -> Expected<unsigned, Error> |
| { |
| auto integralLiteral = consumeIntegralLiteral(); |
| if (!integralLiteral) |
| return makeUnexpected(integralLiteral.error()); |
| auto result = WTF::visit(WTF::makeVisitor([](int x) -> Optional<unsigned> { |
| if (x < 0) |
| return WTF::nullopt; |
| return x; |
| }, [](unsigned x) -> Optional<unsigned> { |
| return x; |
| }), *integralLiteral); |
| if (result) |
| return *result; |
| return fail("int literal is negative"_str); |
| } |
| |
| static Expected<unsigned, Error> recognizeSimpleUnsignedInteger(StringView stringView) |
| { |
| unsigned result = 0; |
| if (stringView.length() < 1) |
| return makeUnexpected(Error(makeString("Simple unsigned literal ", stringView, " is too short"))); |
| for (auto codePoint : stringView.codePoints()) { |
| if (codePoint < '0' || codePoint > '9') |
| return makeUnexpected(Error(makeString("Simple unsigned literal ", stringView, " isn't of the form [0-9]+"))); |
| auto previous = result; |
| result = result * 10 + (codePoint - '0'); |
| if (result < previous) |
| return makeUnexpected(Error(makeString("Simple unsigned literal ", stringView, " is out of bounds"))); |
| } |
| return result; |
| } |
| |
| auto Parser::parseConstantExpression() -> Expected<AST::ConstantExpression, Error> |
| { |
| auto type = consumeTypes< |
| Token::Type::IntLiteral, |
| Token::Type::UintLiteral, |
| Token::Type::FloatLiteral, |
| Token::Type::Null, |
| Token::Type::True, |
| Token::Type::False, |
| Token::Type::Identifier>(); |
| if (!type) |
| return makeUnexpected(type.error()); |
| |
| switch (type->type) { |
| case Token::Type::IntLiteral: { |
| auto value = intLiteralToInt(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return {{ AST::IntegerLiteral({ *type }, *value) }}; |
| } |
| case Token::Type::UintLiteral: { |
| auto value = uintLiteralToUint(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return {{ AST::UnsignedIntegerLiteral({ *type }, *value) }}; |
| } |
| case Token::Type::FloatLiteral: { |
| auto value = floatLiteralToFloat(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return {{ AST::FloatLiteral({ *type }, *value) }}; |
| } |
| case Token::Type::True: |
| return { AST::BooleanLiteral(WTFMove(*type), true) }; |
| case Token::Type::False: |
| return { AST::BooleanLiteral(WTFMove(*type), false) }; |
| default: { |
| ASSERT(type->type == Token::Type::Identifier); |
| CONSUME_TYPE(fullStop, FullStop); |
| CONSUME_TYPE(next, Identifier); |
| return { AST::EnumerationMemberLiteral({ *type, *next }, type->stringView(m_lexer).toString(), next->stringView(m_lexer).toString()) }; |
| } |
| } |
| } |
| |
| auto Parser::parseTypeArgument() -> Expected<AST::TypeArgument, Error> |
| { |
| PEEK(nextToken); |
| PEEK_FURTHER(furtherToken); |
| if (nextToken->type != Token::Type::Identifier || furtherToken->type == Token::Type::FullStop) { |
| PARSE(constantExpression, ConstantExpression); |
| return AST::TypeArgument(WTFMove(*constantExpression)); |
| } |
| CONSUME_TYPE(result, Identifier); |
| CodeLocation location(*result); |
| return AST::TypeArgument(AST::TypeReference::create(location, result->stringView(m_lexer).toString(), AST::TypeArguments())); |
| } |
| |
| auto Parser::parseTypeArguments() -> Expected<AST::TypeArguments, Error> |
| { |
| AST::TypeArguments typeArguments; |
| auto lessThanSign = tryType(Token::Type::LessThanSign); |
| if (!lessThanSign) |
| return typeArguments; |
| |
| auto greaterThanSign = tryType(Token::Type::GreaterThanSign); |
| if (greaterThanSign) |
| return typeArguments; |
| |
| PARSE(typeArgument, TypeArgument); |
| typeArguments.append(WTFMove(*typeArgument)); |
| |
| while (true) { |
| auto greaterThanSign = tryType(Token::Type::GreaterThanSign); |
| if (greaterThanSign) |
| break; |
| |
| CONSUME_TYPE(comma, Comma); |
| PARSE(typeArgument, TypeArgument); |
| typeArguments.append(WTFMove(*typeArgument)); |
| } |
| |
| return typeArguments; |
| } |
| |
| auto Parser::parseTypeSuffixAbbreviated() -> Expected<TypeSuffixAbbreviated, Error> |
| { |
| auto token = consumeTypes< |
| Token::Type::Star, |
| Token::Type::SquareBracketPair, |
| Token::Type::LeftSquareBracket>(); |
| if (!token) |
| return makeUnexpected(token.error()); |
| if (token->type == Token::Type::LeftSquareBracket) { |
| auto numElements = consumeNonNegativeIntegralLiteral(); |
| if (!numElements) |
| return makeUnexpected(numElements.error()); |
| CONSUME_TYPE(rightSquareBracket, RightSquareBracket); |
| return {{ { *token, *rightSquareBracket }, *token, *numElements }}; |
| } |
| return {{ { *token }, *token, WTF::nullopt }}; |
| } |
| |
| auto Parser::parseTypeSuffixNonAbbreviated() -> Expected<TypeSuffixNonAbbreviated, Error> |
| { |
| auto token = consumeTypes< |
| Token::Type::Star, |
| Token::Type::SquareBracketPair, |
| Token::Type::LeftSquareBracket>(); |
| if (!token) |
| return makeUnexpected(token.error()); |
| if (token->type == Token::Type::LeftSquareBracket) { |
| auto numElements = consumeNonNegativeIntegralLiteral(); |
| if (!numElements) |
| return makeUnexpected(numElements.error()); |
| CONSUME_TYPE(rightSquareBracket, RightSquareBracket); |
| return {{ { *token, *rightSquareBracket }, *token, WTF::nullopt, *numElements }}; |
| } |
| auto addressSpaceToken = consumeTypes< |
| Token::Type::Constant, |
| Token::Type::Device, |
| Token::Type::Threadgroup, |
| Token::Type::Thread>(); |
| if (!addressSpaceToken) |
| return makeUnexpected(addressSpaceToken.error()); |
| AST::AddressSpace addressSpace; |
| switch (addressSpaceToken->type) { |
| case Token::Type::Constant: |
| addressSpace = AST::AddressSpace::Constant; |
| break; |
| case Token::Type::Device: |
| addressSpace = AST::AddressSpace::Device; |
| break; |
| case Token::Type::Threadgroup: |
| addressSpace = AST::AddressSpace::Threadgroup; |
| break; |
| default: |
| ASSERT(addressSpaceToken->type == Token::Type::Thread); |
| addressSpace = AST::AddressSpace::Thread; |
| break; |
| } |
| return {{ { *token }, *token, { addressSpace }, WTF::nullopt }}; |
| } |
| |
| auto Parser::parseType() -> Expected<Ref<AST::UnnamedType>, Error> |
| { |
| auto addressSpaceToken = tryTypes< |
| Token::Type::Constant, |
| Token::Type::Device, |
| Token::Type::Threadgroup, |
| Token::Type::Thread>(); |
| |
| CONSUME_TYPE(name, Identifier); |
| PARSE(typeArguments, TypeArguments); |
| |
| if (addressSpaceToken) { |
| AST::AddressSpace addressSpace; |
| switch (addressSpaceToken->type) { |
| case Token::Type::Constant: |
| addressSpace = AST::AddressSpace::Constant; |
| break; |
| case Token::Type::Device: |
| addressSpace = AST::AddressSpace::Device; |
| break; |
| case Token::Type::Threadgroup: |
| addressSpace = AST::AddressSpace::Threadgroup; |
| break; |
| default: |
| ASSERT(addressSpaceToken->type == Token::Type::Thread); |
| addressSpace = AST::AddressSpace::Thread; |
| break; |
| } |
| auto constructTypeFromSuffixAbbreviated = [&](const TypeSuffixAbbreviated& typeSuffixAbbreviated, Ref<AST::UnnamedType>&& previous) -> Ref<AST::UnnamedType> { |
| CodeLocation location(*addressSpaceToken, typeSuffixAbbreviated.location); |
| switch (typeSuffixAbbreviated.token.type) { |
| case Token::Type::Star: |
| return { AST::PointerType::create(location, addressSpace, WTFMove(previous)) }; |
| case Token::Type::SquareBracketPair: |
| return { AST::ArrayReferenceType::create(location, addressSpace, WTFMove(previous)) }; |
| default: |
| ASSERT(typeSuffixAbbreviated.token.type == Token::Type::LeftSquareBracket); |
| return { AST::ArrayType::create(location, WTFMove(previous), *typeSuffixAbbreviated.numElements) }; |
| } |
| }; |
| PARSE(firstTypeSuffixAbbreviated, TypeSuffixAbbreviated); |
| Ref<AST::UnnamedType> result = AST::TypeReference::create(WTFMove(*addressSpaceToken), name->stringView(m_lexer).toString(), WTFMove(*typeArguments)); |
| auto next = constructTypeFromSuffixAbbreviated(*firstTypeSuffixAbbreviated, WTFMove(result)); |
| result = WTFMove(next); |
| while (true) { |
| PEEK(nextToken); |
| if (nextToken->type != Token::Type::Star |
| && nextToken->type != Token::Type::SquareBracketPair |
| && nextToken->type != Token::Type::LeftSquareBracket) { |
| break; |
| } |
| PARSE(typeSuffixAbbreviated, TypeSuffixAbbreviated); |
| // FIXME: The nesting here might be in the wrong order. |
| next = constructTypeFromSuffixAbbreviated(*typeSuffixAbbreviated, WTFMove(result)); |
| result = WTFMove(next); |
| } |
| return WTFMove(result); |
| } |
| |
| auto constructTypeFromSuffixNonAbbreviated = [&](const TypeSuffixNonAbbreviated& typeSuffixNonAbbreviated, Ref<AST::UnnamedType>&& previous) -> Ref<AST::UnnamedType> { |
| CodeLocation location(*name, typeSuffixNonAbbreviated.location); |
| switch (typeSuffixNonAbbreviated.token.type) { |
| case Token::Type::Star: |
| return { AST::PointerType::create(location, *typeSuffixNonAbbreviated.addressSpace, WTFMove(previous)) }; |
| case Token::Type::SquareBracketPair: |
| return { AST::ArrayReferenceType::create(location, *typeSuffixNonAbbreviated.addressSpace, WTFMove(previous)) }; |
| default: |
| ASSERT(typeSuffixNonAbbreviated.token.type == Token::Type::LeftSquareBracket); |
| return { AST::ArrayType::create(location, WTFMove(previous), *typeSuffixNonAbbreviated.numElements) }; |
| } |
| }; |
| Ref<AST::UnnamedType> result = AST::TypeReference::create(*name, name->stringView(m_lexer).toString(), WTFMove(*typeArguments)); |
| while (true) { |
| PEEK(nextToken); |
| if (nextToken->type != Token::Type::Star |
| && nextToken->type != Token::Type::SquareBracketPair |
| && nextToken->type != Token::Type::LeftSquareBracket) { |
| break; |
| } |
| PARSE(typeSuffixNonAbbreviated, TypeSuffixNonAbbreviated); |
| // FIXME: The nesting here might be in the wrong order. |
| auto next = constructTypeFromSuffixNonAbbreviated(*typeSuffixNonAbbreviated, WTFMove(result)); |
| result = WTFMove(next); |
| } |
| return WTFMove(result); |
| } |
| |
| auto Parser::parseTypeDefinition() -> Expected<AST::TypeDefinition, Error> |
| { |
| CONSUME_TYPE(origin, Typedef); |
| CONSUME_TYPE(name, Identifier); |
| CONSUME_TYPE(equals, EqualsSign); |
| PARSE(type, Type); |
| CONSUME_TYPE(semicolon, Semicolon); |
| return AST::TypeDefinition({ *origin, *semicolon }, name->stringView(m_lexer).toString(), WTFMove(*type)); |
| } |
| |
| auto Parser::parseBuiltInSemantic() -> Expected<AST::BuiltInSemantic, Error> |
| { |
| auto origin = consumeTypes< |
| Token::Type::SVInstanceID, |
| Token::Type::SVVertexID, |
| Token::Type::PSize, |
| Token::Type::SVPosition, |
| Token::Type::SVIsFrontFace, |
| Token::Type::SVSampleIndex, |
| Token::Type::SVInnerCoverage, |
| Token::Type::SVTarget, |
| Token::Type::SVDepth, |
| Token::Type::SVCoverage, |
| Token::Type::SVDispatchThreadID, |
| Token::Type::SVGroupID, |
| Token::Type::SVGroupIndex, |
| Token::Type::SVGroupThreadID>(); |
| if (!origin) |
| return makeUnexpected(origin.error()); |
| |
| switch (origin->type) { |
| case Token::Type::SVInstanceID: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVInstanceID); |
| case Token::Type::SVVertexID: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVVertexID); |
| case Token::Type::PSize: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::PSize); |
| case Token::Type::SVPosition: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVPosition); |
| case Token::Type::SVIsFrontFace: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVIsFrontFace); |
| case Token::Type::SVSampleIndex: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVSampleIndex); |
| case Token::Type::SVInnerCoverage: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVInnerCoverage); |
| case Token::Type::SVTarget: { |
| auto target = consumeNonNegativeIntegralLiteral(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=195807 Make this work with strings like "SV_Target0". |
| if (!target) |
| return makeUnexpected(target.error()); |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVTarget, *target); |
| } |
| case Token::Type::SVDepth: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVDepth); |
| case Token::Type::SVCoverage: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVCoverage); |
| case Token::Type::SVDispatchThreadID: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVDispatchThreadID); |
| case Token::Type::SVGroupID: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVGroupID); |
| case Token::Type::SVGroupIndex: |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVGroupIndex); |
| default: |
| ASSERT(origin->type == Token::Type::SVGroupThreadID); |
| return AST::BuiltInSemantic({ *origin }, AST::BuiltInSemantic::Variable::SVGroupThreadID); |
| } |
| } |
| |
| auto Parser::parseResourceSemantic() -> Expected<AST::ResourceSemantic, Error> |
| { |
| CONSUME_TYPE(origin, Register); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| CONSUME_TYPE(info, Identifier); |
| auto infoStringView = info->stringView(m_lexer); |
| if (infoStringView.length() < 2 || (infoStringView[0] != 'u' |
| && infoStringView[0] != 't' |
| && infoStringView[0] != 'b' |
| && infoStringView[0] != 's')) |
| return makeUnexpected(Error(makeString(infoStringView.substring(0, 1), " is not a known resource type ('u', 't', 'b', or 's')"))); |
| |
| AST::ResourceSemantic::Mode mode; |
| switch (infoStringView[0]) { |
| case 'u': |
| mode = AST::ResourceSemantic::Mode::UnorderedAccessView; |
| break; |
| case 't': |
| mode = AST::ResourceSemantic::Mode::Texture; |
| break; |
| case 'b': |
| mode = AST::ResourceSemantic::Mode::Buffer; |
| break; |
| case 's': |
| mode = AST::ResourceSemantic::Mode::Sampler; |
| break; |
| } |
| |
| auto index = recognizeSimpleUnsignedInteger(infoStringView.substring(1)); |
| if (!index) |
| return makeUnexpected(index.error()); |
| |
| unsigned space = 0; |
| if (tryType(Token::Type::Comma)) { |
| CONSUME_TYPE(spaceToken, Identifier); |
| auto spaceTokenStringView = spaceToken->stringView(m_lexer); |
| StringView prefix { "space" }; |
| if (!spaceTokenStringView.startsWith(prefix)) |
| return makeUnexpected(Error(makeString("Second argument to resource semantic ", spaceTokenStringView, " needs be of the form 'space0'"))); |
| if (spaceTokenStringView.length() <= prefix.length()) |
| return makeUnexpected(Error(makeString("Second argument to resource semantic ", spaceTokenStringView, " needs be of the form 'space0'"))); |
| auto spaceValue = recognizeSimpleUnsignedInteger(spaceTokenStringView.substring(prefix.length())); |
| if (!spaceValue) |
| return makeUnexpected(spaceValue.error()); |
| space = *spaceValue; |
| } |
| |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| |
| return AST::ResourceSemantic({ *origin, *rightParenthesis }, mode, *index, space); |
| } |
| |
| auto Parser::parseSpecializationConstantSemantic() -> Expected<AST::SpecializationConstantSemantic, Error> |
| { |
| CONSUME_TYPE(origin, Specialized); |
| return AST::SpecializationConstantSemantic(*origin); |
| } |
| |
| auto Parser::parseStageInOutSemantic() -> Expected<AST::StageInOutSemantic, Error> |
| { |
| CONSUME_TYPE(origin, Attribute); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| auto index = consumeNonNegativeIntegralLiteral(); |
| if (!index) |
| return makeUnexpected(index.error()); |
| |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| |
| return AST::StageInOutSemantic({ *origin, *rightParenthesis }, *index); |
| } |
| |
| auto Parser::parseSemantic() -> Expected<std::unique_ptr<AST::Semantic>, Error> |
| { |
| if (!tryType(Token::Type::Colon)) |
| return { nullptr }; |
| |
| PEEK(token); |
| switch (token->type) { |
| case Token::Type::Attribute: { |
| PARSE(result, StageInOutSemantic); |
| return { makeUnique<AST::Semantic>(WTFMove(*result)) }; |
| } |
| case Token::Type::Specialized: { |
| PARSE(result, SpecializationConstantSemantic); |
| return { makeUnique<AST::Semantic>(WTFMove(*result)) }; |
| } |
| case Token::Type::Register: { |
| PARSE(result, ResourceSemantic); |
| return { makeUnique<AST::Semantic>(WTFMove(*result)) }; |
| } |
| default: { |
| PARSE(result, BuiltInSemantic); |
| return { makeUnique<AST::Semantic>(WTFMove(*result)) }; |
| } |
| } |
| } |
| AST::Qualifiers Parser::parseQualifiers() |
| { |
| AST::Qualifiers qualifiers; |
| while (auto next = tryType(Token::Type::Qualifier)) { |
| auto nextStringView = next->stringView(m_lexer); |
| if ("nointerpolation" == nextStringView) |
| qualifiers.append(AST::Qualifier::Nointerpolation); |
| else if ("noperspective" == nextStringView) |
| qualifiers.append(AST::Qualifier::Noperspective); |
| else if ("uniform" == nextStringView) |
| qualifiers.append(AST::Qualifier::Uniform); |
| else if ("centroid" == nextStringView) |
| qualifiers.append(AST::Qualifier::Centroid); |
| else { |
| ASSERT("sample" == nextStringView); |
| qualifiers.append(AST::Qualifier::Sample); |
| } |
| } |
| return qualifiers; |
| } |
| |
| auto Parser::parseStructureElement() -> Expected<AST::StructureElement, Error> |
| { |
| PEEK(origin); |
| |
| AST::Qualifiers qualifiers = parseQualifiers(); |
| |
| PARSE(type, Type); |
| CONSUME_TYPE(name, Identifier); |
| PARSE(semantic, Semantic); |
| CONSUME_TYPE(semicolon, Semicolon); |
| |
| return AST::StructureElement({ *origin, *semicolon }, WTFMove(qualifiers), WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*semantic)); |
| } |
| |
| auto Parser::parseStructureDefinition() -> Expected<AST::StructureDefinition, Error> |
| { |
| CONSUME_TYPE(origin, Struct); |
| CONSUME_TYPE(name, Identifier); |
| CONSUME_TYPE(leftCurlyBracket, LeftCurlyBracket); |
| |
| AST::StructureElements structureElements; |
| while (!peekType(Token::Type::RightCurlyBracket)) { |
| PARSE(structureElement, StructureElement); |
| structureElements.append(WTFMove(*structureElement)); |
| } |
| |
| auto rightCurlyBracket = m_lexer.consumeToken(); |
| |
| return AST::StructureDefinition({ *origin, rightCurlyBracket }, name->stringView(m_lexer).toString(), WTFMove(structureElements)); |
| } |
| |
| auto Parser::parseEnumerationDefinition() -> Expected<AST::EnumerationDefinition, Error> |
| { |
| CONSUME_TYPE(origin, Enum); |
| CONSUME_TYPE(name, Identifier); |
| |
| auto type = ([&]() -> Expected<Ref<AST::UnnamedType>, Error> { |
| if (tryType(Token::Type::Colon)) { |
| PARSE(parsedType, Type); |
| return WTFMove(*parsedType); |
| } |
| return { AST::TypeReference::create(*origin, "int"_str, AST::TypeArguments()) }; |
| })(); |
| if (!type) |
| return makeUnexpected(type.error()); |
| |
| CONSUME_TYPE(leftCurlyBracket, LeftCurlyBracket); |
| |
| int64_t nextValue = 0; |
| PARSE(firstEnumerationMember, EnumerationMember, nextValue); |
| nextValue = firstEnumerationMember->value() + 1; |
| |
| AST::EnumerationDefinition result({ }, name->stringView(m_lexer).toString(), WTFMove(*type)); |
| auto success = result.add(WTFMove(*firstEnumerationMember)); |
| if (!success) |
| return fail("Cannot add enumeration member"_str); |
| |
| while (tryType(Token::Type::Comma)) { |
| PARSE(member, EnumerationMember, nextValue); |
| nextValue = member->value() + 1; |
| success = result.add(WTFMove(*member)); |
| if (!success) |
| return fail("Cannot add enumeration member"_str); |
| } |
| |
| CONSUME_TYPE(rightCurlyBracket, RightCurlyBracket); |
| result.updateCodeLocation({ *origin, *rightCurlyBracket}); |
| |
| return WTFMove(result); |
| } |
| |
| auto Parser::parseEnumerationMember(int64_t defaultValue) -> Expected<AST::EnumerationMember, Error> |
| { |
| CONSUME_TYPE(identifier, Identifier); |
| auto name = identifier->stringView(m_lexer).toString(); |
| |
| if (tryType(Token::Type::EqualsSign)) { |
| PARSE(constantExpression, ConstantExpression); |
| |
| Optional<int64_t> value; |
| constantExpression->visit(WTF::makeVisitor([&](AST::IntegerLiteral& integerLiteral) { |
| value = integerLiteral.value(); |
| }, [&](AST::UnsignedIntegerLiteral& unsignedIntegerLiteral) { |
| value = unsignedIntegerLiteral.value(); |
| }, [&](AST::FloatLiteral&) { |
| }, [&](AST::BooleanLiteral&) { |
| }, [&](AST::EnumerationMemberLiteral&) { |
| })); |
| |
| if (!value) |
| return makeUnexpected(Error("enum initialization values can only be an int or uint constant.")); |
| return AST::EnumerationMember(*identifier, WTFMove(name), *value); |
| } |
| return AST::EnumerationMember(*identifier, WTFMove(name), defaultValue); |
| } |
| |
| auto Parser::parseNativeTypeDeclaration() -> Expected<AST::NativeTypeDeclaration, Error> |
| { |
| CONSUME_TYPE(origin, Native); |
| CONSUME_TYPE(parsedTypedef, Typedef); |
| CONSUME_TYPE(name, Identifier); |
| PARSE(typeArguments, TypeArguments); |
| CONSUME_TYPE(semicolon, Semicolon); |
| |
| return AST::NativeTypeDeclaration({ *origin, *semicolon }, name->stringView(m_lexer).toString(), WTFMove(*typeArguments)); |
| } |
| |
| auto Parser::parseNumThreadsFunctionAttribute() -> Expected<AST::NumThreadsFunctionAttribute, Error> |
| { |
| CONSUME_TYPE(origin, NumThreads); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| auto width = consumeNonNegativeIntegralLiteral(); |
| if (!width) |
| return makeUnexpected(width.error()); |
| |
| CONSUME_TYPE(comma, Comma); |
| |
| auto height = consumeNonNegativeIntegralLiteral(); |
| if (!height) |
| return makeUnexpected(height.error()); |
| |
| CONSUME_TYPE(secondComma, Comma); |
| |
| auto depth = consumeNonNegativeIntegralLiteral(); |
| if (!depth) |
| return makeUnexpected(depth.error()); |
| |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| |
| return AST::NumThreadsFunctionAttribute({ *origin, *rightParenthesis }, *width, *height, *depth); |
| } |
| |
| auto Parser::parseAttributeBlock() -> Expected<AST::AttributeBlock, Error> |
| { |
| CONSUME_TYPE(leftSquareBracket, LeftSquareBracket); |
| |
| AST::AttributeBlock result; |
| |
| while (!tryType(Token::Type::RightSquareBracket)) { |
| PARSE(numThreadsFunctionAttribute, NumThreadsFunctionAttribute); |
| result.append(WTFMove(*numThreadsFunctionAttribute)); |
| } |
| |
| return WTFMove(result); |
| } |
| |
| auto Parser::parseParameter() -> Expected<AST::VariableDeclaration, Error> |
| { |
| auto startOffset = m_lexer.peek().startOffset(); |
| |
| AST::Qualifiers qualifiers = parseQualifiers(); |
| PARSE(type, Type); |
| |
| String name; |
| if (auto token = tryType(Token::Type::Identifier)) |
| name = token->stringView(m_lexer).toString(); |
| |
| PARSE(semantic, Semantic); |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| return AST::VariableDeclaration({ startOffset, endOffset, m_lexer.nameSpace() }, WTFMove(qualifiers), { WTFMove(*type) }, WTFMove(name), WTFMove(*semantic), nullptr); |
| } |
| |
| auto Parser::parseParameters() -> Expected<AST::VariableDeclarations, Error> |
| { |
| AST::VariableDeclarations parameters; |
| |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| if (tryType(Token::Type::RightParenthesis)) |
| return WTFMove(parameters); |
| |
| PARSE(firstParameter, Parameter); |
| parameters.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*firstParameter))); |
| |
| while (tryType(Token::Type::Comma)) { |
| PARSE(parameter, Parameter); |
| parameters.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*parameter))); |
| } |
| |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| |
| return WTFMove(parameters); |
| } |
| |
| auto Parser::parseFunctionDefinition() -> Expected<AST::FunctionDefinition, Error> |
| { |
| PARSE(functionDeclaration, FunctionDeclaration); |
| PARSE(block, Block); |
| return AST::FunctionDefinition(WTFMove(*functionDeclaration), WTFMove(*block)); |
| } |
| |
| auto Parser::parseComputeFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error> |
| { |
| PEEK(origin); |
| |
| PARSE(attributeBlock, AttributeBlock); |
| CONSUME_TYPE(compute, Compute); |
| PARSE(type, Type); |
| CONSUME_TYPE(name, Identifier); |
| PARSE(parameters, Parameters); |
| PARSE(semantic, Semantic); |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| bool isOperator = false; |
| return AST::FunctionDeclaration({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, WTFMove(*attributeBlock), AST::EntryPointType::Compute, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator, m_mode); |
| } |
| |
| auto Parser::parseVertexOrFragmentFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error> |
| { |
| auto entryPoint = consumeTypes<Token::Type::Vertex, Token::Type::Fragment>(); |
| if (!entryPoint) |
| return makeUnexpected(entryPoint.error()); |
| auto entryPointType = (entryPoint->type == Token::Type::Vertex) ? AST::EntryPointType::Vertex : AST::EntryPointType::Fragment; |
| |
| PARSE(type, Type); |
| CONSUME_TYPE(name, Identifier); |
| PARSE(parameters, Parameters); |
| PARSE(semantic, Semantic); |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| bool isOperator = false; |
| return AST::FunctionDeclaration({ entryPoint->startOffset(), endOffset, m_lexer.nameSpace() }, { }, entryPointType, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator, m_mode); |
| } |
| |
| auto Parser::parseRegularFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error> |
| { |
| PEEK(origin); |
| |
| PARSE(type, Type); |
| |
| auto name = consumeTypes<Token::Type::Identifier, Token::Type::OperatorName>(); |
| if (!name) |
| return makeUnexpected(name.error()); |
| auto isOperator = name->type == Token::Type::OperatorName; |
| |
| PARSE(parameters, Parameters); |
| PARSE(semantic, Semantic); |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| return AST::FunctionDeclaration({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, { }, WTF::nullopt, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator, m_mode); |
| } |
| |
| auto Parser::parseOperatorFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error> |
| { |
| CONSUME_TYPE(origin, Operator); |
| PARSE(type, Type); |
| PARSE(parameters, Parameters); |
| PARSE(semantic, Semantic); |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| bool isOperator = true; |
| return AST::FunctionDeclaration({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, { }, WTF::nullopt, WTFMove(*type), "operator cast"_str, WTFMove(*parameters), WTFMove(*semantic), isOperator, m_mode); |
| } |
| |
| auto Parser::parseFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error> |
| { |
| PEEK(token); |
| switch (token->type) { |
| case Token::Type::Operator: |
| return parseOperatorFunctionDeclaration(); |
| case Token::Type::Vertex: |
| case Token::Type::Fragment: |
| return parseVertexOrFragmentFunctionDeclaration(); |
| case Token::Type::LeftSquareBracket: |
| return parseComputeFunctionDeclaration(); |
| default: |
| return parseRegularFunctionDeclaration(); |
| } |
| } |
| |
| auto Parser::parseNativeFunctionDeclaration() -> Expected<AST::NativeFunctionDeclaration, Error> |
| { |
| CONSUME_TYPE(native, Native); |
| PARSE(functionDeclaration, FunctionDeclaration); |
| CONSUME_TYPE(semicolon, Semicolon); |
| |
| return AST::NativeFunctionDeclaration(WTFMove(*functionDeclaration)); |
| } |
| |
| auto Parser::parseBlock() -> Expected<AST::Block, Error> |
| { |
| CONSUME_TYPE(origin, LeftCurlyBracket); |
| PARSE(result, BlockBody); |
| CONSUME_TYPE(rightCurlyBracket, RightCurlyBracket); |
| result->updateCodeLocation({ *origin, *rightCurlyBracket }); |
| return WTFMove(*result); |
| } |
| |
| auto Parser::parseBlockBody() -> Expected<AST::Block, Error> |
| { |
| auto startOffset = m_lexer.peek().startOffset(); |
| |
| AST::Statements statements; |
| while (!peekTypes<Token::Type::RightCurlyBracket, Token::Type::Case, Token::Type::Default>()) { |
| PARSE(statement, Statement); |
| statements.append(WTFMove(*statement)); |
| } |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| return AST::Block({ startOffset, endOffset, m_lexer.nameSpace() }, WTFMove(statements)); |
| } |
| |
| auto Parser::parseIfStatement() -> Expected<AST::IfStatement, Error> |
| { |
| CONSUME_TYPE(origin, If); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| PARSE(conditional, Expression); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| PARSE(body, Statement); |
| |
| std::unique_ptr<AST::Statement> elseBody(nullptr); |
| if (tryType(Token::Type::Else)) { |
| PARSE(parsedElseBody, Statement); |
| elseBody = (*parsedElseBody).moveToUniquePtr(); |
| } |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| |
| Vector<UniqueRef<AST::Expression>> castArguments; |
| castArguments.append(WTFMove(*conditional)); |
| auto boolCast = makeUniqueRef<AST::CallExpression>(Token(*origin), "bool"_str, WTFMove(castArguments)); |
| return AST::IfStatement({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, WTFMove(boolCast), WTFMove(*body), WTFMove(elseBody)); |
| } |
| |
| auto Parser::parseSwitchStatement() -> Expected<AST::SwitchStatement, Error> |
| { |
| CONSUME_TYPE(origin, Switch); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| PARSE(value, Expression); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| CONSUME_TYPE(leftCurlyBracket, LeftCurlyBracket); |
| |
| Vector<AST::SwitchCase> switchCases; |
| PEEK(nextToken); |
| while (nextToken->type != Token::Type::RightCurlyBracket) { |
| PARSE(switchCase, SwitchCase); |
| switchCases.append(WTFMove(*switchCase)); |
| PEEK(nextTokenInLoop); |
| nextToken = nextTokenInLoop; |
| } |
| |
| auto endToken = m_lexer.consumeToken(); |
| |
| return AST::SwitchStatement({ *origin, endToken }, WTFMove(*value), WTFMove(switchCases)); |
| } |
| |
| auto Parser::parseSwitchCase() -> Expected<AST::SwitchCase, Error> |
| { |
| auto origin = consumeTypes<Token::Type::Case, Token::Type::Default>(); |
| if (!origin) |
| return makeUnexpected(origin.error()); |
| |
| switch (origin->type) { |
| case Token::Type::Case: { |
| PARSE(value, ConstantExpression); |
| CONSUME_TYPE(colon, Colon); |
| |
| PARSE(block, BlockBody); |
| |
| return AST::SwitchCase({ origin->codeLocation, block->codeLocation()}, WTFMove(*value), WTFMove(*block)); |
| } |
| default: { |
| ASSERT(origin->type == Token::Type::Default); |
| CONSUME_TYPE(colon, Colon); |
| |
| PARSE(block, BlockBody); |
| |
| return AST::SwitchCase({ origin->codeLocation, block->codeLocation()}, WTF::nullopt, WTFMove(*block)); |
| } |
| } |
| } |
| |
| auto Parser::parseForLoop() -> Expected<AST::ForLoop, Error> |
| { |
| CONSUME_TYPE(origin, For); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| auto parseRemainder = [&](UniqueRef<AST::Statement>&& initialization) -> Expected<AST::ForLoop, Error> { |
| CONSUME_TYPE(semicolon, Semicolon); |
| |
| std::unique_ptr<AST::Expression> condition(nullptr); |
| if (!tryType(Token::Type::Semicolon)) { |
| if (auto expression = parseExpression()) |
| condition = (*expression).moveToUniquePtr(); |
| else |
| return makeUnexpected(expression.error()); |
| CONSUME_TYPE(secondSemicolon, Semicolon); |
| } |
| |
| std::unique_ptr<AST::Expression> increment(nullptr); |
| if (!tryType(Token::Type::RightParenthesis)) { |
| if (auto expression = parseExpression()) |
| increment = (*expression).moveToUniquePtr(); |
| else |
| return makeUnexpected(expression.error()); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| } |
| |
| PARSE(body, Statement); |
| CodeLocation location(origin->codeLocation, (*body)->codeLocation()); |
| return AST::ForLoop(location, WTFMove(initialization), WTFMove(condition), WTFMove(increment), WTFMove(*body)); |
| }; |
| |
| auto variableDeclarations = backtrackingScope<Expected<AST::VariableDeclarationsStatement, Error>>([&]() { |
| return parseVariableDeclarations(); |
| }); |
| if (variableDeclarations) { |
| UniqueRef<AST::Statement> declarationStatement = makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)); |
| return parseRemainder(WTFMove(declarationStatement)); |
| } |
| |
| PARSE(effectfulExpression, EffectfulExpression); |
| |
| return parseRemainder(WTFMove(*effectfulExpression)); |
| } |
| |
| auto Parser::parseWhileLoop() -> Expected<AST::WhileLoop, Error> |
| { |
| CONSUME_TYPE(origin, While); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| PARSE(conditional, Expression); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| PARSE(body, Statement); |
| |
| CodeLocation location(origin->codeLocation, (*body)->codeLocation()); |
| return AST::WhileLoop(location, WTFMove(*conditional), WTFMove(*body)); |
| } |
| |
| auto Parser::parseDoWhileLoop() -> Expected<AST::DoWhileLoop, Error> |
| { |
| CONSUME_TYPE(origin, Do); |
| PARSE(body, Statement); |
| CONSUME_TYPE(whileKeyword, While); |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| PARSE(conditional, Expression); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| CONSUME_TYPE(semicolon, Semicolon); |
| |
| return AST::DoWhileLoop({ *origin, *semicolon}, WTFMove(*body), WTFMove(*conditional)); |
| } |
| |
| auto Parser::parseVariableDeclaration(Ref<AST::UnnamedType>&& type) -> Expected<AST::VariableDeclaration, Error> |
| { |
| PEEK(origin); |
| |
| auto qualifiers = parseQualifiers(); |
| |
| CONSUME_TYPE(name, Identifier); |
| PARSE(semantic, Semantic); |
| |
| std::unique_ptr<AST::Expression> initializer = nullptr; |
| if (tryType(Token::Type::EqualsSign)) { |
| PARSE(initializingExpression, PossibleTernaryConditional); |
| initializer = initializingExpression.value().moveToUniquePtr(); |
| } |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| return AST::VariableDeclaration({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, WTFMove(qualifiers), { WTFMove(type) }, name->stringView(m_lexer).toString(), WTFMove(*semantic), WTFMove(initializer)); |
| } |
| |
| auto Parser::parseVariableDeclarations() -> Expected<AST::VariableDeclarationsStatement, Error> |
| { |
| PEEK(origin); |
| |
| PARSE(type, Type); |
| |
| auto firstVariableDeclaration = parseVariableDeclaration(type->copyRef()); |
| if (!firstVariableDeclaration) |
| return makeUnexpected(firstVariableDeclaration.error()); |
| |
| Vector<UniqueRef<AST::VariableDeclaration>> result; |
| result.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*firstVariableDeclaration))); |
| |
| while (tryType(Token::Type::Comma)) { |
| auto variableDeclaration = parseVariableDeclaration(type->copyRef()); |
| if (!variableDeclaration) |
| return makeUnexpected(variableDeclaration.error()); |
| result.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*variableDeclaration))); |
| } |
| |
| auto endOffset = m_lexer.peek().startOffset(); |
| return AST::VariableDeclarationsStatement({ origin->startOffset(), endOffset, m_lexer.nameSpace() }, WTFMove(result)); |
| } |
| |
| auto Parser::parseStatement() -> Expected<UniqueRef<AST::Statement>, Error> |
| { |
| PEEK(token); |
| switch (token->type) { |
| case Token::Type::LeftCurlyBracket: { |
| PARSE(block, Block); |
| return { makeUniqueRef<AST::Block>(WTFMove(*block)) }; |
| } |
| case Token::Type::If: { |
| PARSE(ifStatement, IfStatement); |
| return { makeUniqueRef<AST::IfStatement>(WTFMove(*ifStatement)) }; |
| } |
| case Token::Type::Switch: { |
| PARSE(switchStatement, SwitchStatement); |
| return { makeUniqueRef<AST::SwitchStatement>(WTFMove(*switchStatement)) }; |
| } |
| case Token::Type::For: { |
| PARSE(forLoop, ForLoop); |
| return { makeUniqueRef<AST::ForLoop>(WTFMove(*forLoop)) }; |
| } |
| case Token::Type::While: { |
| PARSE(whileLoop, WhileLoop); |
| return { makeUniqueRef<AST::WhileLoop>(WTFMove(*whileLoop)) }; |
| } |
| case Token::Type::Do: { |
| PARSE(doWhileLoop, DoWhileLoop); |
| return { makeUniqueRef<AST::DoWhileLoop>(WTFMove(*doWhileLoop)) }; |
| } |
| case Token::Type::Break: { |
| auto breakToken = m_lexer.consumeToken(); |
| CONSUME_TYPE(semicolon, Semicolon); |
| auto breakObject = AST::Break(WTFMove(breakToken)); |
| return { makeUniqueRef<AST::Break>(WTFMove(breakObject)) }; |
| } |
| case Token::Type::Continue: { |
| auto continueToken = m_lexer.consumeToken(); |
| CONSUME_TYPE(semicolon, Semicolon); |
| auto continueObject = AST::Continue(WTFMove(continueToken)); |
| return { makeUniqueRef<AST::Continue>(WTFMove(continueObject)) }; |
| } |
| case Token::Type::Fallthrough: { |
| auto fallthroughToken = m_lexer.consumeToken(); |
| CONSUME_TYPE(semicolon, Semicolon); |
| auto fallthroughObject = AST::Fallthrough(WTFMove(fallthroughToken)); |
| return { makeUniqueRef<AST::Fallthrough>(WTFMove(fallthroughObject)) }; |
| } |
| case Token::Type::Return: { |
| auto returnToken = m_lexer.consumeToken(); |
| if (auto semicolon = tryType(Token::Type::Semicolon)) { |
| auto returnObject = AST::Return(WTFMove(returnToken), nullptr); |
| return { makeUniqueRef<AST::Return>(WTFMove(returnObject)) }; |
| } |
| PARSE(expression, Expression); |
| CONSUME_TYPE(finalSemicolon, Semicolon); |
| auto returnObject = AST::Return(WTFMove(returnToken), (*expression).moveToUniquePtr()); |
| return { makeUniqueRef<AST::Return>(WTFMove(returnObject)) }; |
| } |
| case Token::Type::Constant: |
| case Token::Type::Device: |
| case Token::Type::Threadgroup: |
| case Token::Type::Thread: { |
| PARSE(variableDeclarations, VariableDeclarations); |
| CONSUME_TYPE(semicolon, Semicolon); |
| return { makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)) }; |
| } |
| case Token::Type::Identifier: { |
| PEEK_FURTHER(nextToken); |
| switch (nextToken->type) { |
| case Token::Type::Identifier: |
| case Token::Type::LessThanSign: |
| case Token::Type::Star: |
| case Token::Type::Qualifier: { |
| PARSE(variableDeclarations, VariableDeclarations); |
| CONSUME_TYPE(semicolon, Semicolon); |
| return { makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)) }; |
| } |
| default: |
| break; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| { |
| auto effectfulExpression = backtrackingScope<Expected<UniqueRef<AST::Statement>, Error>>([&]() -> Expected<UniqueRef<AST::Statement>, Error> { |
| PARSE(result, EffectfulExpression); |
| CONSUME_TYPE(semicolon, Semicolon); |
| return result; |
| }); |
| if (effectfulExpression) |
| return WTFMove(*effectfulExpression); |
| } |
| |
| PARSE(variableDeclarations, VariableDeclarations); |
| CONSUME_TYPE(semicolon, Semicolon); |
| return { makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)) }; |
| } |
| |
| auto Parser::parseEffectfulExpression() -> Expected<UniqueRef<AST::Statement>, Error> |
| { |
| PEEK(origin); |
| if (origin->type == Token::Type::Semicolon) |
| return { makeUniqueRef<AST::Block>(*origin, Vector<UniqueRef<AST::Statement>>()) }; |
| |
| Vector<UniqueRef<AST::Expression>> expressions; |
| PARSE(effectfulExpression, EffectfulAssignment); |
| expressions.append(WTFMove(*effectfulExpression)); |
| |
| while (tryType(Token::Type::Comma)) { |
| PARSE(expression, EffectfulAssignment); |
| expressions.append(WTFMove(*expression)); |
| } |
| |
| if (expressions.size() == 1) |
| return { makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(expressions[0])) }; |
| unsigned endOffset = m_lexer.peek().startOffset(); |
| CodeLocation location(origin->startOffset(), endOffset, m_lexer.nameSpace()); |
| auto commaExpression = makeUniqueRef<AST::CommaExpression>(location, WTFMove(expressions)); |
| return { makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(commaExpression)) }; |
| } |
| |
| auto Parser::parseEffectfulAssignment() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PEEK(origin); |
| |
| bool isEffectful = false; |
| PARSE(expression, PossiblePrefix, &isEffectful); |
| |
| if (!isEffectful || peekTypes< |
| Token::Type::EqualsSign, |
| Token::Type::PlusEquals, |
| Token::Type::MinusEquals, |
| Token::Type::TimesEquals, |
| Token::Type::DivideEquals, |
| Token::Type::ModEquals, |
| Token::Type::XorEquals, |
| Token::Type::AndEquals, |
| Token::Type::OrEquals, |
| Token::Type::RightShiftEquals, |
| Token::Type::LeftShiftEquals |
| >()) { |
| return completeAssignment(WTFMove(*expression)); |
| } |
| |
| return expression; |
| } |
| |
| auto Parser::parseLimitedSuffixOperator(UniqueRef<AST::Expression>&& previous) -> SuffixExpression |
| { |
| auto type = consumeTypes< |
| Token::Type::FullStop, |
| Token::Type::Arrow, |
| Token::Type::LeftSquareBracket>(); |
| if (!type) |
| return SuffixExpression(WTFMove(previous), false); |
| |
| switch (type->type) { |
| case Token::Type::FullStop: { |
| auto identifier = consumeType(Token::Type::Identifier); |
| if (!identifier) |
| return SuffixExpression(WTFMove(previous), false); |
| CodeLocation location(previous->codeLocation(), *identifier); |
| return SuffixExpression(makeUniqueRef<AST::DotExpression>(location, WTFMove(previous), identifier->stringView(m_lexer).toString()), true); |
| } |
| case Token::Type::Arrow: { |
| auto identifier = consumeType(Token::Type::Identifier); |
| if (!identifier) |
| return SuffixExpression(WTFMove(previous), false); |
| CodeLocation location(previous->codeLocation(), *identifier); |
| return SuffixExpression(makeUniqueRef<AST::DotExpression>(location, makeUniqueRef<AST::DereferenceExpression>(location, WTFMove(previous)), identifier->stringView(m_lexer).toString()), true); |
| } |
| default: { |
| ASSERT(type->type == Token::Type::LeftSquareBracket); |
| auto expression = parseExpression(); |
| if (!expression) |
| return SuffixExpression(WTFMove(previous), false); |
| if (auto rightSquareBracket = consumeType(Token::Type::RightSquareBracket)) { |
| CodeLocation location(previous->codeLocation(), *rightSquareBracket); |
| return SuffixExpression(makeUniqueRef<AST::IndexExpression>(location, WTFMove(previous), WTFMove(*expression)), true); |
| } |
| return SuffixExpression(WTFMove(previous), false); |
| } |
| } |
| } |
| |
| auto Parser::parseSuffixOperator(UniqueRef<AST::Expression>&& previous) -> SuffixExpression |
| { |
| auto suffix = consumeTypes< |
| Token::Type::FullStop, |
| Token::Type::Arrow, |
| Token::Type::LeftSquareBracket, |
| Token::Type::PlusPlus, |
| Token::Type::MinusMinus>(); |
| if (!suffix) |
| return SuffixExpression(WTFMove(previous), false); |
| |
| switch (suffix->type) { |
| case Token::Type::FullStop: { |
| auto identifier = consumeType(Token::Type::Identifier); |
| if (!identifier) |
| return SuffixExpression(WTFMove(previous), false); |
| CodeLocation location(previous->codeLocation(), *identifier); |
| return SuffixExpression(makeUniqueRef<AST::DotExpression>(location, WTFMove(previous), identifier->stringView(m_lexer).toString()), true); |
| } |
| case Token::Type::Arrow: { |
| auto identifier = consumeType(Token::Type::Identifier); |
| if (!identifier) |
| return SuffixExpression(WTFMove(previous), false); |
| CodeLocation location(previous->codeLocation(), *identifier); |
| return SuffixExpression(makeUniqueRef<AST::DotExpression>(location, makeUniqueRef<AST::DereferenceExpression>(WTFMove(*suffix), WTFMove(previous)), identifier->stringView(m_lexer).toString()), true); |
| } |
| case Token::Type::LeftSquareBracket: { |
| auto expression = parseExpression(); |
| if (!expression) |
| return SuffixExpression(WTFMove(previous), false); |
| if (auto rightSquareBracket = consumeType(Token::Type::RightSquareBracket)) { |
| CodeLocation location(previous->codeLocation(), *rightSquareBracket); |
| return SuffixExpression(makeUniqueRef<AST::IndexExpression>(location, WTFMove(previous), WTFMove(*expression)), true); |
| } |
| return SuffixExpression(WTFMove(previous), false); |
| } |
| case Token::Type::PlusPlus: { |
| CodeLocation location(previous->codeLocation(), *suffix); |
| auto result = makeUniqueRef<AST::ReadModifyWriteExpression>(location, WTFMove(previous)); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(result->oldVariableReference()); |
| result->setNewValueExpression(makeUniqueRef<AST::CallExpression>(location, "operator++"_str, WTFMove(callArguments))); |
| result->setResultExpression(result->oldVariableReference()); |
| return SuffixExpression(WTFMove(result), true); |
| } |
| default: { |
| ASSERT(suffix->type == Token::Type::MinusMinus); |
| CodeLocation location(previous->codeLocation(), *suffix); |
| auto result = makeUniqueRef<AST::ReadModifyWriteExpression>(location, WTFMove(previous)); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(result->oldVariableReference()); |
| result->setNewValueExpression(makeUniqueRef<AST::CallExpression>(location, "operator--"_str, WTFMove(callArguments))); |
| result->setResultExpression(result->oldVariableReference()); |
| return SuffixExpression(WTFMove(result), true); |
| } |
| } |
| } |
| |
| auto Parser::parseExpression() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(first, PossibleTernaryConditional); |
| Vector<UniqueRef<AST::Expression>> expressions; |
| unsigned startOffset = (*first)->codeLocation().startOffset(); |
| expressions.append(WTFMove(*first)); |
| |
| while (tryType(Token::Type::Comma)) { |
| PARSE(expression, PossibleTernaryConditional); |
| expressions.append(WTFMove(*expression)); |
| } |
| |
| if (expressions.size() == 1) |
| return WTFMove(expressions[0]); |
| auto endOffset = m_lexer.peek().startOffset(); |
| CodeLocation location(startOffset, endOffset, m_lexer.nameSpace()); |
| return { makeUniqueRef<AST::CommaExpression>(location, WTFMove(expressions)) }; |
| } |
| |
| auto Parser::completeTernaryConditional(UniqueRef<AST::Expression>&& predicate) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| CONSUME_TYPE(questionMark, QuestionMark); |
| PARSE(bodyExpression, Expression); |
| CONSUME_TYPE(colon, Colon); |
| PARSE(elseExpression, PossibleTernaryConditional); |
| |
| CodeLocation predicateLocation = predicate->codeLocation(); |
| Vector<UniqueRef<AST::Expression>> castArguments; |
| castArguments.append(WTFMove(predicate)); |
| auto boolCast = makeUniqueRef<AST::CallExpression>(predicateLocation, "bool"_str, WTFMove(castArguments)); |
| CodeLocation location(predicateLocation, (*elseExpression)->codeLocation()); |
| return { makeUniqueRef<AST::TernaryExpression>(location, WTFMove(boolCast), WTFMove(*bodyExpression), WTFMove(*elseExpression)) }; |
| } |
| |
| auto Parser::completeAssignment(UniqueRef<AST::Expression>&& left) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| auto assignmentOperator = consumeTypes< |
| Token::Type::EqualsSign, |
| Token::Type::PlusEquals, |
| Token::Type::MinusEquals, |
| Token::Type::TimesEquals, |
| Token::Type::DivideEquals, |
| Token::Type::ModEquals, |
| Token::Type::XorEquals, |
| Token::Type::AndEquals, |
| Token::Type::OrEquals, |
| Token::Type::RightShiftEquals, |
| Token::Type::LeftShiftEquals>(); |
| if (!assignmentOperator) |
| return makeUnexpected(assignmentOperator.error()); |
| |
| PARSE(right, PossibleTernaryConditional); |
| CodeLocation location = { left->codeLocation(), (*right)->codeLocation() }; |
| |
| if (assignmentOperator->type == Token::Type::EqualsSign) |
| return { makeUniqueRef<AST::AssignmentExpression>(location, WTFMove(left), WTFMove(*right))}; |
| |
| String name; |
| switch (assignmentOperator->type) { |
| case Token::Type::PlusEquals: |
| name = "operator+"_str; |
| break; |
| case Token::Type::MinusEquals: |
| name = "operator-"_str; |
| break; |
| case Token::Type::TimesEquals: |
| name = "operator*"_str; |
| break; |
| case Token::Type::DivideEquals: |
| name = "operator/"_str; |
| break; |
| case Token::Type::ModEquals: |
| name = "operator%"_str; |
| break; |
| case Token::Type::XorEquals: |
| name = "operator^"_str; |
| break; |
| case Token::Type::AndEquals: |
| name = "operator&"_str; |
| break; |
| case Token::Type::OrEquals: |
| name = "operator|"_str; |
| break; |
| case Token::Type::RightShiftEquals: |
| name = "operator>>"_str; |
| break; |
| default: |
| ASSERT(assignmentOperator->type == Token::Type::LeftShiftEquals); |
| name = "operator<<"_str; |
| break; |
| } |
| |
| auto result = makeUniqueRef<AST::ReadModifyWriteExpression>(location, WTFMove(left)); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(result->oldVariableReference()); |
| callArguments.append(WTFMove(*right)); |
| result->setNewValueExpression(makeUniqueRef<AST::CallExpression>(location, WTFMove(name), WTFMove(callArguments))); |
| result->setResultExpression(result->newVariableReference()); |
| return { WTFMove(result) }; |
| } |
| |
| auto Parser::parsePossibleTernaryConditional() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(expression, PossiblePrefix); |
| |
| if (peekTypes<Token::Type::EqualsSign, |
| Token::Type::PlusEquals, |
| Token::Type::MinusEquals, |
| Token::Type::TimesEquals, |
| Token::Type::DivideEquals, |
| Token::Type::ModEquals, |
| Token::Type::XorEquals, |
| Token::Type::AndEquals, |
| Token::Type::OrEquals, |
| Token::Type::RightShiftEquals, |
| Token::Type::LeftShiftEquals>()) { |
| return completeAssignment(WTFMove(*expression)); |
| } |
| |
| expression = completePossibleShift(WTFMove(*expression)); |
| if (!expression) |
| return makeUnexpected(expression.error()); |
| |
| expression = completePossibleMultiply(WTFMove(*expression)); |
| if (!expression) |
| return makeUnexpected(expression.error()); |
| |
| expression = completePossibleAdd(WTFMove(*expression)); |
| if (!expression) |
| return makeUnexpected(expression.error()); |
| |
| expression = completePossibleRelationalBinaryOperation(WTFMove(*expression)); |
| if (!expression) |
| return makeUnexpected(expression.error()); |
| |
| expression = completePossibleLogicalBinaryOperation(WTFMove(*expression)); |
| if (!expression) |
| return makeUnexpected(expression.error()); |
| |
| PEEK(nextToken); |
| if (nextToken->type == Token::Type::QuestionMark) |
| return completeTernaryConditional(WTFMove(*expression)); |
| return expression; |
| } |
| |
| auto Parser::parsePossibleLogicalBinaryOperation() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(parsedPrevious, PossibleRelationalBinaryOperation); |
| return completePossibleLogicalBinaryOperation(WTFMove(*parsedPrevious)); |
| } |
| |
| auto Parser::completePossibleLogicalBinaryOperation(UniqueRef<AST::Expression>&& previous) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| while (auto logicalBinaryOperation = tryTypes< |
| Token::Type::OrOr, |
| Token::Type::AndAnd, |
| Token::Type::Or, |
| Token::Type::Xor, |
| Token::Type::And |
| >()) { |
| PARSE(next, PossibleRelationalBinaryOperation); |
| CodeLocation location(previous->codeLocation(), (*next)->codeLocation()); |
| |
| switch (logicalBinaryOperation->type) { |
| case Token::Type::OrOr: |
| previous = makeUniqueRef<AST::LogicalExpression>(location, AST::LogicalExpression::Type::Or, WTFMove(previous), WTFMove(*next)); |
| break; |
| case Token::Type::AndAnd: |
| previous = makeUniqueRef<AST::LogicalExpression>(location, AST::LogicalExpression::Type::And, WTFMove(previous), WTFMove(*next)); |
| break; |
| case Token::Type::Or: { |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator|"_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::Xor: { |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator^"_str, WTFMove(callArguments)); |
| break; |
| } |
| default: { |
| ASSERT(logicalBinaryOperation->type == Token::Type::And); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator&"_str, WTFMove(callArguments)); |
| break; |
| } |
| } |
| } |
| |
| return { WTFMove(previous) }; |
| } |
| |
| auto Parser::parsePossibleRelationalBinaryOperation() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(parsedPrevious, PossibleShift); |
| return completePossibleRelationalBinaryOperation(WTFMove(*parsedPrevious)); |
| } |
| |
| auto Parser::completePossibleRelationalBinaryOperation(UniqueRef<AST::Expression>&& previous) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| while (auto relationalBinaryOperation = tryTypes< |
| Token::Type::LessThanSign, |
| Token::Type::GreaterThanSign, |
| Token::Type::LessThanOrEqualTo, |
| Token::Type::GreaterThanOrEqualTo, |
| Token::Type::EqualComparison, |
| Token::Type::NotEqual |
| >()) { |
| PARSE(next, PossibleShift); |
| CodeLocation location(previous->codeLocation(), (*next)->codeLocation()); |
| |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| |
| switch (relationalBinaryOperation->type) { |
| case Token::Type::LessThanSign: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator<"_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::GreaterThanSign: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator>"_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::LessThanOrEqualTo: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator<="_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::GreaterThanOrEqualTo: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator>="_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::EqualComparison: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator=="_str, WTFMove(callArguments)); |
| break; |
| } |
| default: { |
| ASSERT(relationalBinaryOperation->type == Token::Type::NotEqual); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator=="_str, WTFMove(callArguments)); |
| previous = makeUniqueRef<AST::LogicalNotExpression>(location, WTFMove(previous)); |
| break; |
| } |
| } |
| } |
| |
| return WTFMove(previous); |
| } |
| |
| auto Parser::parsePossibleShift() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(parsedPrevious, PossibleAdd); |
| return completePossibleShift(WTFMove(*parsedPrevious)); |
| } |
| |
| auto Parser::completePossibleShift(UniqueRef<AST::Expression>&& previous) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| while (auto shift = tryTypes< |
| Token::Type::LeftShift, |
| Token::Type::RightShift |
| >()) { |
| PARSE(next, PossibleAdd); |
| CodeLocation location(previous->codeLocation(), (*next)->codeLocation()); |
| |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| |
| switch (shift->type) { |
| case Token::Type::LeftShift: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator<<"_str, WTFMove(callArguments)); |
| break; |
| } |
| default: { |
| ASSERT(shift->type == Token::Type::RightShift); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator>>"_str, WTFMove(callArguments)); |
| break; |
| } |
| } |
| } |
| |
| return WTFMove(previous); |
| } |
| |
| auto Parser::parsePossibleAdd() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(parsedPrevious, PossibleMultiply); |
| return completePossibleAdd(WTFMove(*parsedPrevious)); |
| } |
| |
| auto Parser::completePossibleAdd(UniqueRef<AST::Expression>&& previous) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| while (auto add = tryTypes< |
| Token::Type::Plus, |
| Token::Type::Minus |
| >()) { |
| PARSE(next, PossibleMultiply); |
| CodeLocation location(previous->codeLocation(), (*next)->codeLocation()); |
| |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| |
| switch (add->type) { |
| case Token::Type::Plus: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator+"_str, WTFMove(callArguments)); |
| break; |
| } |
| default: { |
| ASSERT(add->type == Token::Type::Minus); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator-"_str, WTFMove(callArguments)); |
| break; |
| } |
| } |
| } |
| |
| return WTFMove(previous); |
| } |
| |
| auto Parser::parsePossibleMultiply() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PARSE(parsedPrevious, PossiblePrefix); |
| return completePossibleMultiply(WTFMove(*parsedPrevious)); |
| } |
| |
| auto Parser::completePossibleMultiply(UniqueRef<AST::Expression>&& previous) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| while (auto multiply = tryTypes< |
| Token::Type::Star, |
| Token::Type::Divide, |
| Token::Type::Mod |
| >()) { |
| PARSE(next, PossiblePrefix); |
| CodeLocation location(previous->codeLocation(), (*next)->codeLocation()); |
| |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(previous)); |
| callArguments.append(WTFMove(*next)); |
| |
| switch (multiply->type) { |
| case Token::Type::Star: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator*"_str, WTFMove(callArguments)); |
| break; |
| } |
| case Token::Type::Divide: { |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator/"_str, WTFMove(callArguments)); |
| break; |
| } |
| default: { |
| ASSERT(multiply->type == Token::Type::Mod); |
| previous = makeUniqueRef<AST::CallExpression>(location, "operator%"_str, WTFMove(callArguments)); |
| break; |
| } |
| } |
| } |
| |
| return WTFMove(previous); |
| } |
| |
| auto Parser::parsePossiblePrefix(bool *isEffectful) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| if (auto prefix = tryTypes< |
| Token::Type::PlusPlus, |
| Token::Type::MinusMinus, |
| Token::Type::Plus, |
| Token::Type::Minus, |
| Token::Type::Tilde, |
| Token::Type::ExclamationPoint, |
| Token::Type::And, |
| Token::Type::At, |
| Token::Type::Star |
| >()) { |
| PARSE(next, PossiblePrefix); |
| CodeLocation location(*prefix, (*next)->codeLocation()); |
| |
| switch (prefix->type) { |
| case Token::Type::PlusPlus: { |
| if (isEffectful) |
| *isEffectful = true; |
| auto result = makeUniqueRef<AST::ReadModifyWriteExpression>(location, WTFMove(*next)); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(result->oldVariableReference()); |
| result->setNewValueExpression(makeUniqueRef<AST::CallExpression>(location, "operator++"_str, WTFMove(callArguments))); |
| result->setResultExpression(result->newVariableReference()); |
| return { WTFMove(result) }; |
| } |
| case Token::Type::MinusMinus: { |
| if (isEffectful) |
| *isEffectful = true; |
| auto result = makeUniqueRef<AST::ReadModifyWriteExpression>(location, WTFMove(*next)); |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(result->oldVariableReference()); |
| result->setNewValueExpression(makeUniqueRef<AST::CallExpression>(location, "operator--"_str, WTFMove(callArguments))); |
| result->setResultExpression(result->newVariableReference()); |
| return { WTFMove(result) }; |
| } |
| case Token::Type::Plus: { |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(*next)); |
| return { makeUniqueRef<AST::CallExpression>(location, "operator+"_str, WTFMove(callArguments)) }; |
| } |
| case Token::Type::Minus: { |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(*next)); |
| return { makeUniqueRef<AST::CallExpression>(location, "operator-"_str, WTFMove(callArguments)) }; |
| } |
| case Token::Type::Tilde: { |
| Vector<UniqueRef<AST::Expression>> callArguments; |
| callArguments.append(WTFMove(*next)); |
| return { makeUniqueRef<AST::CallExpression>(location, "operator~"_str, WTFMove(callArguments)) }; |
| } |
| case Token::Type::ExclamationPoint: { |
| Vector<UniqueRef<AST::Expression>> castArguments; |
| castArguments.append(WTFMove(*next)); |
| auto boolCast = makeUniqueRef<AST::CallExpression>(location, "bool"_str, WTFMove(castArguments)); |
| return { makeUniqueRef<AST::LogicalNotExpression>(location, WTFMove(boolCast)) }; |
| } |
| case Token::Type::And: |
| return { makeUniqueRef<AST::MakePointerExpression>(location, WTFMove(*next), AST::AddressEscapeMode::Escapes) }; |
| case Token::Type::At: |
| return { makeUniqueRef<AST::MakeArrayReferenceExpression>(location, WTFMove(*next), AST::AddressEscapeMode::Escapes) }; |
| default: |
| ASSERT(prefix->type == Token::Type::Star); |
| return { makeUniqueRef<AST::DereferenceExpression>(location, WTFMove(*next)) }; |
| } |
| } |
| |
| return parsePossibleSuffix(isEffectful); |
| } |
| |
| auto Parser::parsePossibleSuffix(bool *isEffectful) -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| PEEK(token); |
| PEEK_FURTHER(nextToken); |
| |
| if (token->type == Token::Type::Identifier && nextToken->type == Token::Type::LeftParenthesis) { |
| PARSE(expression, CallExpression); |
| if (isEffectful) |
| *isEffectful = true; |
| while (true) { |
| PEEK(suffixToken); |
| if (suffixToken->type != Token::Type::FullStop && suffixToken->type != Token::Type::Arrow && suffixToken->type != Token::Type::LeftSquareBracket) |
| break; |
| auto result = parseLimitedSuffixOperator(WTFMove(*expression)); |
| expression = WTFMove(result.result); |
| } |
| return expression; |
| } |
| |
| if (token->type == Token::Type::LeftParenthesis && isEffectful) |
| *isEffectful = true; |
| |
| PARSE(expression, Term); |
| bool isLastSuffixTokenEffectful = false; |
| while (true) { |
| PEEK(suffixToken); |
| if (suffixToken->type != Token::Type::FullStop |
| && suffixToken->type != Token::Type::Arrow |
| && suffixToken->type != Token::Type::LeftSquareBracket |
| && suffixToken->type != Token::Type::PlusPlus |
| && suffixToken->type != Token::Type::MinusMinus) { |
| break; |
| } |
| isLastSuffixTokenEffectful = suffixToken->type == Token::Type::PlusPlus || suffixToken->type == Token::Type::MinusMinus; |
| auto result = parseSuffixOperator(WTFMove(*expression)); |
| expression = WTFMove(result.result); |
| } |
| if (isLastSuffixTokenEffectful && isEffectful) |
| *isEffectful = true; |
| return expression; |
| } |
| |
| auto Parser::parseCallExpression() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| CONSUME_TYPE(name, Identifier); |
| auto callName = name->stringView(m_lexer).toString(); |
| |
| CONSUME_TYPE(leftParenthesis, LeftParenthesis); |
| |
| Vector<UniqueRef<AST::Expression>> arguments; |
| if (tryType(Token::Type::RightParenthesis)) |
| return { makeUniqueRef<AST::CallExpression>(WTFMove(*name), WTFMove(callName), WTFMove(arguments)) }; |
| |
| PARSE(firstArgument, PossibleTernaryConditional); |
| arguments.append(WTFMove(*firstArgument)); |
| while (tryType(Token::Type::Comma)) { |
| PARSE(argument, PossibleTernaryConditional); |
| arguments.append(WTFMove(*argument)); |
| } |
| |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| CodeLocation location(*name, *rightParenthesis); |
| |
| return { makeUniqueRef<AST::CallExpression>(location, WTFMove(callName), WTFMove(arguments)) }; |
| } |
| |
| auto Parser::parseTerm() -> Expected<UniqueRef<AST::Expression>, Error> |
| { |
| auto type = consumeTypes< |
| Token::Type::IntLiteral, |
| Token::Type::UintLiteral, |
| Token::Type::FloatLiteral, |
| Token::Type::Null, |
| Token::Type::True, |
| Token::Type::False, |
| Token::Type::Identifier, |
| Token::Type::LeftParenthesis>(); |
| if (!type) |
| return makeUnexpected(type.error()); |
| |
| switch (type->type) { |
| case Token::Type::IntLiteral: { |
| auto value = intLiteralToInt(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return { makeUniqueRef<AST::IntegerLiteral>(*type, *value) }; |
| } |
| case Token::Type::UintLiteral: { |
| auto value = uintLiteralToUint(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return { makeUniqueRef<AST::UnsignedIntegerLiteral>(*type, *value) }; |
| } |
| case Token::Type::FloatLiteral: { |
| auto value = floatLiteralToFloat(type->stringView(m_lexer)); |
| if (!value) |
| return makeUnexpected(value.error()); |
| return { makeUniqueRef<AST::FloatLiteral>(*type, *value) }; |
| } |
| case Token::Type::Null: |
| return makeUnexpected(Error("'null' is a reserved keyword.", type->codeLocation)); |
| case Token::Type::True: |
| return { makeUniqueRef<AST::BooleanLiteral>(*type, true) }; |
| case Token::Type::False: |
| return { makeUniqueRef<AST::BooleanLiteral>(*type, false) }; |
| case Token::Type::Identifier: { |
| auto name = type->stringView(m_lexer).toString(); |
| return { makeUniqueRef<AST::VariableReference>(*type, WTFMove(name)) }; |
| } |
| default: { |
| ASSERT(type->type == Token::Type::LeftParenthesis); |
| PARSE(expression, Expression); |
| CONSUME_TYPE(rightParenthesis, RightParenthesis); |
| |
| return { WTFMove(*expression) }; |
| } |
| } |
| } |
| |
| } // namespace WHLSL |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(WEBGPU) |