Added: trunk/Source/WebGPU/WGSL/Parser.cpp (0 => 291075)
--- trunk/Source/WebGPU/WGSL/Parser.cpp (rev 0)
+++ trunk/Source/WebGPU/WGSL/Parser.cpp 2022-03-09 22:25:11 UTC (rev 291075)
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2022 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 "Parser.h"
+#include "config.h"
+
+#include "AST/Attribute.h"
+#include "AST/_expression_.h"
+#include "AST/Expressions/IdentifierExpression.h"
+#include "AST/Expressions/LiteralExpressions.h"
+#include "AST/Expressions/StructureAccess.h"
+#include "AST/Expressions/TypeConversion.h"
+#include "AST/GlobalDecl.h"
+#include "AST/Statement.h"
+#include "AST/Statements/AssignmentStatement.h"
+#include "AST/Statements/CompoundStatement.h"
+#include "AST/Statements/ReturnStatement.h"
+#include "AST/StructureDecl.h"
+#include "Lexer.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace WGSL {
+
+#define START_PARSE() \
+ auto _startOfElementPosition = m_lexer.currentPosition();
+
+#define CURRENT_SOURCE_SPAN() \
+ SourceSpan(_startOfElementPosition, m_lexer.currentPosition())
+
+#define RETURN_NODE(type, ...) \
+ do { \
+ AST::type astNodeResult(CURRENT_SOURCE_SPAN(), __VA_ARGS__); \
+ return { WTFMove(astNodeResult) }; \
+ } while (false)
+
+// Passing 0 arguments beyond the type to RETURN_NODE is invalid because of a stupid limitation of the C preprocessor
+#define RETURN_NODE_NO_ARGS(type) \
+ do { \
+ AST::type astNodeResult(CURRENT_SOURCE_SPAN()); \
+ return { WTFMove(astNodeResult) }; \
+ } while (false)
+
+#define RETURN_NODE_REF(type, ...) \
+ return { makeUniqueRef<AST::type>(CURRENT_SOURCE_SPAN(), __VA_ARGS__) };
+
+// Passing 0 arguments beyond the type to RETURN_NODE_REF is invalid because of a stupid limitation of the C preprocessor
+#define RETURN_NODE_REF_NO_ARGS(type) \
+ return { makeUniqueRef<AST::type>(CURRENT_SOURCE_SPAN()) };
+
+#define FAIL(string) \
+ return makeUnexpected(Error(string, CURRENT_SOURCE_SPAN()));
+
+// Warning: cannot use the do..while trick because it defines a new identifier named `name`.
+// So do not use after an if/for/while without braces.
+#define PARSE(name, element, ...) \
+ auto name##Expected = parse##element(__VA_ARGS__); \
+ if (!name##Expected) \
+ return makeUnexpected(name##Expected.error()); \
+ auto& name = *name##Expected;
+
+// Warning: cannot use the do..while trick because it defines a new identifier named `name`.
+// So do not use after an if/for/while without braces.
+#define CONSUME_TYPE_NAMED(name, type) \
+ auto name##Expected = consumeType(TokenType::type); \
+ if (!name##Expected) { \
+ StringBuilder builder; \
+ builder.append("Expected a "); \
+ builder.append(toString(TokenType::type)); \
+ builder.append(", but got a "); \
+ builder.append(toString(name##Expected.error())); \
+ FAIL(builder.toString()); \
+ } \
+ auto& name = *name##Expected;
+
+#define CONSUME_TYPE(type) \
+ do { \
+ auto expectedToken = consumeType(TokenType::type); \
+ if (!expectedToken) { \
+ StringBuilder builder; \
+ builder.append("Expected a "); \
+ builder.append(toString(TokenType::type)); \
+ builder.append(", but got a "); \
+ builder.append(toString(expectedToken.error())); \
+ FAIL(builder.toString()); \
+ } \
+ } while (false)
+
+template<typename Lexer>
+class Parser {
+public:
+ Parser(Lexer& lexer)
+ : m_lexer(lexer)
+ , m_current(lexer.lex())
+ {
+ }
+
+ Expected<AST::ShaderModule, Error> parseShader();
+
+private:
+ // UniqueRef whenever it can return multiple types.
+ Expected<UniqueRef<AST::GlobalDecl>, Error> parseGlobalDecl();
+ Expected<AST::Attributes, Error> parseAttributes();
+ Expected<UniqueRef<AST::Attribute>, Error> parseAttribute();
+ Expected<AST::StructDecl, Error> parseStructDecl(AST::Attributes&&);
+ Expected<AST::StructMember, Error> parseStructMember();
+ Expected<UniqueRef<AST::TypeDecl>, Error> parseTypeDecl();
+ Expected<UniqueRef<AST::TypeDecl>, Error> parseTypeDeclAfterIdentifier(StringView&&, SourcePosition start);
+ Expected<AST::GlobalVariableDecl, Error> parseGlobalVariableDecl(AST::Attributes&&);
+ Expected<AST::VariableQualifier, Error> parseVariableQualifier();
+ Expected<AST::StorageClass, Error> parseStorageClass();
+ Expected<AST::AccessMode, Error> parseAccessMode();
+ Expected<AST::FunctionDecl, Error> parseFunctionDecl(AST::Attributes&&);
+ Expected<AST::Parameter, Error> parseParameter();
+ Expected<UniqueRef<AST::Statement>, Error> parseStatement();
+ Expected<AST::CompoundStatement, Error> parseCompoundStatement();
+ Expected<AST::ReturnStatement, Error> parseReturnStatement();
+ Expected<UniqueRef<AST::_expression_>, Error> parseShortCircuitOrExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseRelationalExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseShiftExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseAdditiveExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseMultiplicativeExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseUnaryExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseSingularExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parsePostfixExpression(UniqueRef<AST::_expression_>&& base, SourcePosition startPosition);
+ Expected<UniqueRef<AST::_expression_>, Error> parsePrimaryExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseLHSExpression();
+ Expected<UniqueRef<AST::_expression_>, Error> parseCoreLHSExpression();
+ Expected<Vector<UniqueRef<AST::_expression_>>, Error> parseArgumentExpressionList();
+
+ Expected<Token, TokenType> consumeType(TokenType);
+ void consume();
+
+ Token& current() { return m_current; }
+
+ Lexer& m_lexer;
+ Token m_current;
+};
+
+template<typename Lexer>
+Expected<AST::ShaderModule, Error> parse(const String& wgsl)
+{
+ Lexer lexer(wgsl);
+ Parser parser(lexer);
+
+ return parser.parseShader();
+}
+
+Expected<AST::ShaderModule, Error> parseLChar(const String& wgsl)
+{
+ return parse<Lexer<LChar>>(wgsl);
+}
+
+Expected<AST::ShaderModule, Error> parseUChar(const String& wgsl)
+{
+ return parse<Lexer<UChar>>(wgsl);
+}
+
+template<typename Lexer>
+Expected<Token, TokenType> Parser<Lexer>::consumeType(TokenType type)
+{
+ if (current().m_type == type) {
+ Expected<Token, TokenType> result = { m_current };
+ m_current = m_lexer.lex();
+ return result;
+ }
+ return makeUnexpected(current().m_type);
+}
+
+template<typename Lexer>
+void Parser<Lexer>::consume()
+{
+ m_current = m_lexer.lex();
+}
+
+template<typename Lexer>
+Expected<AST::ShaderModule, Error> Parser<Lexer>::parseShader()
+{
+ START_PARSE();
+
+ Vector<UniqueRef<AST::GlobalDirective>> directives;
+ // FIXME: parse directives here.
+
+ Vector<UniqueRef<AST::GlobalDecl>> decls;
+ while (!m_lexer.isAtEndOfFile()) {
+ PARSE(globalDecl, GlobalDecl)
+ decls.append(WTFMove(globalDecl));
+ }
+
+ RETURN_NODE(ShaderModule, WTFMove(directives), WTFMove(decls));
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::GlobalDecl>, Error> Parser<Lexer>::parseGlobalDecl()
+{
+ START_PARSE();
+
+ PARSE(attributes, Attributes);
+
+ switch (current().m_type) {
+ case TokenType::KeywordStruct: {
+ PARSE(structDecl, StructDecl, WTFMove(attributes));
+ return { makeUniqueRef<AST::StructDecl>(WTFMove(structDecl)) };
+ }
+ case TokenType::KeywordVar: {
+ PARSE(varDecl, GlobalVariableDecl, WTFMove(attributes));
+ CONSUME_TYPE(Semicolon);
+ return { makeUniqueRef<AST::GlobalVariableDecl>(WTFMove(varDecl)) };
+ }
+ case TokenType::KeywordFn: {
+ PARSE(fn, FunctionDecl, WTFMove(attributes));
+ return { makeUniqueRef<AST::FunctionDecl>(WTFMove(fn)) };
+ }
+ default:
+ FAIL("Trying to parse a GlobalDecl, expected 'var', 'fn', or 'struct'.");
+ }
+}
+
+template<typename Lexer>
+Expected<AST::Attributes, Error> Parser<Lexer>::parseAttributes()
+{
+ AST::Attributes attributes;
+
+ while (current().m_type == TokenType::Attribute) {
+ PARSE(firstAttribute, Attribute);
+ attributes.append(WTFMove(firstAttribute));
+ }
+
+ return { WTFMove(attributes) };
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::Attribute>, Error> Parser<Lexer>::parseAttribute()
+{
+ START_PARSE();
+
+ CONSUME_TYPE(Attribute);
+ CONSUME_TYPE_NAMED(ident, Identifier);
+ if (ident.m_ident == "group") {
+ CONSUME_TYPE(ParenLeft);
+ // FIXME: should more kinds of literals be accepted here?
+ CONSUME_TYPE_NAMED(id, IntegerLiteral);
+ CONSUME_TYPE(ParenRight);
+ RETURN_NODE_REF(GroupAttribute, id.m_literalValue);
+ }
+ if (ident.m_ident == "binding") {
+ CONSUME_TYPE(ParenLeft);
+ // FIXME: should more kinds of literals be accepted here?
+ CONSUME_TYPE_NAMED(id, IntegerLiteral);
+ CONSUME_TYPE(ParenRight);
+ RETURN_NODE_REF(BindingAttribute, id.m_literalValue);
+ }
+ if (ident.m_ident == "stage") {
+ CONSUME_TYPE(ParenLeft);
+ CONSUME_TYPE_NAMED(stage, Identifier);
+ CONSUME_TYPE(ParenRight);
+ if (stage.m_ident == "compute")
+ RETURN_NODE_REF(StageAttribute, AST::StageAttribute::Stage::Compute);
+ if (stage.m_ident == "vertex")
+ RETURN_NODE_REF(StageAttribute, AST::StageAttribute::Stage::Vertex);
+ if (stage.m_ident == "fragment")
+ RETURN_NODE_REF(StageAttribute, AST::StageAttribute::Stage::Fragment);
+ FAIL("Invalid stage attribute, the only options are 'compute', 'vertex', 'fragment'.");
+ }
+ if (ident.m_ident == "location") {
+ CONSUME_TYPE(ParenLeft);
+ // FIXME: should more kinds of literals be accepted here?
+ CONSUME_TYPE_NAMED(id, IntegerLiteral);
+ CONSUME_TYPE(ParenRight);
+ RETURN_NODE_REF(LocationAttribute, id.m_literalValue);
+ }
+ if (ident.m_ident == "builtin") {
+ CONSUME_TYPE(ParenLeft);
+ CONSUME_TYPE_NAMED(name, Identifier);
+ CONSUME_TYPE(ParenRight);
+ RETURN_NODE_REF(BuiltinAttribute, name.m_ident);
+ }
+ FAIL("Unknown attribute, the only supported attributes are 'group', 'binding', 'stage'.");
+}
+
+template<typename Lexer>
+Expected<AST::StructDecl, Error> Parser<Lexer>::parseStructDecl(AST::Attributes&& attributes)
+{
+ START_PARSE();
+
+ CONSUME_TYPE(KeywordStruct);
+ CONSUME_TYPE_NAMED(name, Identifier);
+ CONSUME_TYPE(BraceLeft);
+
+ Vector<UniqueRef<AST::StructMember>> members;
+ while (current().m_type != TokenType::BraceRight) {
+ PARSE(member, StructMember);
+ members.append(makeUniqueRef<AST::StructMember>(WTFMove(member)));
+ }
+
+ CONSUME_TYPE(BraceRight);
+
+ RETURN_NODE(StructDecl, name.m_ident, WTFMove(members), WTFMove(attributes));
+}
+
+template<typename Lexer>
+Expected<AST::StructMember, Error> Parser<Lexer>::parseStructMember()
+{
+ START_PARSE();
+
+ PARSE(attributes, Attributes);
+ CONSUME_TYPE_NAMED(name, Identifier);
+ CONSUME_TYPE(Colon);
+ PARSE(type, TypeDecl);
+ CONSUME_TYPE(Semicolon);
+
+ RETURN_NODE(StructMember, name.m_ident, WTFMove(type), WTFMove(attributes));
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::TypeDecl>, Error> Parser<Lexer>::parseTypeDecl()
+{
+ START_PARSE();
+
+ if (current().m_type == TokenType::KeywordI32) {
+ consume();
+ RETURN_NODE_REF(NamedType, StringView { "i32" });
+ }
+ if (current().m_type == TokenType::KeywordF32) {
+ consume();
+ RETURN_NODE_REF(NamedType, StringView { "f32" });
+ }
+ if (current().m_type == TokenType::KeywordU32) {
+ consume();
+ RETURN_NODE_REF(NamedType, StringView { "u32" });
+ }
+ if (current().m_type == TokenType::KeywordBool) {
+ consume();
+ RETURN_NODE_REF(NamedType, StringView { "bool" });
+ }
+ if (current().m_type == TokenType::Identifier) {
+ CONSUME_TYPE_NAMED(name, Identifier);
+ return parseTypeDeclAfterIdentifier(WTFMove(name.m_ident), _startOfElementPosition);
+ }
+
+ FAIL("Tried parsing a type and it did not start with an identifier");
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::TypeDecl>, Error> Parser<Lexer>::parseTypeDeclAfterIdentifier(StringView&& name, SourcePosition _startOfElementPosition)
+{
+ if (auto kind = AST::ParameterizedType::stringViewToKind(name)) {
+ CONSUME_TYPE(LT);
+ PARSE(elementType, TypeDecl);
+ CONSUME_TYPE(GT);
+ RETURN_NODE_REF(ParameterizedType, *kind, WTFMove(elementType));
+ }
+ RETURN_NODE_REF(NamedType, WTFMove(name));
+}
+
+template<typename Lexer>
+Expected<AST::GlobalVariableDecl, Error> Parser<Lexer>::parseGlobalVariableDecl(AST::Attributes&& attributes)
+{
+ START_PARSE();
+
+ CONSUME_TYPE(KeywordVar);
+
+ std::unique_ptr<AST::VariableQualifier> maybeQualifier = nullptr;
+ if (current().m_type == TokenType::LT) {
+ PARSE(variableQualifier, VariableQualifier);
+ maybeQualifier = WTF::makeUnique<AST::VariableQualifier>(WTFMove(variableQualifier));
+ }
+
+ CONSUME_TYPE_NAMED(name, Identifier);
+
+ std::unique_ptr<AST::TypeDecl> maybeType = nullptr;
+ if (current().m_type == TokenType::Colon) {
+ consume();
+ PARSE(typeDecl, TypeDecl);
+ maybeType = typeDecl.moveToUniquePtr();
+ }
+
+ std::unique_ptr<AST::_expression_> maybeInitializer = nullptr;
+ // FIXME: initializer
+
+ RETURN_NODE(GlobalVariableDecl, name.m_ident, WTFMove(maybeQualifier), WTFMove(maybeType), WTFMove(maybeInitializer), WTFMove(attributes));
+}
+
+template<typename Lexer>
+Expected<AST::VariableQualifier, Error> Parser<Lexer>::parseVariableQualifier()
+{
+ START_PARSE();
+
+ CONSUME_TYPE(LT);
+ PARSE(storageClass, StorageClass);
+
+ // FIXME: verify that Read is the correct default in all cases.
+ AST::AccessMode accessMode = AST::AccessMode::Read;
+ if (current().m_type == TokenType::Comma) {
+ consume();
+ PARSE(actualAccessMode, AccessMode);
+ accessMode = actualAccessMode;
+ }
+
+ CONSUME_TYPE(GT);
+
+ RETURN_NODE(VariableQualifier, storageClass, accessMode);
+}
+
+template<typename Lexer>
+Expected<AST::StorageClass, Error> Parser<Lexer>::parseStorageClass()
+{
+ START_PARSE();
+
+ if (current().m_type == TokenType::KeywordFunction) {
+ consume();
+ return { AST::StorageClass::Function };
+ }
+ if (current().m_type == TokenType::KeywordPrivate) {
+ consume();
+ return { AST::StorageClass::Private };
+ }
+ if (current().m_type == TokenType::KeywordWorkgroup) {
+ consume();
+ return { AST::StorageClass::Workgroup };
+ }
+ if (current().m_type == TokenType::KeywordUniform) {
+ consume();
+ return { AST::StorageClass::Uniform };
+ }
+ if (current().m_type == TokenType::KeywordStorage) {
+ consume();
+ return { AST::StorageClass::Storage };
+ }
+
+ FAIL("Expected one of 'function'/'private'/'storage'/'uniform'/'workgroup'");
+}
+
+template<typename Lexer>
+Expected<AST::AccessMode, Error> Parser<Lexer>::parseAccessMode()
+{
+ START_PARSE();
+
+ if (current().m_type == TokenType::KeywordRead) {
+ consume();
+ return { AST::AccessMode::Read };
+ }
+ if (current().m_type == TokenType::KeywordWrite) {
+ consume();
+ return { AST::AccessMode::Write };
+ }
+ if (current().m_type == TokenType::KeywordReadWrite) {
+ consume();
+ return { AST::AccessMode::ReadWrite };
+ }
+
+ FAIL("Expected one of 'read'/'write'/'read_write'");
+}
+
+template<typename Lexer>
+Expected<AST::FunctionDecl, Error> Parser<Lexer>::parseFunctionDecl(AST::Attributes&& attributes)
+{
+ START_PARSE();
+
+ CONSUME_TYPE(KeywordFn);
+ CONSUME_TYPE_NAMED(name, Identifier);
+
+ CONSUME_TYPE(ParenLeft);
+ Vector<UniqueRef<AST::Parameter>> parameters;
+ while (current().m_type != TokenType::ParenRight) {
+ PARSE(parameter, Parameter);
+ parameters.append(makeUniqueRef<AST::Parameter>(WTFMove(parameter)));
+ }
+ CONSUME_TYPE(ParenRight);
+
+ AST::Attributes returnAttributes;
+ std::unique_ptr<AST::TypeDecl> maybeReturnType = nullptr;
+ if (current().m_type == TokenType::Arrow) {
+ consume();
+ PARSE(parsedReturnAttributes, Attributes);
+ returnAttributes = WTFMove(parsedReturnAttributes);
+ PARSE(type, TypeDecl);
+ maybeReturnType = type.moveToUniquePtr();
+ }
+
+ PARSE(body, CompoundStatement);
+
+ RETURN_NODE(FunctionDecl, name.m_ident, WTFMove(parameters), WTFMove(maybeReturnType), WTFMove(body), WTFMove(attributes), WTFMove(returnAttributes));
+}
+
+template<typename Lexer>
+Expected<AST::Parameter, Error> Parser<Lexer>::parseParameter()
+{
+ START_PARSE();
+
+ PARSE(attributes, Attributes);
+ CONSUME_TYPE_NAMED(name, Identifier)
+ CONSUME_TYPE(Colon);
+ PARSE(type, TypeDecl);
+
+ RETURN_NODE(Parameter, name.m_ident, WTFMove(type), WTFMove(attributes));
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::Statement>, Error> Parser<Lexer>::parseStatement()
+{
+ START_PARSE();
+
+ switch (current().m_type) {
+ case TokenType::BraceLeft: {
+ PARSE(compoundStmt, CompoundStatement);
+ return { makeUniqueRef<AST::CompoundStatement>(WTFMove(compoundStmt)) };
+ }
+ case TokenType::Semicolon: {
+ consume();
+ Vector<UniqueRef<AST::Statement>> statements;
+ return { makeUniqueRef<AST::CompoundStatement>(CURRENT_SOURCE_SPAN(), WTFMove(statements)) };
+ }
+ case TokenType::KeywordReturn: {
+ PARSE(returnStmt, ReturnStatement);
+ CONSUME_TYPE(Semicolon);
+ return { makeUniqueRef<AST::ReturnStatement>(WTFMove(returnStmt)) };
+ }
+ case TokenType::Identifier: {
+ // FIXME: there will be other cases here eventually for function calls
+ PARSE(lhs, LHSExpression);
+ CONSUME_TYPE(Equal);
+ PARSE(rhs, _expression_);
+ CONSUME_TYPE(Semicolon);
+ RETURN_NODE_REF(AssignmentStatement, lhs.moveToUniquePtr(), WTFMove(rhs));
+ }
+ default:
+ FAIL("Not a valid statement");
+ }
+}
+
+template<typename Lexer>
+Expected<AST::CompoundStatement, Error> Parser<Lexer>::parseCompoundStatement()
+{
+ START_PARSE();
+
+ CONSUME_TYPE(BraceLeft);
+
+ Vector<UniqueRef<AST::Statement>> statements;
+ while (current().m_type != TokenType::BraceRight) {
+ PARSE(stmt, Statement);
+ statements.append(WTFMove(stmt));
+ }
+
+ CONSUME_TYPE(BraceRight);
+
+ RETURN_NODE(CompoundStatement, WTFMove(statements));
+}
+
+template<typename Lexer>
+Expected<AST::ReturnStatement, Error> Parser<Lexer>::parseReturnStatement()
+{
+ START_PARSE();
+
+ CONSUME_TYPE(KeywordReturn);
+
+ if (current().m_type == TokenType::Semicolon) {
+ RETURN_NODE(ReturnStatement, { });
+ }
+
+ PARSE(expr, ShortCircuitOrExpression);
+ RETURN_NODE(ReturnStatement, expr.moveToUniquePtr());
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseShortCircuitOrExpression()
+{
+ // FIXME: fill in
+ return parseRelationalExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseRelationalExpression()
+{
+ // FIXME: fill in
+ return parseShiftExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseShiftExpression()
+{
+ // FIXME: fill in
+ return parseAdditiveExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseAdditiveExpression()
+{
+ // FIXME: fill in
+ return parseMultiplicativeExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseMultiplicativeExpression()
+{
+ // FIXME: fill in
+ return parseUnaryExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseUnaryExpression()
+{
+ // FIXME: fill in
+ return parseSingularExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseSingularExpression()
+{
+ START_PARSE();
+ PARSE(base, PrimaryExpression);
+ return parsePostfixExpression(WTFMove(base), _startOfElementPosition);
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parsePostfixExpression(UniqueRef<AST::_expression_>&& base, SourcePosition startPosition)
+{
+ START_PARSE();
+
+ UniqueRef<AST::_expression_> expr = WTFMove(base);
+ // FIXME: add the case for array/vector/matrix access
+ while (current().m_type == TokenType::Period) {
+ consume();
+ CONSUME_TYPE_NAMED(fieldName, Identifier);
+ SourceSpan span(startPosition, m_lexer.currentPosition());
+ expr = makeUniqueRef<AST::StructureAccess>(span, WTFMove(expr), fieldName.m_ident);
+ }
+
+ return { WTFMove(expr) };
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parsePrimaryExpression()
+{
+ START_PARSE();
+
+ switch (current().m_type) {
+ case TokenType::ParenLeft: {
+ consume();
+ PARSE(expr, _expression_);
+ CONSUME_TYPE(ParenRight);
+ return { WTFMove(expr) };
+ }
+ case TokenType::Identifier: {
+ CONSUME_TYPE_NAMED(ident, Identifier);
+ if (ident.m_ident == "true") {
+ RETURN_NODE_REF(BoolLiteral, true);
+ }
+ if (ident.m_ident == "false") {
+ RETURN_NODE_REF(BoolLiteral, false);
+ }
+ if (current().m_type == TokenType::LT || current().m_type == TokenType::ParenLeft) {
+ PARSE(type, TypeDeclAfterIdentifier, WTFMove(ident.m_ident), _startOfElementPosition);
+ PARSE(arguments, ArgumentExpressionList);
+ RETURN_NODE_REF(TypeConversion, WTFMove(type), WTFMove(arguments));
+ }
+ RETURN_NODE_REF(IdentifierExpression, ident.m_ident);
+ }
+ case TokenType::IntegerLiteralSigned: {
+ CONSUME_TYPE_NAMED(lit, IntegerLiteralSigned);
+ RETURN_NODE_REF(Int32Literal, lit.m_literalValue);
+ }
+ case TokenType::IntegerLiteralUnsigned: {
+ CONSUME_TYPE_NAMED(lit, IntegerLiteralUnsigned);
+ RETURN_NODE_REF(Uint32Literal, lit.m_literalValue);
+ }
+ case TokenType::DecimalFloatLiteral: {
+ CONSUME_TYPE_NAMED(lit, DecimalFloatLiteral);
+ RETURN_NODE_REF(Float32Literal, lit.m_literalValue);
+ }
+ // FIXME: HexFloatLiteral and IntegerLiteral
+ default:
+ break;
+ }
+ FAIL("Expected one of '(', a literal, or an identifier");
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseExpression()
+{
+ // FIXME: Fill in
+ return parseRelationalExpression();
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseLHSExpression()
+{
+ START_PARSE();
+
+ // FIXME: Add the possibility of a prefix
+ PARSE(base, CoreLHSExpression);
+ return parsePostfixExpression(WTFMove(base), _startOfElementPosition);
+}
+
+template<typename Lexer>
+Expected<UniqueRef<AST::_expression_>, Error> Parser<Lexer>::parseCoreLHSExpression()
+{
+ START_PARSE();
+
+ switch (current().m_type) {
+ case TokenType::ParenLeft: {
+ consume();
+ PARSE(expr, LHSExpression);
+ CONSUME_TYPE(ParenRight);
+ return { WTFMove(expr) };
+ }
+ case TokenType::Identifier: {
+ CONSUME_TYPE_NAMED(ident, Identifier);
+ RETURN_NODE_REF(IdentifierExpression, ident.m_ident);
+ }
+ default:
+ break;
+ }
+
+ FAIL("Tried to parse the left-hand side of an assignment and failed");
+}
+
+template<typename Lexer>
+Expected<Vector<UniqueRef<AST::_expression_>>, Error> Parser<Lexer>::parseArgumentExpressionList()
+{
+ START_PARSE();
+ CONSUME_TYPE(ParenLeft);
+
+ Vector<UniqueRef<AST::_expression_>> arguments;
+ while (current().m_type != TokenType::ParenRight) {
+ PARSE(expr, _expression_);
+ arguments.append(WTFMove(expr));
+ if (current().m_type != TokenType::ParenRight) {
+ CONSUME_TYPE(Comma);
+ }
+ }
+
+ CONSUME_TYPE(ParenRight);
+ return { WTFMove(arguments) };
+}
+
+} // namespace WGSL
Modified: trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj (291074 => 291075)
--- trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj 2022-03-09 22:16:38 UTC (rev 291074)
+++ trunk/Source/WebGPU/WebGPU.xcodeproj/project.pbxproj 2022-03-09 22:25:11 UTC (rev 291075)
@@ -48,13 +48,15 @@
338BB2D227B6B63F00E066AB /* SourceSpan.h in Headers */ = {isa = PBXBuildFile; fileRef = 338BB2D127B6B63F00E066AB /* SourceSpan.h */; };
338BB2D427B6B66C00E066AB /* Lexer.h in Headers */ = {isa = PBXBuildFile; fileRef = 338BB2D327B6B66C00E066AB /* Lexer.h */; };
338BB2D627B6B68700E066AB /* Lexer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 338BB2D527B6B68700E066AB /* Lexer.cpp */; };
- DD05A35C27BF09C60096EFAB /* libWTF.a in Product Dependencies */ = {isa = PBXBuildFile; fileRef = 1CEBD8292716CAE700A5254D /* libWTF.a */; };
+ 339B7B1827D7FFA40072BF9A /* Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 339B7B1727D7FFA40072BF9A /* Parser.h */; };
+ 339B7B1B27D800090072BF9A /* Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 339B7B1A27D800090072BF9A /* Parser.cpp */; };
+ 339B7B1D27D80A1C0072BF9A /* WGSLParserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 339B7B1C27D80A1C0072BF9A /* WGSLParserTests.mm */; };
+ 339B7B1E27D816270072BF9A /* CompilationMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33EA186727BC1B1400A1DD52 /* CompilationMessage.cpp */; };
33EA185E27BC194F00A1DD52 /* ASTNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA185D27BC194F00A1DD52 /* ASTNode.h */; };
33EA186027BC198100A1DD52 /* GlobalDirective.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA185F27BC198100A1DD52 /* GlobalDirective.h */; };
33EA186227BC19C100A1DD52 /* GlobalDecl.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186127BC19C100A1DD52 /* GlobalDecl.h */; };
33EA186427BC1A1D00A1DD52 /* GlobalVariableDecl.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186327BC1A1D00A1DD52 /* GlobalVariableDecl.h */; };
33EA186627BC1AD500A1DD52 /* CompilationMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186527BC1AD500A1DD52 /* CompilationMessage.h */; };
- 33EA186827BC1B1400A1DD52 /* CompilationMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33EA186727BC1B1400A1DD52 /* CompilationMessage.cpp */; };
33EA186A27BC1BE600A1DD52 /* Attribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186927BC1BE600A1DD52 /* Attribute.h */; };
33EA186C27BC1CBC00A1DD52 /* _expression_.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186B27BC1CBC00A1DD52 /* _expression_.h */; };
33EA186E27BC1D4C00A1DD52 /* TypeDecl.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA186D27BC1D4C00A1DD52 /* TypeDecl.h */; };
@@ -70,6 +72,7 @@
33EA188427BC268600A1DD52 /* StructureAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA188327BC268600A1DD52 /* StructureAccess.h */; };
33EA188627BC26DF00A1DD52 /* TypeConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA188527BC26DF00A1DD52 /* TypeConversion.h */; };
33EA188827BC361E00A1DD52 /* LiteralExpressions.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EA188727BC361E00A1DD52 /* LiteralExpressions.h */; };
+ DD05A35C27BF09C60096EFAB /* libWTF.a in Product Dependencies */ = {isa = PBXBuildFile; fileRef = 1CEBD8292716CAE700A5254D /* libWTF.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -201,6 +204,9 @@
338BB2D127B6B63F00E066AB /* SourceSpan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SourceSpan.h; sourceTree = "<group>"; };
338BB2D327B6B66C00E066AB /* Lexer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Lexer.h; sourceTree = "<group>"; };
338BB2D527B6B68700E066AB /* Lexer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Lexer.cpp; sourceTree = "<group>"; };
+ 339B7B1727D7FFA40072BF9A /* Parser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Parser.h; sourceTree = "<group>"; };
+ 339B7B1A27D800090072BF9A /* Parser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Parser.cpp; sourceTree = "<group>"; };
+ 339B7B1C27D80A1C0072BF9A /* WGSLParserTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WGSLParserTests.mm; sourceTree = "<group>"; };
33EA185D27BC194F00A1DD52 /* ASTNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ASTNode.h; path = WGSL/ASTNode.h; sourceTree = SOURCE_ROOT; };
33EA185F27BC198100A1DD52 /* GlobalDirective.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDirective.h; sourceTree = "<group>"; };
33EA186127BC19C100A1DD52 /* GlobalDecl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalDecl.h; sourceTree = "<group>"; };
@@ -265,6 +271,7 @@
1C023D3D27449070001DB734 /* WGSLUnitTests */ = {
isa = PBXGroup;
children = (
+ 339B7B1C27D80A1C0072BF9A /* WGSLParserTests.mm */,
1C023D3E27449070001DB734 /* WGSLLexerTests.mm */,
);
path = WGSLUnitTests;
@@ -368,11 +375,12 @@
1CEBD8042716BFAB00A5254D /* config.h */,
33EA186527BC1AD500A1DD52 /* CompilationMessage.h */,
33EA186727BC1B1400A1DD52 /* CompilationMessage.cpp */,
- 338BB2D327B6B66C00E066AB /* Lexer.h */,
338BB2D527B6B68700E066AB /* Lexer.cpp */,
338BB2D327B6B66C00E066AB /* Lexer.h */,
338BB2D127B6B63F00E066AB /* SourceSpan.h */,
338BB2CF27B6B61B00E066AB /* Token.cpp */,
+ 339B7B1727D7FFA40072BF9A /* Parser.h */,
+ 339B7B1A27D800090072BF9A /* Parser.cpp */,
338BB2CD27B6B60200E066AB /* Token.h */,
1CEBD8022716BF8200A5254D /* WGSL.cpp */,
1CEBD7F72716B34400A5254D /* WGSL.h */,
@@ -472,6 +480,7 @@
33EA185E27BC194F00A1DD52 /* ASTNode.h in Headers */,
33EA188627BC26DF00A1DD52 /* TypeConversion.h in Headers */,
33EA186427BC1A1D00A1DD52 /* GlobalVariableDecl.h in Headers */,
+ 339B7B1827D7FFA40072BF9A /* Parser.h in Headers */,
33EA187B27BC230E00A1DD52 /* CompoundStatement.h in Headers */,
33EA187627BC216B00A1DD52 /* FunctionDecl.h in Headers */,
338BB2D227B6B63F00E066AB /* SourceSpan.h in Headers */,
@@ -680,6 +689,7 @@
buildActionMask = 2147483647;
files = (
1C023D3F27449070001DB734 /* WGSLLexerTests.mm in Sources */,
+ 339B7B1D27D80A1C0072BF9A /* WGSLParserTests.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -725,6 +735,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 339B7B1E27D816270072BF9A /* CompilationMessage.cpp in Sources */,
+ 339B7B1B27D800090072BF9A /* Parser.cpp in Sources */,
338BB2D627B6B68700E066AB /* Lexer.cpp in Sources */,
338BB2D027B6B61B00E066AB /* Token.cpp in Sources */,
1CEBD8032716BF8200A5254D /* WGSL.cpp in Sources */,