blob: 2be121755fb7dee57021701193c82f7dba252815 [file] [log] [blame]
/*
* 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 <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 Unexpected<Error>(name.error()); \
#define CONSUME_TYPE(name, type) \
auto name = consumeType(Token::Type::type); \
if (!name) \
return Unexpected<Error>(name.error());
#define PEEK(name) \
auto name = peek(); \
if (!name) \
return Unexpected<Error>(name.error());
#define PEEK_FURTHER(name) \
auto name = peekFurther(); \
if (!name) \
return Unexpected<Error>(name.error());
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=195682 Return a better error code from this, and report it to JavaScript.
auto Parser::parse(Program& program, StringView stringView, Mode mode) -> Optional<Error>
{
m_lexer = Lexer(stringView);
m_mode = mode;
while (!m_lexer.isFullyConsumed()) {
auto token = m_lexer.peek();
switch (token.type) {
case Token::Type::Invalid:
return WTF::nullopt;
case Token::Type::Semicolon:
m_lexer.consumeToken();
continue;
case Token::Type::Typedef: {
auto typeDefinition = parseTypeDefinition();
if (!typeDefinition)
return typeDefinition.error();
program.append(WTFMove(*typeDefinition));
continue;
}
case Token::Type::Struct: {
auto structureDefinition = parseStructureDefinition();
if (!structureDefinition)
return structureDefinition.error();
program.append(WTFMove(*structureDefinition));
continue;
}
case Token::Type::Enum: {
auto enumerationDefinition = parseEnumerationDefinition();
if (!enumerationDefinition)
return enumerationDefinition.error();
program.append(WTFMove(*enumerationDefinition));
continue;
}
case Token::Type::Native: {
ASSERT(m_mode == Mode::StandardLibrary);
auto furtherToken = peekFurther();
if (!furtherToken)
return WTF::nullopt;
if (furtherToken->type == Token::Type::Typedef) {
auto nativeTypeDeclaration = parseNativeTypeDeclaration();
if (!nativeTypeDeclaration)
return nativeTypeDeclaration.error();
program.append(WTFMove(*nativeTypeDeclaration));
continue;
}
auto nativeFunctionDeclaration = parseNativeFunctionDeclaration();
if (!nativeFunctionDeclaration)
return nativeFunctionDeclaration.error();
program.append(WTFMove(*nativeFunctionDeclaration));
continue;
}
default: {
auto functionDefinition = parseFunctionDefinition();
if (!functionDefinition)
return functionDefinition.error();
program.append(WTFMove(*functionDefinition));
continue;
}
}
}
return WTF::nullopt;
}
auto Parser::fail(const String& message, TryToPeek tryToPeek) -> Unexpected<Error>
{
if (tryToPeek == TryToPeek::Yes) {
if (auto nextToken = peek())
return Unexpected<Error>(Error(m_lexer.errorString(*nextToken, message)));
}
return Unexpected<Error>(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));
builder.append(", ");
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, Parser::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 Unexpected<Parser::Error>(Parser::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 Unexpected<Parser::Error>(Parser::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 Unexpected<Parser::Error>(Parser::Error(makeString("int literal ", text, " is out of bounds")));
return { static_cast<int>(result) };
}
static Expected<unsigned, Parser::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 Unexpected<Parser::Error>(Parser::Error(makeString("uint literal ", text, " is out of bounds")));
}
return { result };
}
static Expected<float, Parser::Error> floatLiteralToFloat(StringView text)
{
size_t parsedLength;
auto result = parseDouble(text, parsedLength);
if (parsedLength != text.length())
return Unexpected<Parser::Error>(Parser::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 Unexpected<Error>(integralLiteralToken.error());
switch (integralLiteralToken->type) {
case Token::Type::IntLiteral: {
auto result = intLiteralToInt(integralLiteralToken->stringView(m_lexer));
if (result)
return {{ *result }};
return Unexpected<Error>(result.error());
}
default: {
ASSERT(integralLiteralToken->type == Token::Type::UintLiteral);
auto result = uintLiteralToUint(integralLiteralToken->stringView(m_lexer));
if (result)
return {{ *result }};
return Unexpected<Error>(result.error());
}
}
}
auto Parser::consumeNonNegativeIntegralLiteral() -> Expected<unsigned, Error>
{
auto integralLiteral = consumeIntegralLiteral();
if (!integralLiteral)
return Unexpected<Error>(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, Parser::Error> recognizeSimpleUnsignedInteger(StringView stringView)
{
unsigned result = 0;
if (stringView.length() < 1)
return Unexpected<Parser::Error>(Parser::Error(makeString("Simple unsigned literal ", stringView, " is too short")));
for (auto codePoint : stringView.codePoints()) {
if (codePoint < '0' || codePoint > '9')
return Unexpected<Parser::Error>(Parser::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 Unexpected<Parser::Error>(Parser::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 Unexpected<Error>(type.error());
switch (type->type) {
case Token::Type::IntLiteral: {
auto value = intLiteralToInt(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return {{ AST::IntegerLiteral({ *type }, *value) }};
}
case Token::Type::UintLiteral: {
auto value = uintLiteralToUint(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return {{ AST::UnsignedIntegerLiteral({ *type }, *value) }};
}
case Token::Type::FloatLiteral: {
auto value = floatLiteralToFloat(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return {{ AST::FloatLiteral({ *type }, *value) }};
}
case Token::Type::Null:
return { AST::NullLiteral(WTFMove(*type)) };
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);
AST::CodeLocation location(*result);
return AST::TypeArgument(makeUniqueRef<AST::TypeReference>(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 Unexpected<Error>(token.error());
if (token->type == Token::Type::LeftSquareBracket) {
auto numElements = consumeNonNegativeIntegralLiteral();
if (!numElements)
return Unexpected<Error>(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 Unexpected<Error>(token.error());
if (token->type == Token::Type::LeftSquareBracket) {
auto numElements = consumeNonNegativeIntegralLiteral();
if (!numElements)
return Unexpected<Error>(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 Unexpected<Error>(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<UniqueRef<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, UniqueRef<AST::UnnamedType>&& previous) -> UniqueRef<AST::UnnamedType> {
AST::CodeLocation location(*addressSpaceToken, typeSuffixAbbreviated.location);
switch (typeSuffixAbbreviated.token.type) {
case Token::Type::Star:
return { makeUniqueRef<AST::PointerType>(location, addressSpace, WTFMove(previous)) };
case Token::Type::SquareBracketPair:
return { makeUniqueRef<AST::ArrayReferenceType>(location, addressSpace, WTFMove(previous)) };
default:
ASSERT(typeSuffixAbbreviated.token.type == Token::Type::LeftSquareBracket);
return { makeUniqueRef<AST::ArrayType>(location, WTFMove(previous), *typeSuffixAbbreviated.numElements) };
}
};
PARSE(firstTypeSuffixAbbreviated, TypeSuffixAbbreviated);
UniqueRef<AST::UnnamedType> result = makeUniqueRef<AST::TypeReference>(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, UniqueRef<AST::UnnamedType>&& previous) -> UniqueRef<AST::UnnamedType> {
AST::CodeLocation location(*name, typeSuffixNonAbbreviated.location);
switch (typeSuffixNonAbbreviated.token.type) {
case Token::Type::Star:
return { makeUniqueRef<AST::PointerType>(location, *typeSuffixNonAbbreviated.addressSpace, WTFMove(previous)) };
case Token::Type::SquareBracketPair:
return { makeUniqueRef<AST::ArrayReferenceType>(location, *typeSuffixNonAbbreviated.addressSpace, WTFMove(previous)) };
default:
ASSERT(typeSuffixNonAbbreviated.token.type == Token::Type::LeftSquareBracket);
return { makeUniqueRef<AST::ArrayType>(location, WTFMove(previous), *typeSuffixNonAbbreviated.numElements) };
}
};
UniqueRef<AST::UnnamedType> result = makeUniqueRef<AST::TypeReference>(*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 Unexpected<Error>(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 Unexpected<Error>(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 Unexpected<Error>(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 Unexpected<Error>(index.error());
unsigned space = 0;
if (tryType(Token::Type::Comma)) {
CONSUME_TYPE(spaceToken, Identifier);
auto spaceTokenStringView = spaceToken->stringView(m_lexer);
auto prefix = "space"_str;
if (!spaceTokenStringView.startsWith(StringView(prefix)))
return Unexpected<Error>(Error(makeString("Second argument to resource semantic ", spaceTokenStringView, " needs be of the form 'space0'")));
if (spaceTokenStringView.length() <= prefix.length())
return Unexpected<Error>(Error(makeString("Second argument to resource semantic ", spaceTokenStringView, " needs be of the form 'space0'")));
auto spaceValue = recognizeSimpleUnsignedInteger(spaceTokenStringView.substring(prefix.length()));
if (!spaceValue)
return Unexpected<Error>(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 Unexpected<Error>(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 { std::make_unique<AST::Semantic>(WTFMove(*result)) };
}
case Token::Type::Specialized: {
PARSE(result, SpecializationConstantSemantic);
return { std::make_unique<AST::Semantic>(WTFMove(*result)) };
}
case Token::Type::Register: {
PARSE(result, ResourceSemantic);
return { std::make_unique<AST::Semantic>(WTFMove(*result)) };
}
default: {
PARSE(result, BuiltInSemantic);
return { std::make_unique<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<UniqueRef<AST::UnnamedType>, Error> {
if (tryType(Token::Type::Colon)) {
PARSE(parsedType, Type);
return WTFMove(*parsedType);
}
return { makeUniqueRef<AST::TypeReference>(*origin, "int"_str, AST::TypeArguments()) };
})();
if (!type)
return Unexpected<Error>(type.error());
CONSUME_TYPE(leftCurlyBracket, LeftCurlyBracket);
PARSE(firstEnumerationMember, EnumerationMember);
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);
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() -> Expected<AST::EnumerationMember, Error>
{
CONSUME_TYPE(identifier, Identifier);
auto name = identifier->stringView(m_lexer).toString();
if (tryType(Token::Type::EqualsSign)) {
PARSE(constantExpression, ConstantExpression);
return AST::EnumerationMember(*identifier, WTFMove(name), WTFMove(*constantExpression));
}
return AST::EnumerationMember(*identifier, WTFMove(name));
}
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 Unexpected<Error>(width.error());
CONSUME_TYPE(comma, Comma);
auto height = consumeNonNegativeIntegralLiteral();
if (!height)
return Unexpected<Error>(height.error());
CONSUME_TYPE(secondComma, Comma);
auto depth = consumeNonNegativeIntegralLiteral();
if (!depth)
return Unexpected<Error>(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 }, 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 }, WTFMove(*attributeBlock), AST::EntryPointType::Compute, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator);
}
auto Parser::parseVertexOrFragmentFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error>
{
auto entryPoint = consumeTypes<Token::Type::Vertex, Token::Type::Fragment>();
if (!entryPoint)
return Unexpected<Error>(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 }, { }, entryPointType, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator);
}
auto Parser::parseRegularFunctionDeclaration() -> Expected<AST::FunctionDeclaration, Error>
{
PEEK(origin);
PARSE(type, Type);
auto name = consumeTypes<Token::Type::Identifier, Token::Type::OperatorName>();
if (!name)
return Unexpected<Error>(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 }, { }, WTF::nullopt, WTFMove(*type), name->stringView(m_lexer).toString(), WTFMove(*parameters), WTFMove(*semantic), isOperator);
}
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 }, { }, WTF::nullopt, WTFMove(*type), "operator cast"_str, WTFMove(*parameters), WTFMove(*semantic), isOperator);
}
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>()) {
bool allowVariableDeclarations = true;
PARSE(statement, Statement, allowVariableDeclarations);
statements.append(WTFMove(*statement));
}
auto endOffset = m_lexer.peek().startOffset();
return AST::Block({ startOffset, endOffset}, 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 }, 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 Unexpected<Error>(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::Block, Error>
{
CONSUME_TYPE(origin, For);
CONSUME_TYPE(leftParenthesis, LeftParenthesis);
auto variableDeclarations = backtrackingScope<Expected<AST::VariableDeclarationsStatement, Error>>([&]() {
return parseVariableDeclarations();
});
Optional<UniqueRef<AST::Statement>> initialization;
if (variableDeclarations)
initialization = static_cast<UniqueRef<AST::Statement>>(makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)));
else {
PARSE(effectfulExpression, EffectfulExpression);
initialization = WTFMove(*effectfulExpression);
}
CONSUME_TYPE(semicolon, Semicolon);
Optional<UniqueRef<AST::Expression>> condition;
auto secondSemicolon = tryType(Token::Type::Semicolon);
if (!secondSemicolon) {
PARSE(expression, Expression);
condition = WTFMove(*expression);
CONSUME_TYPE(secondSemicolon, Semicolon);
} else
condition = static_cast<UniqueRef<AST::Expression>>(makeUniqueRef<AST::BooleanLiteral>(*secondSemicolon, true));
Optional<UniqueRef<AST::Expression>> increment;
auto rightParenthesis = tryType(Token::Type::RightParenthesis);
if (!rightParenthesis) {
PARSE(expression, Expression);
increment = WTFMove(*expression);
CONSUME_TYPE(rightParenthesis, RightParenthesis);
} else
increment = static_cast<UniqueRef<AST::Expression>>(makeUniqueRef<AST::BooleanLiteral>(*origin, true)); // FIXME: NullLiteral would make more sense, but is buggy right now. Anything side-effect free is fine.
PARSE(body, Statement);
AST::CodeLocation location(origin->codeLocation, (*body)->codeLocation());
auto forLoop = makeUniqueRef<AST::ForLoop>(location, WTFMove(*condition), WTFMove(*increment), WTFMove(*body));
AST::Statements statements = Vector<UniqueRef<AST::Statement>>::from(WTFMove(*initialization), WTFMove(forLoop));
return AST::Block(location, WTFMove(statements));
}
auto Parser::parseWhileLoop() -> Expected<AST::ForLoop, Error>
{
CONSUME_TYPE(origin, While);
CONSUME_TYPE(leftParenthesis, LeftParenthesis);
PARSE(conditional, Expression);
CONSUME_TYPE(rightParenthesis, RightParenthesis);
PARSE(body, Statement);
AST::CodeLocation location(origin->codeLocation, (*body)->codeLocation());
auto increment = makeUniqueRef<AST::BooleanLiteral>(*origin, true); // FIXME: NullLiteral would make more sense, but is buggy right now. Anything side-effect free is fine.
return AST::ForLoop(location, WTFMove(*conditional), WTFMove(increment), 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(UniqueRef<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 }, 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)->clone());
if (!firstVariableDeclaration)
return Unexpected<Error>(firstVariableDeclaration.error());
Vector<UniqueRef<AST::VariableDeclaration>> result;
result.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*firstVariableDeclaration)));
while (tryType(Token::Type::Comma)) {
auto variableDeclaration = parseVariableDeclaration((*type)->clone());
if (!variableDeclaration)
return Unexpected<Error>(variableDeclaration.error());
result.append(makeUniqueRef<AST::VariableDeclaration>(WTFMove(*variableDeclaration)));
}
auto endOffset = m_lexer.peek().startOffset();
return AST::VariableDeclarationsStatement({ origin->startOffset(), endOffset }, WTFMove(result));
}
auto Parser::parseStatement(bool allowVariableDeclarations) -> 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::Block>(WTFMove(*forLoop)) };
}
case Token::Type::While: {
PARSE(whileLoop, WhileLoop);
return { makeUniqueRef<AST::ForLoop>(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::Trap: {
auto trapToken = m_lexer.consumeToken();
CONSUME_TYPE(semicolon, Semicolon);
auto trapObject = AST::Trap(WTFMove(trapToken));
return { makeUniqueRef<AST::Trap>(WTFMove(trapObject)) };
}
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 effectfulExpressionStatement = backtrackingScope<Expected<UniqueRef<AST::Statement>, Error>>([&]() -> Expected<UniqueRef<AST::Statement>, Error> {
PARSE(result, EffectfulExpression);
CONSUME_TYPE(semicolon, Semicolon);
return result;
});
if (effectfulExpressionStatement)
return effectfulExpressionStatement;
}
if (allowVariableDeclarations) {
PARSE(variableDeclarations, VariableDeclarations);
CONSUME_TYPE(semicolon, Semicolon);
return { makeUniqueRef<AST::VariableDeclarationsStatement>(WTFMove(*variableDeclarations)) };
}
return Unexpected<Error>("A variable declaration is only valid inside a block");
}
auto Parser::parseEffectfulExpression() -> Expected<UniqueRef<AST::Statement>, Error>
{
PEEK(origin);
if (origin->type == Token::Type::Semicolon) {
AST::Statements statements;
return { makeUniqueRef<AST::Block>(*origin, WTFMove(statements)) };
}
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();
AST::CodeLocation location(origin->startOffset(), endOffset);
auto expression = makeUniqueRef<AST::CommaExpression>(location, WTFMove(expressions));
return { makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(expression)) };
}
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);
AST::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);
AST::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)) {
AST::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);
AST::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);
AST::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)) {
AST::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: {
AST::CodeLocation location(previous->codeLocation(), *suffix);
auto result = AST::ReadModifyWriteExpression::create(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);
AST::CodeLocation location(previous->codeLocation(), *suffix);
auto result = AST::ReadModifyWriteExpression::create(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();
AST::CodeLocation location(startOffset, endOffset);
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);
AST::CodeLocation predicateLocation = predicate->codeLocation();
Vector<UniqueRef<AST::Expression>> castArguments;
castArguments.append(WTFMove(predicate));
auto boolCast = makeUniqueRef<AST::CallExpression>(predicateLocation, "bool"_str, WTFMove(castArguments));
AST::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 Unexpected<Error>(assignmentOperator.error());
PARSE(right, PossibleTernaryConditional);
AST::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 = AST::ReadModifyWriteExpression::create(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));
expression = completePossibleMultiply(WTFMove(*expression));
expression = completePossibleAdd(WTFMove(*expression));
expression = completePossibleRelationalBinaryOperation(WTFMove(*expression));
expression = completePossibleLogicalBinaryOperation(WTFMove(*expression));
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);
AST::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);
AST::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);
AST::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);
AST::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);
AST::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);
AST::CodeLocation location(*prefix, (*next)->codeLocation());
switch (prefix->type) {
case Token::Type::PlusPlus: {
if (isEffectful)
*isEffectful = true;
auto result = AST::ReadModifyWriteExpression::create(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 = AST::ReadModifyWriteExpression::create(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)) };
case Token::Type::At:
return { makeUniqueRef<AST::MakeArrayReferenceExpression>(location, WTFMove(*next)) };
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);
AST::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 Unexpected<Error>(type.error());
switch (type->type) {
case Token::Type::IntLiteral: {
auto value = intLiteralToInt(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return { makeUniqueRef<AST::IntegerLiteral>(*type, *value) };
}
case Token::Type::UintLiteral: {
auto value = uintLiteralToUint(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return { makeUniqueRef<AST::UnsignedIntegerLiteral>(*type, *value) };
}
case Token::Type::FloatLiteral: {
auto value = floatLiteralToFloat(type->stringView(m_lexer));
if (!value)
return Unexpected<Error>(value.error());
return { makeUniqueRef<AST::FloatLiteral>(*type, *value) };
}
case Token::Type::Null:
return { makeUniqueRef<AST::NullLiteral>(*type) };
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)