diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /source/slang/slang-parser.cpp | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'source/slang/slang-parser.cpp')
| -rw-r--r-- | source/slang/slang-parser.cpp | 13751 |
1 files changed, 6866 insertions, 6885 deletions
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 0c65ca85e..d0ea9797e 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1,1820 +1,1771 @@ #include "slang-parser.h" -#include <assert.h> -#include <float.h> -#include <optional> - +#include "../core/slang-semantic-version.h" #include "slang-compiler.h" +#include "slang-lookup-spirv.h" #include "slang-lookup.h" #include "slang-visitor.h" -#include "slang-lookup-spirv.h" -#include "../core/slang-semantic-version.h" +#include <assert.h> +#include <float.h> +#include <optional> namespace Slang { - // pre-declare - static Name* getName(Parser* parser, String const& text); +// pre-declare +static Name* getName(Parser* parser, String const& text); - // Helper class useful to build a list of modifiers. - struct ModifierListBuilder +// Helper class useful to build a list of modifiers. +struct ModifierListBuilder +{ + ModifierListBuilder() { m_next = &m_result; } + void add(Modifier* modifier) { - ModifierListBuilder() - { - m_next = &m_result; - } - void add(Modifier* modifier) - { - // Doesn't handle SharedModifiers - SLANG_ASSERT(as<SharedModifiers>(modifier) == nullptr); + // Doesn't handle SharedModifiers + SLANG_ASSERT(as<SharedModifiers>(modifier) == nullptr); - // Splice at end - *m_next = modifier; - m_next = &modifier->next; - } - template <typename T> - T* find() const + // Splice at end + *m_next = modifier; + m_next = &modifier->next; + } + template<typename T> + T* find() const + { + Modifier* cur = m_result; + while (cur) { - Modifier* cur = m_result; - while (cur) + T* castCur = as<T>(cur); + if (castCur) { - T* castCur = as<T>(cur); - if (castCur) - { - return castCur; - } - cur = cur->next; + return castCur; } - return nullptr; - } - template <typename T> - bool hasType() const - { - return find<T>() != nullptr; + cur = cur->next; } - Modifier* getFirst() { return m_result; }; - protected: - - Modifier* m_result = nullptr; - Modifier** m_next; - }; + return nullptr; + } + template<typename T> + bool hasType() const + { + return find<T>() != nullptr; + } + Modifier* getFirst() { return m_result; }; - enum Precedence : int - { - Invalid = -1, - Comma, - Assignment, - TernaryConditional, - LogicalOr, - LogicalAnd, - BitOr, - BitXor, - BitAnd, - EqualityComparison, - RelationalComparison, - BitShift, - Additive, - Multiplicative, - Prefix, - Postfix, - }; +protected: + Modifier* m_result = nullptr; + Modifier** m_next; +}; - struct ParserOptions - { - bool enableEffectAnnotations = false; - bool allowGLSLInput = false; - bool isInLanguageServer = false; - CompilerOptionSet optionSet; - }; +enum Precedence : int +{ + Invalid = -1, + Comma, + Assignment, + TernaryConditional, + LogicalOr, + LogicalAnd, + BitOr, + BitXor, + BitAnd, + EqualityComparison, + RelationalComparison, + BitShift, + Additive, + Multiplicative, + Prefix, + Postfix, +}; + +struct ParserOptions +{ + bool enableEffectAnnotations = false; + bool allowGLSLInput = false; + bool isInLanguageServer = false; + CompilerOptionSet optionSet; +}; - // TODO: implement two pass parsing for file reference and struct type recognition +// TODO: implement two pass parsing for file reference and struct type recognition - class Parser - { - public: - NamePool* namePool; - SourceLanguage sourceLanguage; - ASTBuilder* astBuilder; +class Parser +{ +public: + NamePool* namePool; + SourceLanguage sourceLanguage; + ASTBuilder* astBuilder; - NamePool* getNamePool() { return namePool; } - SourceLanguage getSourceLanguage() { return sourceLanguage; } + NamePool* getNamePool() { return namePool; } + SourceLanguage getSourceLanguage() { return sourceLanguage; } - int anonymousCounter = 0; + int anonymousCounter = 0; - // Numbers of times we are peeking the same token at `ReadToken` without advancing in - // recover mode. - int sameTokenPeekedTimes = 0; + // Numbers of times we are peeking the same token at `ReadToken` without advancing in + // recover mode. + int sameTokenPeekedTimes = 0; - Scope* outerScope = nullptr; - Scope* currentLookupScope = nullptr; // Scope where expr lookup should initiate from. - Scope* currentScope = nullptr; // Scope where new decl definitions should be inserted into. - ModuleDecl* currentModule = nullptr; + Scope* outerScope = nullptr; + Scope* currentLookupScope = nullptr; // Scope where expr lookup should initiate from. + Scope* currentScope = nullptr; // Scope where new decl definitions should be inserted into. + ModuleDecl* currentModule = nullptr; - bool hasSeenCompletionToken = false; + bool hasSeenCompletionToken = false; - // Track whether or not we are inside a generics that has variadic parameters. - // If so we will enable the new `expand` and `each` keyword. - bool isInVariadicGenerics = false; + // Track whether or not we are inside a generics that has variadic parameters. + // If so we will enable the new `expand` and `each` keyword. + bool isInVariadicGenerics = false; - TokenReader tokenReader; - DiagnosticSink* sink; - SourceLoc lastErrorLoc; - ParserOptions options; - Modifiers* pendingModifiers = nullptr; - int genericDepth = 0; + TokenReader tokenReader; + DiagnosticSink* sink; + SourceLoc lastErrorLoc; + ParserOptions options; + Modifiers* pendingModifiers = nullptr; + int genericDepth = 0; - // Is the parser in a "recovering" state? - // During recovery we don't emit additional errors, until we find - // a token that we expected, when we exit recovery. - bool isRecovering = false; + // Is the parser in a "recovering" state? + // During recovery we don't emit additional errors, until we find + // a token that we expected, when we exit recovery. + bool isRecovering = false; - void FillPosition(SyntaxNode * node) - { - node->loc = tokenReader.peekLoc(); - } + void FillPosition(SyntaxNode* node) { node->loc = tokenReader.peekLoc(); } - void resetLookupScope() - { - currentLookupScope = currentScope; - } + void resetLookupScope() { currentLookupScope = currentScope; } - void PushScope(ContainerDecl* containerDecl) - { - Scope* newScope = astBuilder->create<Scope>(); - newScope->containerDecl = containerDecl; - newScope->parent = currentScope; - currentScope = newScope; - containerDecl->ownedScope = newScope; - resetLookupScope(); - } + void PushScope(ContainerDecl* containerDecl) + { + Scope* newScope = astBuilder->create<Scope>(); + newScope->containerDecl = containerDecl; + newScope->parent = currentScope; + currentScope = newScope; + containerDecl->ownedScope = newScope; + resetLookupScope(); + } - void PushScope(Scope* newScope) - { - currentScope = newScope; - resetLookupScope(); - } + void PushScope(Scope* newScope) + { + currentScope = newScope; + resetLookupScope(); + } - void pushScopeAndSetParent(ContainerDecl* containerDecl) - { - containerDecl->parentDecl = currentScope->containerDecl; - PushScope(containerDecl); - } + void pushScopeAndSetParent(ContainerDecl* containerDecl) + { + containerDecl->parentDecl = currentScope->containerDecl; + PushScope(containerDecl); + } - void PopScope() - { - currentScope = currentScope->parent; - resetLookupScope(); - } + void PopScope() + { + currentScope = currentScope->parent; + resetLookupScope(); + } - ModuleDecl* getCurrentModuleDecl() - { - return currentModule; - } + ModuleDecl* getCurrentModuleDecl() { return currentModule; } - Parser( - ASTBuilder* inAstBuilder, - TokenSpan const& _tokens, - DiagnosticSink * sink, - Scope* outerScope, - ParserOptions inOptions) - : tokenReader(_tokens) - , astBuilder(inAstBuilder) - , sink(sink) - , outerScope(outerScope) - , options(inOptions) - {} - Parser(const Parser & other) = default; + Parser( + ASTBuilder* inAstBuilder, + TokenSpan const& _tokens, + DiagnosticSink* sink, + Scope* outerScope, + ParserOptions inOptions) + : tokenReader(_tokens) + , astBuilder(inAstBuilder) + , sink(sink) + , outerScope(outerScope) + , options(inOptions) + { + } + Parser(const Parser& other) = default; - //Session* getSession() { return m_session; } + // Session* getSession() { return m_session; } - Token ReadToken(); - Token readTokenImpl(TokenType type, bool forceSkippingToClosingToken); - Token ReadToken(TokenType type); - // Same as `ReadToken`, but force skip to the matching closing token on error. - Token ReadMatchingToken(TokenType type); - Token ReadToken(const char* string); + Token ReadToken(); + Token readTokenImpl(TokenType type, bool forceSkippingToClosingToken); + Token ReadToken(TokenType type); + // Same as `ReadToken`, but force skip to the matching closing token on error. + Token ReadMatchingToken(TokenType type); + Token ReadToken(const char* string); - bool LookAheadToken(TokenType type); - bool LookAheadToken(const char* string); + bool LookAheadToken(TokenType type); + bool LookAheadToken(const char* string); - bool LookAheadToken(TokenType type, int offset); - bool LookAheadToken(const char* string, int offset); + bool LookAheadToken(TokenType type, int offset); + bool LookAheadToken(const char* string, int offset); - void parseSourceFile(ContainerDecl* parentDecl); - Decl* ParseStruct(); - ClassDecl* ParseClass(); - Decl* ParseGLSLInterfaceBlock(); - Stmt* ParseStatement(Stmt* parentStmt = nullptr); - Stmt* parseBlockStatement(); - Stmt* parseLabelStatement(); - DeclStmt* parseVarDeclrStatement(Modifiers modifiers); - IfStmt* parseIfStatement(); - Stmt* parseIfLetStatement(); - ForStmt* ParseForStatement(); - WhileStmt* ParseWhileStatement(); - DoWhileStmt* ParseDoWhileStatement(); - BreakStmt* ParseBreakStatement(); - ContinueStmt* ParseContinueStatement(); - ReturnStmt* ParseReturnStatement(); - ExpressionStmt* ParseExpressionStatement(); - Expr* ParseExpression(Precedence level = Precedence::Comma); + void parseSourceFile(ContainerDecl* parentDecl); + Decl* ParseStruct(); + ClassDecl* ParseClass(); + Decl* ParseGLSLInterfaceBlock(); + Stmt* ParseStatement(Stmt* parentStmt = nullptr); + Stmt* parseBlockStatement(); + Stmt* parseLabelStatement(); + DeclStmt* parseVarDeclrStatement(Modifiers modifiers); + IfStmt* parseIfStatement(); + Stmt* parseIfLetStatement(); + ForStmt* ParseForStatement(); + WhileStmt* ParseWhileStatement(); + DoWhileStmt* ParseDoWhileStatement(); + BreakStmt* ParseBreakStatement(); + ContinueStmt* ParseContinueStatement(); + ReturnStmt* ParseReturnStatement(); + ExpressionStmt* ParseExpressionStatement(); + Expr* ParseExpression(Precedence level = Precedence::Comma); - // Parse an expression that might be used in an initializer or argument context, so we should avoid operator-comma - inline Expr* ParseInitExpr() { return ParseExpression(Precedence::Assignment); } - inline Expr* ParseArgExpr() { return ParseExpression(Precedence::Assignment); } + // Parse an expression that might be used in an initializer or argument context, so we should + // avoid operator-comma + inline Expr* ParseInitExpr() { return ParseExpression(Precedence::Assignment); } + inline Expr* ParseArgExpr() { return ParseExpression(Precedence::Assignment); } - Expr* ParseLeafExpression(); - ParamDecl* ParseParameter(); - Expr* ParseType(); - TypeExp ParseTypeExp(); + Expr* ParseLeafExpression(); + ParamDecl* ParseParameter(); + Expr* ParseType(); + TypeExp ParseTypeExp(); - Parser & operator = (const Parser &) = delete; + Parser& operator=(const Parser&) = delete; - // Helper to issue diagnose message that filters out errors for the same token. - template <typename P, typename... Args> - void diagnose(P const& pos, DiagnosticInfo const& info, Args const&... args) + // Helper to issue diagnose message that filters out errors for the same token. + template<typename P, typename... Args> + void diagnose(P const& pos, DiagnosticInfo const& info, Args const&... args) + { + auto loc = getDiagnosticPos(pos); + if (loc != lastErrorLoc) { - auto loc = getDiagnosticPos(pos); - if (loc != lastErrorLoc) - { - sink->diagnose(pos, info, args...); - lastErrorLoc = loc; - } + sink->diagnose(pos, info, args...); + lastErrorLoc = loc; } + } - public: - void setBindingOffset(int binding, int64_t byteOffset) - { - bindingToByteOffset.set(binding, byteOffset); - } - int64_t getNextBindingOffset(int binding) - { - int64_t currentOffset; - if (bindingToByteOffset.addIfNotExists(binding, 0)) - currentOffset = 0; - else - currentOffset = bindingToByteOffset.getValue(binding) + sizeof(uint32_t); - - bindingToByteOffset.set( - binding, - currentOffset + sizeof(uint32_t) - ); - return currentOffset; - } +public: + void setBindingOffset(int binding, int64_t byteOffset) + { + bindingToByteOffset.set(binding, byteOffset); + } + int64_t getNextBindingOffset(int binding) + { + int64_t currentOffset; + if (bindingToByteOffset.addIfNotExists(binding, 0)) + currentOffset = 0; + else + currentOffset = bindingToByteOffset.getValue(binding) + sizeof(uint32_t); - private: - Dictionary<int, int64_t> bindingToByteOffset; - }; + bindingToByteOffset.set(binding, currentOffset + sizeof(uint32_t)); + return currentOffset; + } - // Forward Declarations +private: + Dictionary<int, int64_t> bindingToByteOffset; +}; - enum class MatchedTokenType - { - Parentheses, - SquareBrackets, - CurlyBraces, - File, - }; +// Forward Declarations - /// Parse declarations making up the body of `parent`, up to a matching token for `matchType` - static void parseDecls( - Parser* parser, - ContainerDecl* parent, - MatchedTokenType matchType); +enum class MatchedTokenType +{ + Parentheses, + SquareBrackets, + CurlyBraces, + File, +}; - /// Parse a body consisting of declarations enclosed in `{}`, as the children of `parent`. - static void parseDeclBody( - Parser* parser, - ContainerDecl* parent); +/// Parse declarations making up the body of `parent`, up to a matching token for `matchType` +static void parseDecls(Parser* parser, ContainerDecl* parent, MatchedTokenType matchType); - static Decl* parseEnumDecl(Parser* parser); +/// Parse a body consisting of declarations enclosed in `{}`, as the children of `parent`. +static void parseDeclBody(Parser* parser, ContainerDecl* parent); - static Modifiers _parseOptSemantics( - Parser* parser); +static Decl* parseEnumDecl(Parser* parser); - static void _parseOptSemantics( - Parser* parser, - Decl* decl); +static Modifiers _parseOptSemantics(Parser* parser); - static DeclBase* ParseDecl( - Parser* parser, - ContainerDecl* containerDecl); +static void _parseOptSemantics(Parser* parser, Decl* decl); - static Decl* ParseSingleDecl( - Parser* parser, - ContainerDecl* containerDecl); +static DeclBase* ParseDecl(Parser* parser, ContainerDecl* containerDecl); - static void parseModernParamList( - Parser* parser, - CallableDecl* decl); +static Decl* ParseSingleDecl(Parser* parser, ContainerDecl* containerDecl); - static TokenType peekTokenType(Parser* parser); +static void parseModernParamList(Parser* parser, CallableDecl* decl); - static Expr* _parseGenericArg(Parser* parser); +static TokenType peekTokenType(Parser* parser); - static Expr* parsePrefixExpr(Parser* parser); +static Expr* _parseGenericArg(Parser* parser); - // +static Expr* parsePrefixExpr(Parser* parser); - static void Unexpected( - Parser* parser) +// + +static void Unexpected(Parser* parser) +{ + // Don't emit "unexpected token" errors if we are in recovering mode + if (!parser->isRecovering) { - // Don't emit "unexpected token" errors if we are in recovering mode - if (!parser->isRecovering) - { - parser->diagnose(parser->tokenReader.peekLoc(), Diagnostics::unexpectedToken, - parser->tokenReader.peekTokenType()); + parser->diagnose( + parser->tokenReader.peekLoc(), + Diagnostics::unexpectedToken, + parser->tokenReader.peekTokenType()); - // Switch into recovery mode, to suppress additional errors - parser->isRecovering = true; - } + // Switch into recovery mode, to suppress additional errors + parser->isRecovering = true; } +} - static void Unexpected( - Parser* parser, - char const* expected) +static void Unexpected(Parser* parser, char const* expected) +{ + // Don't emit "unexpected token" errors if we are in recovering mode + if (!parser->isRecovering) { - // Don't emit "unexpected token" errors if we are in recovering mode - if (!parser->isRecovering) - { - parser->sink->diagnose(parser->tokenReader.peekLoc(), Diagnostics::unexpectedTokenExpectedTokenName, - parser->tokenReader.peekTokenType(), - expected); + parser->sink->diagnose( + parser->tokenReader.peekLoc(), + Diagnostics::unexpectedTokenExpectedTokenName, + parser->tokenReader.peekTokenType(), + expected); - // Switch into recovery mode, to suppress additional errors - parser->isRecovering = true; - } + // Switch into recovery mode, to suppress additional errors + parser->isRecovering = true; } +} - static void Unexpected( - Parser* parser, - TokenType expected) +static void Unexpected(Parser* parser, TokenType expected) +{ + // Don't emit "unexpected token" errors if we are in recovering mode + if (!parser->isRecovering) { - // Don't emit "unexpected token" errors if we are in recovering mode - if (!parser->isRecovering) + if (parser->lastErrorLoc != parser->tokenReader.peekLoc()) { - if (parser->lastErrorLoc != parser->tokenReader.peekLoc()) - { - parser->sink->diagnose( - parser->tokenReader.peekLoc(), - Diagnostics::unexpectedTokenExpectedTokenType, - parser->tokenReader.peekTokenType(), - expected); - parser->lastErrorLoc = parser->tokenReader.peekLoc(); - } - // Switch into recovery mode, to suppress additional errors - parser->isRecovering = true; + parser->sink->diagnose( + parser->tokenReader.peekLoc(), + Diagnostics::unexpectedTokenExpectedTokenType, + parser->tokenReader.peekTokenType(), + expected); + parser->lastErrorLoc = parser->tokenReader.peekLoc(); } + // Switch into recovery mode, to suppress additional errors + parser->isRecovering = true; } +} - static TokenType SkipToMatchingToken(TokenReader* reader, TokenType tokenType); +static TokenType SkipToMatchingToken(TokenReader* reader, TokenType tokenType); - // Skip a singel balanced token, which is either a single token in - // the common case, or a matched pair of tokens for `()`, `[]`, and `{}` - static TokenType SkipBalancedToken( - TokenReader* reader) +// Skip a singel balanced token, which is either a single token in +// the common case, or a matched pair of tokens for `()`, `[]`, and `{}` +static TokenType SkipBalancedToken(TokenReader* reader) +{ + TokenType tokenType = reader->advanceToken().type; + switch (tokenType) { - TokenType tokenType = reader->advanceToken().type; - switch (tokenType) - { - default: - break; + default: break; - case TokenType::LParent: tokenType = SkipToMatchingToken(reader, TokenType::RParent); break; - case TokenType::LBrace: tokenType = SkipToMatchingToken(reader, TokenType::RBrace); break; - case TokenType::LBracket: tokenType = SkipToMatchingToken(reader, TokenType::RBracket); break; - } - return tokenType; + case TokenType::LParent: tokenType = SkipToMatchingToken(reader, TokenType::RParent); break; + case TokenType::LBrace: tokenType = SkipToMatchingToken(reader, TokenType::RBrace); break; + case TokenType::LBracket: tokenType = SkipToMatchingToken(reader, TokenType::RBracket); break; } + return tokenType; +} - // Skip balanced - static TokenType SkipToMatchingToken( - TokenReader* reader, - TokenType tokenType) +// Skip balanced +static TokenType SkipToMatchingToken(TokenReader* reader, TokenType tokenType) +{ + for (;;) { - for (;;) + if (reader->isAtEnd()) + return TokenType::EndOfFile; + if (reader->peekTokenType() == tokenType) { - if (reader->isAtEnd()) return TokenType::EndOfFile; - if (reader->peekTokenType() == tokenType) - { - reader->advanceToken(); - return tokenType; - } - SkipBalancedToken(reader); + reader->advanceToken(); + return tokenType; } + SkipBalancedToken(reader); } +} - // Is the given token type one that is used to "close" a - // balanced construct. - static bool IsClosingToken(TokenType tokenType) +// Is the given token type one that is used to "close" a +// balanced construct. +static bool IsClosingToken(TokenType tokenType) +{ + switch (tokenType) { - switch (tokenType) - { - case TokenType::EndOfFile: - case TokenType::RBracket: - case TokenType::RParent: - case TokenType::RBrace: - return true; + case TokenType::EndOfFile: + case TokenType::RBracket: + case TokenType::RParent: + case TokenType::RBrace: return true; - default: - return false; - } + default: return false; } +} - // Expect an identifier token with the given content, and consume it. - Token Parser::ReadToken(const char* expected) +// Expect an identifier token with the given content, and consume it. +Token Parser::ReadToken(const char* expected) +{ + if (tokenReader.peekTokenType() == TokenType::Identifier && + tokenReader.peekToken().getContent() == expected) { - if (tokenReader.peekTokenType() == TokenType::Identifier - && tokenReader.peekToken().getContent() == expected) - { - isRecovering = false; - return tokenReader.advanceToken(); - } + isRecovering = false; + return tokenReader.advanceToken(); + } - if (!isRecovering) - { - Unexpected(this, expected); - return tokenReader.peekToken(); - } - else + if (!isRecovering) + { + Unexpected(this, expected); + return tokenReader.peekToken(); + } + else + { + // Try to find a place to recover + for (;;) { - // Try to find a place to recover - for (;;) + // The token we expected? + // Then exit recovery mode and pretend like all is well. + if (tokenReader.peekTokenType() == TokenType::Identifier && + tokenReader.peekToken().getContent() == expected) { - // The token we expected? - // Then exit recovery mode and pretend like all is well. - if (tokenReader.peekTokenType() == TokenType::Identifier - && tokenReader.peekToken().getContent() == expected) - { - isRecovering = false; - return tokenReader.advanceToken(); - } - + isRecovering = false; + return tokenReader.advanceToken(); + } - // Don't skip past any "closing" tokens. - if (IsClosingToken(tokenReader.peekTokenType())) - { - return tokenReader.peekToken(); - } - // Skip balanced tokens and try again. - SkipBalancedToken(&tokenReader); + // Don't skip past any "closing" tokens. + if (IsClosingToken(tokenReader.peekTokenType())) + { + return tokenReader.peekToken(); } + + // Skip balanced tokens and try again. + SkipBalancedToken(&tokenReader); } } +} + +Token Parser::ReadToken() +{ + return tokenReader.advanceToken(); +} + +static bool TryRecover( + Parser* parser, + TokenType const* recoverBefore, + int recoverBeforeCount, + TokenType const* recoverAfter, + int recoverAfterCount) +{ + if (!parser->isRecovering) + return true; + + // Determine if we are looking for common closing tokens, + // so that we can know whether or we are allowed to skip + // over them. - Token Parser::ReadToken() + bool lookingForEOF = false; + bool lookingForRCurly = false; + bool lookingForRParen = false; + bool lookingForRSquare = false; + + for (int ii = 0; ii < recoverBeforeCount; ++ii) { - return tokenReader.advanceToken(); - } + switch (recoverBefore[ii]) + { + default: break; - static bool TryRecover( - Parser* parser, - TokenType const* recoverBefore, - int recoverBeforeCount, - TokenType const* recoverAfter, - int recoverAfterCount) + case TokenType::EndOfFile: lookingForEOF = true; break; + case TokenType::RBrace: lookingForRCurly = true; break; + case TokenType::RParent: lookingForRParen = true; break; + case TokenType::RBracket: lookingForRSquare = true; break; + } + } + for (int ii = 0; ii < recoverAfterCount; ++ii) { - if (!parser->isRecovering) - return true; + switch (recoverAfter[ii]) + { + default: break; - // Determine if we are looking for common closing tokens, - // so that we can know whether or we are allowed to skip - // over them. + case TokenType::EndOfFile: lookingForEOF = true; break; + case TokenType::RBrace: lookingForRCurly = true; break; + case TokenType::RParent: lookingForRParen = true; break; + case TokenType::RBracket: lookingForRSquare = true; break; + } + } - bool lookingForEOF = false; - bool lookingForRCurly = false; - bool lookingForRParen = false; - bool lookingForRSquare = false; + TokenReader* tokenReader = &parser->tokenReader; + for (;;) + { + TokenType peek = tokenReader->peekTokenType(); + // Is the next token in our recover-before set? + // If so, then we have recovered successfully! for (int ii = 0; ii < recoverBeforeCount; ++ii) { - switch (recoverBefore[ii]) + if (peek == recoverBefore[ii]) { - default: - break; - - case TokenType::EndOfFile: lookingForEOF = true; break; - case TokenType::RBrace: lookingForRCurly = true; break; - case TokenType::RParent: lookingForRParen = true; break; - case TokenType::RBracket: lookingForRSquare = true; break; + parser->isRecovering = false; + return true; } } + + // If we are looking at a token in our recover-after set, + // then consume it and recover for (int ii = 0; ii < recoverAfterCount; ++ii) { - switch (recoverAfter[ii]) + if (peek == recoverAfter[ii]) { - default: - break; - - case TokenType::EndOfFile: lookingForEOF = true; break; - case TokenType::RBrace: lookingForRCurly = true; break; - case TokenType::RParent: lookingForRParen = true; break; - case TokenType::RBracket: lookingForRSquare = true; break; + tokenReader->advanceToken(); + parser->isRecovering = false; + return true; } } - TokenReader* tokenReader = &parser->tokenReader; - for (;;) - { - TokenType peek = tokenReader->peekTokenType(); + // Don't try to skip past end of file + if (peek == TokenType::EndOfFile) + return false; - // Is the next token in our recover-before set? - // If so, then we have recovered successfully! - for (int ii = 0; ii < recoverBeforeCount; ++ii) + switch (peek) + { + // Don't skip past simple "closing" tokens, *unless* + // we are looking for a closing token + case TokenType::RParent: + case TokenType::RBracket: + if (lookingForRParen || lookingForRSquare || lookingForRCurly || lookingForEOF) { - if (peek == recoverBefore[ii]) - { - parser->isRecovering = false; - return true; - } + // We are looking for a closing token, so it is okay to skip these } + else + return false; + break; - // If we are looking at a token in our recover-after set, - // then consume it and recover - for (int ii = 0; ii < recoverAfterCount; ++ii) + // Don't skip a `}`, to avoid spurious errors, + // with the exception of when we are looking for EOF + case TokenType::RBrace: + if (lookingForRCurly || lookingForEOF) { - if (peek == recoverAfter[ii]) - { - tokenReader->advanceToken(); - parser->isRecovering = false; - return true; - } + // We are looking for end-of-file, so it is okay to skip here } - - // Don't try to skip past end of file - if (peek == TokenType::EndOfFile) - return false; - - switch (peek) + else { - // Don't skip past simple "closing" tokens, *unless* - // we are looking for a closing token - case TokenType::RParent: - case TokenType::RBracket: - if (lookingForRParen || lookingForRSquare || lookingForRCurly || lookingForEOF) - { - // We are looking for a closing token, so it is okay to skip these - } - else - return false; - break; - - // Don't skip a `}`, to avoid spurious errors, - // with the exception of when we are looking for EOF - case TokenType::RBrace: - if (lookingForRCurly || lookingForEOF) - { - // We are looking for end-of-file, so it is okay to skip here - } - else - { - return false; - } + return false; } + } - // Skip balanced tokens and try again. - TokenType skipped = SkipBalancedToken(tokenReader); + // Skip balanced tokens and try again. + TokenType skipped = SkipBalancedToken(tokenReader); - // If we happened to find a matched pair of tokens, and - // the end of it was a token we were looking for, - // then recover here - for (int ii = 0; ii < recoverAfterCount; ++ii) + // If we happened to find a matched pair of tokens, and + // the end of it was a token we were looking for, + // then recover here + for (int ii = 0; ii < recoverAfterCount; ++ii) + { + if (skipped == recoverAfter[ii]) { - if (skipped == recoverAfter[ii]) - { - parser->isRecovering = false; - return true; - } + parser->isRecovering = false; + return true; } } } +} - static bool TryRecoverBefore( - Parser* parser, - TokenType before0) - { - TokenType recoverBefore[] = { before0 }; - return TryRecover(parser, recoverBefore, 1, nullptr, 0); - } +static bool TryRecoverBefore(Parser* parser, TokenType before0) +{ + TokenType recoverBefore[] = {before0}; + return TryRecover(parser, recoverBefore, 1, nullptr, 0); +} - // Default recovery strategy, to use inside `{}`-delimeted blocks. - static bool TryRecover( - Parser* parser) +// Default recovery strategy, to use inside `{}`-delimeted blocks. +static bool TryRecover(Parser* parser) +{ + TokenType recoverBefore[] = {TokenType::RBrace}; + TokenType recoverAfter[] = {TokenType::Semicolon}; + return TryRecover(parser, recoverBefore, 1, recoverAfter, 1); +} + +Token Parser::readTokenImpl(TokenType expected, bool forceSkippingToClosingToken) +{ + if (tokenReader.peekTokenType() == expected) { - TokenType recoverBefore[] = { TokenType::RBrace }; - TokenType recoverAfter[] = { TokenType::Semicolon }; - return TryRecover(parser, recoverBefore, 1, recoverAfter, 1); + isRecovering = false; + sameTokenPeekedTimes = 0; + return tokenReader.advanceToken(); } - Token Parser::readTokenImpl(TokenType expected, bool forceSkippingToClosingToken) + if (!isRecovering) { - if (tokenReader.peekTokenType() == expected) - { - isRecovering = false; - sameTokenPeekedTimes = 0; - return tokenReader.advanceToken(); - } - - if (!isRecovering) - { - Unexpected(this, expected); - if (!forceSkippingToClosingToken) - return tokenReader.peekToken(); - switch (expected) - { - case TokenType::RBrace: - case TokenType::RParent: - case TokenType::RBracket: - break; - default: - return tokenReader.peekToken(); - } - } - - // Try to find a place to recover - if (TryRecoverBefore(this, expected)) - { - isRecovering = false; - return tokenReader.advanceToken(); - } - // This could be dangerous: if `ReadToken()` is being called - // in a loop we may never make forward progress, so we use - // a counter to limit the maximum amount of times we are allowed - // to peek the same token. If the outter parsing logic is - // correct, we will pop back to the right level. If there are - // erroneous parsing logic, this counter is to prevent us - // looping infinitely. - static const int kMaxTokenPeekCount = 64; - sameTokenPeekedTimes++; - if (sameTokenPeekedTimes < kMaxTokenPeekCount) + Unexpected(this, expected); + if (!forceSkippingToClosingToken) return tokenReader.peekToken(); - else + switch (expected) { - sameTokenPeekedTimes = 0; - return tokenReader.advanceToken(); + case TokenType::RBrace: + case TokenType::RParent: + case TokenType::RBracket: break; + default: return tokenReader.peekToken(); } } - Token Parser::ReadToken(TokenType expected) + // Try to find a place to recover + if (TryRecoverBefore(this, expected)) { - return readTokenImpl(expected, false); + isRecovering = false; + return tokenReader.advanceToken(); } - - Token Parser::ReadMatchingToken(TokenType expected) - { - return readTokenImpl(expected, true); + // This could be dangerous: if `ReadToken()` is being called + // in a loop we may never make forward progress, so we use + // a counter to limit the maximum amount of times we are allowed + // to peek the same token. If the outter parsing logic is + // correct, we will pop back to the right level. If there are + // erroneous parsing logic, this counter is to prevent us + // looping infinitely. + static const int kMaxTokenPeekCount = 64; + sameTokenPeekedTimes++; + if (sameTokenPeekedTimes < kMaxTokenPeekCount) + return tokenReader.peekToken(); + else + { + sameTokenPeekedTimes = 0; + return tokenReader.advanceToken(); } +} - bool Parser::LookAheadToken(const char* string, int offset) - { - TokenReader r = tokenReader; - for (int ii = 0; ii < offset; ++ii) - r.advanceToken(); +Token Parser::ReadToken(TokenType expected) +{ + return readTokenImpl(expected, false); +} - return r.peekTokenType() == TokenType::Identifier - && r.peekToken().getContent() == string; +Token Parser::ReadMatchingToken(TokenType expected) +{ + return readTokenImpl(expected, true); } - bool Parser::LookAheadToken(TokenType type, int offset) - { - TokenReader r = tokenReader; - for (int ii = 0; ii < offset; ++ii) - r.advanceToken(); +bool Parser::LookAheadToken(const char* string, int offset) +{ + TokenReader r = tokenReader; + for (int ii = 0; ii < offset; ++ii) + r.advanceToken(); - return r.peekTokenType() == type; - } + return r.peekTokenType() == TokenType::Identifier && r.peekToken().getContent() == string; +} - bool Parser::LookAheadToken(TokenType type) - { - return tokenReader.peekTokenType() == type; - } +bool Parser::LookAheadToken(TokenType type, int offset) +{ + TokenReader r = tokenReader; + for (int ii = 0; ii < offset; ++ii) + r.advanceToken(); - bool Parser::LookAheadToken(const char* string) - { - const auto& token = tokenReader.peekToken(); - return token.type == TokenType::Identifier && token.getContent() == string; - } + return r.peekTokenType() == type; +} - // Consume a token and return true it if matches, otherwise false - bool AdvanceIf(Parser* parser, TokenType tokenType) - { - if (parser->LookAheadToken(tokenType)) - { - parser->ReadToken(); - return true; - } - return false; - } +bool Parser::LookAheadToken(TokenType type) +{ + return tokenReader.peekTokenType() == type; +} - bool AdvanceIf(Parser* parser, TokenType tokenType, Token* outToken) +bool Parser::LookAheadToken(const char* string) +{ + const auto& token = tokenReader.peekToken(); + return token.type == TokenType::Identifier && token.getContent() == string; +} + +// Consume a token and return true it if matches, otherwise false +bool AdvanceIf(Parser* parser, TokenType tokenType) +{ + if (parser->LookAheadToken(tokenType)) { - if (parser->LookAheadToken(tokenType)) - { - *outToken = parser->ReadToken(); - return true; - } - return false; + parser->ReadToken(); + return true; } + return false; +} - // Consume a token and return true it if matches, otherwise false - bool AdvanceIf(Parser* parser, char const* text) +bool AdvanceIf(Parser* parser, TokenType tokenType, Token* outToken) +{ + if (parser->LookAheadToken(tokenType)) { - if (parser->LookAheadToken(text)) - { - parser->ReadToken(); - return true; - } - return false; + *outToken = parser->ReadToken(); + return true; } + return false; +} - bool AdvanceIf(Parser* parser, char const* text, Token* outToken) +// Consume a token and return true it if matches, otherwise false +bool AdvanceIf(Parser* parser, char const* text) +{ + if (parser->LookAheadToken(text)) { - if (parser->LookAheadToken(text)) - { - *outToken = parser->ReadToken(); - return true; - } - return false; + parser->ReadToken(); + return true; } + return false; +} - /// Information on how to parse certain pairs of matches tokens - struct MatchedTokenInfo +bool AdvanceIf(Parser* parser, char const* text, Token* outToken) +{ + if (parser->LookAheadToken(text)) { - /// The token type that opens the pair - TokenType openTokenType; + *outToken = parser->ReadToken(); + return true; + } + return false; +} - /// The token type that closes the pair - TokenType closeTokenType; +/// Information on how to parse certain pairs of matches tokens +struct MatchedTokenInfo +{ + /// The token type that opens the pair + TokenType openTokenType; + + /// The token type that closes the pair + TokenType closeTokenType; + + /// A list of token types that should lead the parser + /// to abandon its search for a matchign closing token + /// (terminated by `TokenType::EndOfFile`). + TokenType const* bailAtCloseTokens; +}; +static const TokenType kMatchedToken_BailAtEOF[] = {TokenType::EndOfFile}; +static const TokenType kMatchedToken_BailAtCurlyBraceOrEOF[] = { + TokenType::RBrace, + TokenType::EndOfFile}; +static const MatchedTokenInfo kMatchedTokenInfos[] = { + {TokenType::LParent, TokenType::RParent, kMatchedToken_BailAtCurlyBraceOrEOF}, + {TokenType::LBracket, TokenType::RBracket, kMatchedToken_BailAtCurlyBraceOrEOF}, + {TokenType::LBrace, TokenType::RBrace, kMatchedToken_BailAtEOF}, + {TokenType::Unknown, TokenType::EndOfFile, kMatchedToken_BailAtEOF}, +}; + +/// Expect to enter a matched region starting with `tokenType` +/// +/// Returns `true` on a match and `false` if a region is not entered. +bool beginMatch(Parser* parser, MatchedTokenType type) +{ + auto& info = kMatchedTokenInfos[int(type)]; + bool result = peekTokenType(parser) == info.openTokenType; + parser->ReadToken(info.openTokenType); + return result; +} - /// A list of token types that should lead the parser - /// to abandon its search for a matchign closing token - /// (terminated by `TokenType::EndOfFile`). - TokenType const* bailAtCloseTokens; - }; - static const TokenType kMatchedToken_BailAtEOF[] = { TokenType::EndOfFile }; - static const TokenType kMatchedToken_BailAtCurlyBraceOrEOF[] = { TokenType::RBrace, TokenType::EndOfFile }; - static const MatchedTokenInfo kMatchedTokenInfos[] = - { - { TokenType::LParent, TokenType::RParent, kMatchedToken_BailAtCurlyBraceOrEOF }, - { TokenType::LBracket, TokenType::RBracket, kMatchedToken_BailAtCurlyBraceOrEOF }, - { TokenType::LBrace, TokenType::RBrace, kMatchedToken_BailAtEOF }, - { TokenType::Unknown, TokenType::EndOfFile, kMatchedToken_BailAtEOF }, - }; +// Consume a token and return true if it matches, otherwise check +// for end-of-file and expect that token (potentially producing +// an error) and return true to maintain forward progress. +// Otherwise return false. +bool AdvanceIfMatch(Parser* parser, MatchedTokenType type, Token* outToken) +{ + // The behavior of the seatch for a match can depend on the + // type of matches tokens we are parsing. + // + auto& info = kMatchedTokenInfos[int(type)]; - /// Expect to enter a matched region starting with `tokenType` - /// - /// Returns `true` on a match and `false` if a region is not entered. - bool beginMatch(Parser* parser, MatchedTokenType type) + // First, if the parser is already in a state where it is recovering + // from an earlier syntax error, we want to give it a fighting chance + // to recover here, because we know a token type we are looking for. + // + // Basically, if the parser can skip ahead some number of tokens to + // find a token of the correct type to close this matched list, then + // we would like to do so. + // + // Note: this behavior does not mean that any syntax error in a list + // will automatically skip the remainder of the list. The reason is + // that most syntax lists have a separate or terminator (e.g., a + // comma or semicolon), and reading in a separator will also serve + // to recover the parser. The case here is only going to come up + // when the lookahead for a separator/terminator already failed. + // + if (parser->isRecovering) { - auto& info = kMatchedTokenInfos[int(type)]; - bool result = peekTokenType(parser) == info.openTokenType; - parser->ReadToken(info.openTokenType); - return result; + TryRecoverBefore(parser, info.closeTokenType); } - // Consume a token and return true if it matches, otherwise check - // for end-of-file and expect that token (potentially producing - // an error) and return true to maintain forward progress. - // Otherwise return false. - bool AdvanceIfMatch(Parser* parser, MatchedTokenType type, Token* outToken) - { - // The behavior of the seatch for a match can depend on the - // type of matches tokens we are parsing. - // - auto& info = kMatchedTokenInfos[int(type)]; + // If the result of our recovery effort is that we are looking + // at the token type we wanted, we can consume it and return, + // with the parser happily recovered. + // + if (AdvanceIf(parser, info.closeTokenType, outToken)) + return true; - // First, if the parser is already in a state where it is recovering - // from an earlier syntax error, we want to give it a fighting chance - // to recover here, because we know a token type we are looking for. - // - // Basically, if the parser can skip ahead some number of tokens to - // find a token of the correct type to close this matched list, then - // we would like to do so. - // - // Note: this behavior does not mean that any syntax error in a list - // will automatically skip the remainder of the list. The reason is - // that most syntax lists have a separate or terminator (e.g., a - // comma or semicolon), and reading in a separator will also serve - // to recover the parser. The case here is only going to come up - // when the lookahead for a separator/terminator already failed. - // - if (parser->isRecovering) + // Otherwise, we know that we haven't yet recovered. + // The challenge here is that `AdvanceIfMatch()` is almost always + // called in a loop, and we need that loop to terminate at + // some point. + // + // Each of the types of matched tokens is assocaited with a + // list of token types where we should "bail" from our search + // for a closing token and exit a nested construct. + // In the simplest terms, when looking for `)` or `]` we will + // bail on a `}` or end-of-file, while when looking for a `}` + // we will only bail on an end-of-file. + // + auto nextTokenType = parser->tokenReader.peekTokenType(); + for (auto bailAtTokenTypePtr = info.bailAtCloseTokens;; bailAtTokenTypePtr++) + { + auto bailAtTokenType = *bailAtTokenTypePtr; + if (nextTokenType == bailAtTokenType) { - TryRecoverBefore(parser, info.closeTokenType); - } - - // If the result of our recovery effort is that we are looking - // at the token type we wanted, we can consume it and return, - // with the parser happily recovered. - // - if (AdvanceIf(parser, info.closeTokenType, outToken)) + // If we are going to bail out of the loop here, then + // we make sure to try to read the token type we were + // originally looking for, even though we know it will + // fail. + // + // If we are already in recovery mode, this will do nothing. + // If we *aren't* in recovery mode, this step is what leads + // the parser to output an error message like "expected + // a `)`, found a `}`" which is pretty much exactly what + // we want. + // + *outToken = parser->ReadToken(info.closeTokenType); return true; + } - // Otherwise, we know that we haven't yet recovered. - // The challenge here is that `AdvanceIfMatch()` is almost always - // called in a loop, and we need that loop to terminate at - // some point. - // - // Each of the types of matched tokens is assocaited with a - // list of token types where we should "bail" from our search - // for a closing token and exit a nested construct. - // In the simplest terms, when looking for `)` or `]` we will - // bail on a `}` or end-of-file, while when looking for a `}` - // we will only bail on an end-of-file. + // The list of token types that should cause us to "bail" on + // our search is always terminated by the EOF token type, so + // we don't want to read past that one. // - auto nextTokenType = parser->tokenReader.peekTokenType(); - for(auto bailAtTokenTypePtr = info.bailAtCloseTokens;; bailAtTokenTypePtr++) - { - auto bailAtTokenType = *bailAtTokenTypePtr; - if(nextTokenType == bailAtTokenType) - { - // If we are going to bail out of the loop here, then - // we make sure to try to read the token type we were - // originally looking for, even though we know it will - // fail. - // - // If we are already in recovery mode, this will do nothing. - // If we *aren't* in recovery mode, this step is what leads - // the parser to output an error message like "expected - // a `)`, found a `}`" which is pretty much exactly what - // we want. - // - *outToken = parser->ReadToken(info.closeTokenType); - return true; - } - - // The list of token types that should cause us to "bail" on - // our search is always terminated by the EOF token type, so - // we don't want to read past that one. - // - if(bailAtTokenType == TokenType::EndOfFile) - break; - } - return false; + if (bailAtTokenType == TokenType::EndOfFile) + break; } + return false; +} - bool AdvanceIfMatch(Parser* parser, MatchedTokenType type) - { - Token ignored; - return AdvanceIfMatch(parser, type, &ignored); - } +bool AdvanceIfMatch(Parser* parser, MatchedTokenType type) +{ + Token ignored; + return AdvanceIfMatch(parser, type, &ignored); +} - NodeBase* parseTypeDef(Parser* parser, void* /*userData*/) - { - TypeDefDecl* typeDefDecl = parser->astBuilder->create<TypeDefDecl>(); +NodeBase* parseTypeDef(Parser* parser, void* /*userData*/) +{ + TypeDefDecl* typeDefDecl = parser->astBuilder->create<TypeDefDecl>(); - // TODO(tfoley): parse an actual declarator - auto type = parser->ParseTypeExp(); + // TODO(tfoley): parse an actual declarator + auto type = parser->ParseTypeExp(); - auto nameToken = parser->ReadToken(TokenType::Identifier); - typeDefDecl->loc = nameToken.loc; + auto nameToken = parser->ReadToken(TokenType::Identifier); + typeDefDecl->loc = nameToken.loc; - typeDefDecl->nameAndLoc = NameLoc(nameToken); - typeDefDecl->type = type; + typeDefDecl->nameAndLoc = NameLoc(nameToken); + typeDefDecl->type = type; - return typeDefDecl; - } + return typeDefDecl; +} + +// Add a modifier to a list of modifiers being built +static void AddModifier(Modifier*** ioModifierLink, Modifier* modifier) +{ + Modifier**& modifierLink = *ioModifierLink; - // Add a modifier to a list of modifiers being built - static void AddModifier(Modifier*** ioModifierLink, Modifier* modifier) + // We'd like to add the modifier to the end of the list, + // but we need to be careful, in case there is a "shared" + // section of modifiers for multiple declarations. + // + // TODO: This whole approach is a mess because we are "accidentally quadratic" + // when adding many modifiers. + for (;;) { - Modifier**& modifierLink = *ioModifierLink; + // At end of the chain? Done. + if (!*modifierLink) + break; - // We'd like to add the modifier to the end of the list, - // but we need to be careful, in case there is a "shared" - // section of modifiers for multiple declarations. - // - // TODO: This whole approach is a mess because we are "accidentally quadratic" - // when adding many modifiers. - for(;;) + // About to look at shared modifiers? Done. + Modifier* linkMod = *modifierLink; + if (as<SharedModifiers>(linkMod)) { - // At end of the chain? Done. - if(!*modifierLink) - break; + break; + } - // About to look at shared modifiers? Done. - Modifier* linkMod = *modifierLink; - if(as<SharedModifiers>(linkMod)) - { - break; - } + // Otherwise: keep traversing the modifier list. + modifierLink = &(*modifierLink)->next; + } - // Otherwise: keep traversing the modifier list. - modifierLink = &(*modifierLink)->next; - } + // Splice the modifier into the linked list - // Splice the modifier into the linked list + // We need to deal with the case where the modifier to + // be spliced in might actually be a modifier *list*, + // so that we actually want to splice in at the + // end of the new list... + auto spliceLink = &modifier->next; + while (*spliceLink) + spliceLink = &(*spliceLink)->next; - // We need to deal with the case where the modifier to - // be spliced in might actually be a modifier *list*, - // so that we actually want to splice in at the - // end of the new list... - auto spliceLink = &modifier->next; - while(*spliceLink) - spliceLink = &(*spliceLink)->next; + // Do the splice. + *spliceLink = *modifierLink; - // Do the splice. - *spliceLink = *modifierLink; + *modifierLink = modifier; + modifierLink = &modifier->next; +} + +void addModifier(ModifiableSyntaxNode* syntax, Modifier* modifier) +{ + auto modifierLink = &syntax->modifiers.first; + AddModifier(&modifierLink, modifier); +} - *modifierLink = modifier; - modifierLink = &modifier->next; +// +// '::'? identifier ('::' identifier)* +static Token parseAttributeName(Parser* parser, Token& outOriginalLastToken) +{ + const SourceLoc scopedIdSourceLoc = parser->tokenReader.peekLoc(); + + // Strip initial :: if there is one + const TokenType initialTokenType = parser->tokenReader.peekTokenType(); + if (initialTokenType == TokenType::Scope) + { + parser->ReadToken(TokenType::Scope); } + if (parser->LookAheadToken(TokenType::CompletionRequest)) + return parser->ReadToken(); - void addModifier( - ModifiableSyntaxNode* syntax, - Modifier* modifier) + const Token firstIdentifier = parser->ReadToken(TokenType::Identifier); + outOriginalLastToken = firstIdentifier; + if (initialTokenType != TokenType::Scope && + parser->tokenReader.peekTokenType() != TokenType::Scope) { - auto modifierLink = &syntax->modifiers.first; - AddModifier(&modifierLink, modifier); + return firstIdentifier; } - // - // '::'? identifier ('::' identifier)* - static Token parseAttributeName(Parser* parser, Token& outOriginalLastToken) + // Build up scoped string + StringBuilder scopedIdentifierBuilder; + if (initialTokenType == TokenType::Scope) { - const SourceLoc scopedIdSourceLoc = parser->tokenReader.peekLoc(); + scopedIdentifierBuilder.append('_'); + } + scopedIdentifierBuilder.append(firstIdentifier.getContent()); - // Strip initial :: if there is one - const TokenType initialTokenType = parser->tokenReader.peekTokenType(); - if (initialTokenType == TokenType::Scope) - { - parser->ReadToken(TokenType::Scope); - } - if (parser->LookAheadToken(TokenType::CompletionRequest)) - return parser->ReadToken(); + while (parser->tokenReader.peekTokenType() == TokenType::Scope) + { + parser->ReadToken(TokenType::Scope); + scopedIdentifierBuilder.append('_'); - const Token firstIdentifier = parser->ReadToken(TokenType::Identifier); - outOriginalLastToken = firstIdentifier; - if (initialTokenType != TokenType::Scope && parser->tokenReader.peekTokenType() != TokenType::Scope) - { - return firstIdentifier; - } + const Token nextIdentifier(parser->ReadToken(TokenType::Identifier)); + outOriginalLastToken = nextIdentifier; + scopedIdentifierBuilder.append(nextIdentifier.getContent()); + } - // Build up scoped string - StringBuilder scopedIdentifierBuilder; - if (initialTokenType == TokenType::Scope) - { - scopedIdentifierBuilder.append('_'); - } - scopedIdentifierBuilder.append(firstIdentifier.getContent()); + // Make a 'token' + SourceManager* sourceManager = parser->sink->getSourceManager(); + const UnownedStringSlice scopedIdentifier( + sourceManager->allocateStringSlice(scopedIdentifierBuilder.getUnownedSlice())); + Token token(TokenType::Identifier, scopedIdentifier, scopedIdSourceLoc); - while (parser->tokenReader.peekTokenType() == TokenType::Scope) - { - parser->ReadToken(TokenType::Scope); - scopedIdentifierBuilder.append('_'); - - const Token nextIdentifier(parser->ReadToken(TokenType::Identifier)); - outOriginalLastToken = nextIdentifier; - scopedIdentifierBuilder.append(nextIdentifier.getContent()); - } + // Get the name pool + auto namePool = parser->getNamePool(); - // Make a 'token' - SourceManager* sourceManager = parser->sink->getSourceManager(); - const UnownedStringSlice scopedIdentifier(sourceManager->allocateStringSlice(scopedIdentifierBuilder.getUnownedSlice())); - Token token(TokenType::Identifier, scopedIdentifier, scopedIdSourceLoc); + // Since it's an Identifier have to set the name. + token.setName(namePool->getName(token.getContent())); - // Get the name pool - auto namePool = parser->getNamePool(); + return token; +} - // Since it's an Identifier have to set the name. - token.setName(namePool->getName(token.getContent())); +// Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers +static void ParseSquareBracketAttributes(Parser* parser, Modifier*** ioModifierLink) +{ + parser->ReadToken(TokenType::LBracket); - return token; - } + const bool hasDoubleBracket = AdvanceIf(parser, TokenType::LBracket); - // Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers - static void ParseSquareBracketAttributes(Parser* parser, Modifier*** ioModifierLink) + for (;;) { - parser->ReadToken(TokenType::LBracket); - - const bool hasDoubleBracket = AdvanceIf(parser, TokenType::LBracket); + // Note: When parsing we just construct an AST node for an + // "unchecked" attribute, and defer all detailed semantic + // checking until later. + // + // An alternative would be to perform lookup of an `AttributeDecl` + // at this point, similar to what we do for `SyntaxDecl`, but it + // seems better to not complicate the parsing process any more. + // - for(;;) - { - // Note: When parsing we just construct an AST node for an - // "unchecked" attribute, and defer all detailed semantic - // checking until later. - // - // An alternative would be to perform lookup of an `AttributeDecl` - // at this point, similar to what we do for `SyntaxDecl`, but it - // seems better to not complicate the parsing process any more. - // + Token originalLastToken; + Token nameToken = parseAttributeName(parser, originalLastToken); - Token originalLastToken; - Token nameToken = parseAttributeName(parser, originalLastToken); + UncheckedAttribute* modifier = parser->astBuilder->create<UncheckedAttribute>(); + modifier->keywordName = nameToken.getName(); + modifier->loc = originalLastToken.getLoc(); + modifier->scope = parser->currentScope; + modifier->originalIdentifierToken = originalLastToken; - UncheckedAttribute* modifier = parser->astBuilder->create<UncheckedAttribute>(); - modifier->keywordName = nameToken.getName(); - modifier->loc = originalLastToken.getLoc(); - modifier->scope = parser->currentScope; - modifier->originalIdentifierToken = originalLastToken; + if (AdvanceIf(parser, TokenType::LParent)) + { + // HLSL-style `[name(arg0, ...)]` attribute - if (AdvanceIf(parser, TokenType::LParent)) + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - // HLSL-style `[name(arg0, ...)]` attribute - - while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + auto arg = parser->ParseArgExpr(); + if (arg) { - auto arg = parser->ParseArgExpr(); - if (arg) - { - modifier->args.add(arg); - } - - if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - break; - - - parser->ReadToken(TokenType::Comma); + modifier->args.add(arg); } - } - AddModifier(ioModifierLink, modifier); + if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + break; - if (AdvanceIfMatch(parser, MatchedTokenType::SquareBrackets)) - break; - // If there is a comma consume it. It appears that the comma is optional. - AdvanceIf(parser, TokenType::Comma); + parser->ReadToken(TokenType::Comma); + } } + AddModifier(ioModifierLink, modifier); - if (hasDoubleBracket) - { - // Read the second ] - parser->ReadToken(TokenType::RBracket); - } - } - static TokenType peekTokenType(Parser* parser) - { - return parser->tokenReader.peekTokenType(); - } + if (AdvanceIfMatch(parser, MatchedTokenType::SquareBrackets)) + break; - /// Peek the token `offset` tokens after the cursor - static TokenType peekTokenType(Parser* parser, int offset) - { - TokenReader r = parser->tokenReader; - for (int ii = 0; ii < offset; ++ii) - r.advanceToken(); - return r.peekTokenType(); + // If there is a comma consume it. It appears that the comma is optional. + AdvanceIf(parser, TokenType::Comma); } - static Token advanceToken(Parser* parser) + if (hasDoubleBracket) { - return parser->ReadToken(); + // Read the second ] + parser->ReadToken(TokenType::RBracket); } +} - static Token peekToken(Parser* parser) - { - return parser->tokenReader.peekToken(); - } +static TokenType peekTokenType(Parser* parser) +{ + return parser->tokenReader.peekTokenType(); +} - static SyntaxDecl* tryLookUpSyntaxDecl( - Parser* parser, - Name* name) - { - // Let's look up the name and see what we find. +/// Peek the token `offset` tokens after the cursor +static TokenType peekTokenType(Parser* parser, int offset) +{ + TokenReader r = parser->tokenReader; + for (int ii = 0; ii < offset; ++ii) + r.advanceToken(); + return r.peekTokenType(); +} - auto lookupResult = lookUp( - parser->astBuilder, - nullptr, // no semantics visitor available yet - name, - parser->currentScope, - LookupMask::Default, - true); - - // If we didn't find anything, or the result was overloaded, - // then we aren't going to be able to extract a single decl. - if(!lookupResult.isValid() || lookupResult.isOverloaded()) - return nullptr; +static Token advanceToken(Parser* parser) +{ + return parser->ReadToken(); +} - auto decl = lookupResult.item.declRef.getDecl(); - if( auto syntaxDecl = as<SyntaxDecl>(decl) ) - { - return syntaxDecl; - } - else - { - return nullptr; - } - } +static Token peekToken(Parser* parser) +{ + return parser->tokenReader.peekToken(); +} - template<typename T> - bool tryParseUsingSyntaxDeclImpl( - Parser* parser, - SyntaxDecl* syntaxDecl, - T** outSyntax) +static SyntaxDecl* tryLookUpSyntaxDecl(Parser* parser, Name* name) +{ + // Let's look up the name and see what we find. + + auto lookupResult = lookUp( + parser->astBuilder, + nullptr, // no semantics visitor available yet + name, + parser->currentScope, + LookupMask::Default, + true); + + // If we didn't find anything, or the result was overloaded, + // then we aren't going to be able to extract a single decl. + if (!lookupResult.isValid() || lookupResult.isOverloaded()) + return nullptr; + + auto decl = lookupResult.item.declRef.getDecl(); + if (auto syntaxDecl = as<SyntaxDecl>(decl)) { - if (!syntaxDecl) - return false; + return syntaxDecl; + } + else + { + return nullptr; + } +} - if (!syntaxDecl->syntaxClass.isSubClassOf<T>()) - return false; +template<typename T> +bool tryParseUsingSyntaxDeclImpl(Parser* parser, SyntaxDecl* syntaxDecl, T** outSyntax) +{ + if (!syntaxDecl) + return false; - // Consume the token that specified the keyword - auto keywordToken = advanceToken(parser); + if (!syntaxDecl->syntaxClass.isSubClassOf<T>()) + return false; - NodeBase* parsedObject = syntaxDecl->parseCallback(parser, syntaxDecl->parseUserData); - if (!parsedObject) - { - return false; - } + // Consume the token that specified the keyword + auto keywordToken = advanceToken(parser); - auto innerParsedObject = parsedObject; - auto genericDecl = as<GenericDecl>(parsedObject); - if (genericDecl) - innerParsedObject = genericDecl->inner; + NodeBase* parsedObject = syntaxDecl->parseCallback(parser, syntaxDecl->parseUserData); + if (!parsedObject) + { + return false; + } - auto syntax = as<T>(innerParsedObject); - if (syntax) + auto innerParsedObject = parsedObject; + auto genericDecl = as<GenericDecl>(parsedObject); + if (genericDecl) + innerParsedObject = genericDecl->inner; + + auto syntax = as<T>(innerParsedObject); + if (syntax) + { + if (!syntax->loc.isValid()) { - if (!syntax->loc.isValid()) + syntax->loc = keywordToken.loc; + if (genericDecl) { - syntax->loc = keywordToken.loc; - if (genericDecl) - { - genericDecl->nameAndLoc.loc = syntax->loc; - genericDecl->loc = syntax->loc; - } - if (auto decl = as<Decl>(syntax)) - decl->nameAndLoc.loc = syntax->loc; + genericDecl->nameAndLoc.loc = syntax->loc; + genericDecl->loc = syntax->loc; } + if (auto decl = as<Decl>(syntax)) + decl->nameAndLoc.loc = syntax->loc; } - else if (parsedObject) - { - // Something was parsed, but it didn't have the expected type! - SLANG_DIAGNOSE_UNEXPECTED(parser->sink, keywordToken, "parser callback did not return the expected type"); - } - - if (auto converted = as<T>(parsedObject)) - { - *outSyntax = converted; - return true; - } - return false; + } + else if (parsedObject) + { + // Something was parsed, but it didn't have the expected type! + SLANG_DIAGNOSE_UNEXPECTED( + parser->sink, + keywordToken, + "parser callback did not return the expected type"); } - template<typename T> - bool tryParseUsingSyntaxDecl( - Parser* parser, - T** outSyntax) + if (auto converted = as<T>(parsedObject)) { - if (peekTokenType(parser) != TokenType::Identifier) - return false; + *outSyntax = converted; + return true; + } + return false; +} - auto nameToken = peekToken(parser); - auto name = nameToken.getName(); +template<typename T> +bool tryParseUsingSyntaxDecl(Parser* parser, T** outSyntax) +{ + if (peekTokenType(parser) != TokenType::Identifier) + return false; - auto syntaxDecl = tryLookUpSyntaxDecl(parser, name); + auto nameToken = peekToken(parser); + auto name = nameToken.getName(); - if (!syntaxDecl) - return false; + auto syntaxDecl = tryLookUpSyntaxDecl(parser, name); - return tryParseUsingSyntaxDeclImpl<T>(parser, syntaxDecl, outSyntax); - } + if (!syntaxDecl) + return false; + + return tryParseUsingSyntaxDeclImpl<T>(parser, syntaxDecl, outSyntax); +} - static Modifiers ParseModifiers(Parser* parser) +static Modifiers ParseModifiers(Parser* parser) +{ + Modifiers modifiers; + Modifier** modifierLink = &modifiers.first; + for (;;) { - Modifiers modifiers; - Modifier** modifierLink = &modifiers.first; - for (;;) + switch (peekTokenType(parser)) { - switch (peekTokenType(parser)) - { - default: - // If we don't see a token type that we recognize, then - // assume we are done with the modifier sequence. - return modifiers; + default: + // If we don't see a token type that we recognize, then + // assume we are done with the modifier sequence. + return modifiers; - case TokenType::Identifier: - { - // We see an identifier ahead, and it might be the name - // of a modifier keyword of some kind. + case TokenType::Identifier: + { + // We see an identifier ahead, and it might be the name + // of a modifier keyword of some kind. - Token nameToken = peekToken(parser); + Token nameToken = peekToken(parser); - Modifier* parsedModifier = nullptr; - if (tryParseUsingSyntaxDecl<Modifier>(parser, &parsedModifier)) + Modifier* parsedModifier = nullptr; + if (tryParseUsingSyntaxDecl<Modifier>(parser, &parsedModifier)) + { + parsedModifier->keywordName = nameToken.getName(); + if (!parsedModifier->loc.isValid()) { - parsedModifier->keywordName = nameToken.getName(); - if (!parsedModifier->loc.isValid()) - { - parsedModifier->loc = nameToken.loc; - } - if (as<VisibilityModifier>(parsedModifier)) - { - if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; - } - AddModifier(&modifierLink, parsedModifier); - continue; + parsedModifier->loc = nameToken.loc; + } + if (as<VisibilityModifier>(parsedModifier)) + { + if (auto currentModule = parser->getCurrentModuleDecl()) + currentModule->isInLegacyLanguage = false; } - else if (AdvanceIf(parser, "no_diff")) + AddModifier(&modifierLink, parsedModifier); + continue; + } + else if (AdvanceIf(parser, "no_diff")) + { + parsedModifier = parser->astBuilder->create<NoDiffModifier>(); + parsedModifier->keywordName = nameToken.getName(); + parsedModifier->loc = nameToken.loc; + AddModifier(&modifierLink, parsedModifier); + continue; + } + else if (parser->options.allowGLSLInput) + { + if (AdvanceIf(parser, "flat")) { - parsedModifier = parser->astBuilder->create<NoDiffModifier>(); + parsedModifier = parser->astBuilder->create<HLSLNoInterpolationModifier>(); parsedModifier->keywordName = nameToken.getName(); parsedModifier->loc = nameToken.loc; AddModifier(&modifierLink, parsedModifier); continue; } - else if (parser->options.allowGLSLInput) - { - if (AdvanceIf(parser, "flat")) - { - parsedModifier = parser->astBuilder->create<HLSLNoInterpolationModifier>(); - parsedModifier->keywordName = nameToken.getName(); - parsedModifier->loc = nameToken.loc; - AddModifier(&modifierLink, parsedModifier); - continue; - } - } - // If there was no match for a modifier keyword, then we - // must be at the end of the modifier sequence - return modifiers; } - break; - - // HLSL uses `[attributeName]` style for its modifiers, which closely - // matches the C++ `[[attributeName]]` style. - case TokenType::LBracket: - ParseSquareBracketAttributes(parser, &modifierLink); - break; + // If there was no match for a modifier keyword, then we + // must be at the end of the modifier sequence + return modifiers; } + break; + + // HLSL uses `[attributeName]` style for its modifiers, which closely + // matches the C++ `[[attributeName]]` style. + case TokenType::LBracket: ParseSquareBracketAttributes(parser, &modifierLink); break; } } +} - static Name* getName(Parser* parser, String const& text) - { - return parser->getNamePool()->getName(text); - } +static Name* getName(Parser* parser, String const& text) +{ + return parser->getNamePool()->getName(text); +} - static bool expect(Parser* parser, TokenType tokenType) - { - return parser->ReadToken(tokenType).type == tokenType; - } +static bool expect(Parser* parser, TokenType tokenType) +{ + return parser->ReadToken(tokenType).type == tokenType; +} - static NameLoc expectIdentifier(Parser* parser) - { - if (!parser->hasSeenCompletionToken && parser->LookAheadToken(TokenType::CompletionRequest)) - parser->hasSeenCompletionToken = true; - return NameLoc(parser->ReadToken(TokenType::Identifier)); - } +static NameLoc expectIdentifier(Parser* parser) +{ + if (!parser->hasSeenCompletionToken && parser->LookAheadToken(TokenType::CompletionRequest)) + parser->hasSeenCompletionToken = true; + return NameLoc(parser->ReadToken(TokenType::Identifier)); +} + +static void parseFileReferenceDeclBase(Parser* parser, FileReferenceDeclBase* decl) +{ + decl->scope = parser->currentScope; + decl->startLoc = parser->tokenReader.peekLoc(); - static void parseFileReferenceDeclBase(Parser* parser, FileReferenceDeclBase* decl) + if (peekTokenType(parser) == TokenType::StringLiteral) { - decl->scope = parser->currentScope; - decl->startLoc = parser->tokenReader.peekLoc(); + auto nameToken = parser->ReadToken(TokenType::StringLiteral); + auto nameString = getStringLiteralTokenValue(nameToken); + auto moduleName = getName(parser, nameString); - if (peekTokenType(parser) == TokenType::StringLiteral) - { - auto nameToken = parser->ReadToken(TokenType::StringLiteral); - auto nameString = getStringLiteralTokenValue(nameToken); - auto moduleName = getName(parser, nameString); + decl->moduleNameAndLoc = NameLoc(moduleName, nameToken.loc); + } + else + { + auto moduleNameAndLoc = expectIdentifier(parser); - decl->moduleNameAndLoc = NameLoc(moduleName, nameToken.loc); - } - else + // We allow a dotted format for the name, as sugar + if (peekTokenType(parser) == TokenType::Dot) { - auto moduleNameAndLoc = expectIdentifier(parser); - - // We allow a dotted format for the name, as sugar - if (peekTokenType(parser) == TokenType::Dot) + StringBuilder sb; + sb << getText(moduleNameAndLoc.name); + while (AdvanceIf(parser, TokenType::Dot)) { - StringBuilder sb; - sb << getText(moduleNameAndLoc.name); - while (AdvanceIf(parser, TokenType::Dot)) - { - sb << "/"; - sb << parser->ReadToken(TokenType::Identifier).getContent(); - } - - moduleNameAndLoc.name = getName(parser, sb.produceString()); + sb << "/"; + sb << parser->ReadToken(TokenType::Identifier).getContent(); } - decl->moduleNameAndLoc = moduleNameAndLoc; + moduleNameAndLoc.name = getName(parser, sb.produceString()); } - decl->endLoc = parser->tokenReader.peekLoc(); - parser->ReadToken(TokenType::Semicolon); - } - static NodeBase* parseImportDecl( - Parser* parser, void* /*userData*/) - { - auto decl = parser->astBuilder->create<ImportDecl>(); - parseFileReferenceDeclBase(parser, decl); - return decl; + decl->moduleNameAndLoc = moduleNameAndLoc; } + decl->endLoc = parser->tokenReader.peekLoc(); + parser->ReadToken(TokenType::Semicolon); +} - static NodeBase* parseIncludeDecl( - Parser* parser, void* /*userData*/) - { - auto decl = parser->astBuilder->create<IncludeDecl>(); - parseFileReferenceDeclBase(parser, decl); - if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; - return decl; - } +static NodeBase* parseImportDecl(Parser* parser, void* /*userData*/) +{ + auto decl = parser->astBuilder->create<ImportDecl>(); + parseFileReferenceDeclBase(parser, decl); + return decl; +} - static NodeBase* parseImplementingDecl( - Parser* parser, void* /*userData*/) - { - auto decl = parser->astBuilder->create<ImplementingDecl>(); - parseFileReferenceDeclBase(parser, decl); - return decl; - } +static NodeBase* parseIncludeDecl(Parser* parser, void* /*userData*/) +{ + auto decl = parser->astBuilder->create<IncludeDecl>(); + parseFileReferenceDeclBase(parser, decl); + if (auto currentModule = parser->getCurrentModuleDecl()) + currentModule->isInLegacyLanguage = false; + return decl; +} + +static NodeBase* parseImplementingDecl(Parser* parser, void* /*userData*/) +{ + auto decl = parser->astBuilder->create<ImplementingDecl>(); + parseFileReferenceDeclBase(parser, decl); + return decl; +} - static NodeBase* parseModuleDeclarationDecl( - Parser* parser, void* /*userData*/) +static NodeBase* parseModuleDeclarationDecl(Parser* parser, void* /*userData*/) +{ + auto decl = parser->astBuilder->create<ModuleDeclarationDecl>(); + auto moduleDecl = parser->getCurrentModuleDecl(); + if (parser->LookAheadToken(TokenType::Identifier)) { - auto decl = parser->astBuilder->create<ModuleDeclarationDecl>(); - auto moduleDecl = parser->getCurrentModuleDecl(); - if (parser->LookAheadToken(TokenType::Identifier)) - { - auto nameToken = parser->ReadToken(TokenType::Identifier); - decl->nameAndLoc.name = parser->getNamePool()->getName(nameToken.getContent()); - decl->nameAndLoc.loc = nameToken.loc; - if (moduleDecl) moduleDecl->nameAndLoc = decl->nameAndLoc; - } - else if (parser->LookAheadToken(TokenType::StringLiteral)) - { - auto nameToken = parser->ReadToken(TokenType::StringLiteral); - decl->nameAndLoc.name = parser->getNamePool()->getName(getStringLiteralTokenValue(nameToken)); - decl->nameAndLoc.loc = nameToken.loc; - if (moduleDecl) moduleDecl->nameAndLoc = decl->nameAndLoc; - } - else - { - if (moduleDecl) - decl->nameAndLoc.name = moduleDecl->getName(); - decl->nameAndLoc.loc = parser->tokenReader.peekLoc(); - } - parser->ReadToken(TokenType::Semicolon); - if (auto currentModule = parser->getCurrentModuleDecl()) - currentModule->isInLegacyLanguage = false; - return decl; - } + auto nameToken = parser->ReadToken(TokenType::Identifier); + decl->nameAndLoc.name = parser->getNamePool()->getName(nameToken.getContent()); + decl->nameAndLoc.loc = nameToken.loc; + if (moduleDecl) + moduleDecl->nameAndLoc = decl->nameAndLoc; + } + else if (parser->LookAheadToken(TokenType::StringLiteral)) + { + auto nameToken = parser->ReadToken(TokenType::StringLiteral); + decl->nameAndLoc.name = + parser->getNamePool()->getName(getStringLiteralTokenValue(nameToken)); + decl->nameAndLoc.loc = nameToken.loc; + if (moduleDecl) + moduleDecl->nameAndLoc = decl->nameAndLoc; + } + else + { + if (moduleDecl) + decl->nameAndLoc.name = moduleDecl->getName(); + decl->nameAndLoc.loc = parser->tokenReader.peekLoc(); + } + parser->ReadToken(TokenType::Semicolon); + if (auto currentModule = parser->getCurrentModuleDecl()) + currentModule->isInLegacyLanguage = false; + return decl; +} - static NameLoc ParseDeclName( - Parser* parser) +static NameLoc ParseDeclName(Parser* parser) +{ + Token nameToken; + if (AdvanceIf(parser, "operator")) { - Token nameToken; - if (AdvanceIf(parser, "operator")) + nameToken = parser->ReadToken(); + switch (nameToken.type) { - nameToken = parser->ReadToken(); - switch (nameToken.type) - { - case TokenType::OpAdd: case TokenType::OpSub: case TokenType::OpMul: case TokenType::OpDiv: - case TokenType::OpMod: case TokenType::OpNot: case TokenType::OpBitNot: case TokenType::OpLsh: case TokenType::OpRsh: - case TokenType::OpEql: case TokenType::OpNeq: case TokenType::OpGreater: case TokenType::OpLess: case TokenType::OpGeq: - case TokenType::OpLeq: case TokenType::OpAnd: case TokenType::OpOr: case TokenType::OpBitXor: case TokenType::OpBitAnd: - case TokenType::OpBitOr: case TokenType::OpInc: case TokenType::OpDec: - case TokenType::OpAddAssign: - case TokenType::OpSubAssign: - case TokenType::OpMulAssign: - case TokenType::OpDivAssign: - case TokenType::OpModAssign: - case TokenType::OpShlAssign: - case TokenType::OpShrAssign: - case TokenType::OpOrAssign: - case TokenType::OpAndAssign: - case TokenType::OpXorAssign: - - // Note(tfoley): A bit of a hack: - case TokenType::Comma: - case TokenType::OpAssign: - break; - case TokenType::LParent: - parser->ReadToken(TokenType::RParent); - break; + case TokenType::OpAdd: + case TokenType::OpSub: + case TokenType::OpMul: + case TokenType::OpDiv: + case TokenType::OpMod: + case TokenType::OpNot: + case TokenType::OpBitNot: + case TokenType::OpLsh: + case TokenType::OpRsh: + case TokenType::OpEql: + case TokenType::OpNeq: + case TokenType::OpGreater: + case TokenType::OpLess: + case TokenType::OpGeq: + case TokenType::OpLeq: + case TokenType::OpAnd: + case TokenType::OpOr: + case TokenType::OpBitXor: + case TokenType::OpBitAnd: + case TokenType::OpBitOr: + case TokenType::OpInc: + case TokenType::OpDec: + case TokenType::OpAddAssign: + case TokenType::OpSubAssign: + case TokenType::OpMulAssign: + case TokenType::OpDivAssign: + case TokenType::OpModAssign: + case TokenType::OpShlAssign: + case TokenType::OpShrAssign: + case TokenType::OpOrAssign: + case TokenType::OpAndAssign: + case TokenType::OpXorAssign: - // Note(tfoley): Even more of a hack! - case TokenType::QuestionMark: - if (AdvanceIf(parser, TokenType::Colon)) - { - // Concat : onto ? - nameToken.setContent(UnownedStringSlice::fromLiteral("?:")); - break; - } - ; // fall-thru - default: - parser->sink->diagnose(nameToken.loc, Diagnostics::invalidOperator, nameToken); + // Note(tfoley): A bit of a hack: + case TokenType::Comma: + case TokenType::OpAssign: break; + case TokenType::LParent: parser->ReadToken(TokenType::RParent); break; + + // Note(tfoley): Even more of a hack! + case TokenType::QuestionMark: + if (AdvanceIf(parser, TokenType::Colon)) + { + // Concat : onto ? + nameToken.setContent(UnownedStringSlice::fromLiteral("?:")); break; - } + }; // fall-thru + default: + parser->sink->diagnose(nameToken.loc, Diagnostics::invalidOperator, nameToken); + break; + } - if (nameToken.type == TokenType::LParent) - return NameLoc(getName(parser, "()"), nameToken.loc); + if (nameToken.type == TokenType::LParent) + return NameLoc(getName(parser, "()"), nameToken.loc); - return NameLoc( - getName(parser, nameToken.getContent()), - nameToken.loc); - } - else - { - nameToken = parser->ReadToken(TokenType::Identifier); - return NameLoc(nameToken); - } + return NameLoc(getName(parser, nameToken.getContent()), nameToken.loc); } - - // A "declarator" as used in C-style languages - struct Declarator : RefObject + else { - // Different cases of declarator appear as "flavors" here - enum class Flavor - { - name, - Pointer, - Array, - }; - Flavor flavor; - }; + nameToken = parser->ReadToken(TokenType::Identifier); + return NameLoc(nameToken); + } +} - // The most common case of declarator uses a simple name - struct NameDeclarator : Declarator +// A "declarator" as used in C-style languages +struct Declarator : RefObject +{ + // Different cases of declarator appear as "flavors" here + enum class Flavor { - NameLoc nameAndLoc; + name, + Pointer, + Array, }; + Flavor flavor; +}; - // A declarator that declares a pointer type - struct PointerDeclarator : Declarator - { - // location of the `*` token - SourceLoc starLoc; +// The most common case of declarator uses a simple name +struct NameDeclarator : Declarator +{ + NameLoc nameAndLoc; +}; - RefPtr<Declarator> inner; - }; +// A declarator that declares a pointer type +struct PointerDeclarator : Declarator +{ + // location of the `*` token + SourceLoc starLoc; - // A declarator that declares an array type - struct ArrayDeclarator : Declarator - { - RefPtr<Declarator> inner; + RefPtr<Declarator> inner; +}; + +// A declarator that declares an array type +struct ArrayDeclarator : Declarator +{ + RefPtr<Declarator> inner; - // location of the `[` token - SourceLoc openBracketLoc; + // location of the `[` token + SourceLoc openBracketLoc; - // The expression that yields the element count, or NULL - Expr* elementCountExpr = nullptr; - }; + // The expression that yields the element count, or NULL + Expr* elementCountExpr = nullptr; +}; - // "Unwrapped" information about a declarator - struct DeclaratorInfo +// "Unwrapped" information about a declarator +struct DeclaratorInfo +{ + Expr* typeSpec = nullptr; + NameLoc nameAndLoc; + Modifiers semantics; + Expr* initializer = nullptr; +}; + +// Add a member declaration to its container, and ensure that its +// parent link is set up correctly. +static void AddMember(ContainerDecl* container, Decl* member) +{ + if (container) { - Expr* typeSpec = nullptr; - NameLoc nameAndLoc; - Modifiers semantics; - Expr* initializer = nullptr; - }; + container->addMember(member); + } +} - // Add a member declaration to its container, and ensure that its - // parent link is set up correctly. - static void AddMember(ContainerDecl* container, Decl* member) +static void AddMember(Scope* scope, Decl* member) +{ + if (scope) { - if (container) - { - container->addMember(member); - } + scope->containerDecl->addMember(member); } +} - static void AddMember(Scope* scope, Decl* member) +static Decl* ParseGenericParamDecl(Parser* parser, GenericDecl* genericDecl) +{ + // simple syntax to introduce a value parameter + if (AdvanceIf(parser, "let")) { - if (scope) + // default case is a type parameter + auto paramDecl = parser->astBuilder->create<GenericValueParamDecl>(); + paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + if (AdvanceIf(parser, TokenType::Colon)) { - scope->containerDecl->addMember(member); + paramDecl->type = parser->ParseTypeExp(); + } + if (AdvanceIf(parser, TokenType::OpAssign)) + { + paramDecl->initExpr = parser->ParseInitExpr(); } + return paramDecl; } - - static Decl* ParseGenericParamDecl( - Parser* parser, - GenericDecl* genericDecl) + Decl* paramDecl = nullptr; + if (AdvanceIf(parser, "each")) + { + // A type pack parameter. + paramDecl = parser->astBuilder->create<GenericTypePackParamDecl>(); + parser->FillPosition(paramDecl); + paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + } + else { - // simple syntax to introduce a value parameter - if (AdvanceIf(parser, "let")) + // Disambiguate between a type parameter and a value parameter. + // If next token is "typename", then it is a type parameter. + bool isTypeParam = AdvanceIf(parser, "typename"); + if (!isTypeParam) { - // default case is a type parameter - auto paramDecl = parser->astBuilder->create<GenericValueParamDecl>(); - paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - if (AdvanceIf(parser, TokenType::Colon)) - { - paramDecl->type = parser->ParseTypeExp(); - } - if (AdvanceIf(parser, TokenType::OpAssign)) + // Otherwise, if the next token is an identifier, followed by a colon, comma, '=' or + // '>', then it is a type parameter. + isTypeParam = parser->LookAheadToken(TokenType::Identifier); + auto nextNextTokenType = peekTokenType(parser, 1); + switch (nextNextTokenType) { - paramDecl->initExpr = parser->ParseInitExpr(); + case TokenType::Colon: + case TokenType::Comma: + case TokenType::OpGreater: + case TokenType::OpAssign: break; + default: isTypeParam = false; break; } - return paramDecl; } - Decl* paramDecl = nullptr; - if (AdvanceIf(parser, "each")) + + if (isTypeParam) { - // A type pack parameter. - paramDecl = parser->astBuilder->create<GenericTypePackParamDecl>(); + // Parse as a type parameter. + paramDecl = parser->astBuilder->create<GenericTypeParamDecl>(); parser->FillPosition(paramDecl); paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); } else { - // Disambiguate between a type parameter and a value parameter. - // If next token is "typename", then it is a type parameter. - bool isTypeParam = AdvanceIf(parser, "typename"); - if (!isTypeParam) - { - // Otherwise, if the next token is an identifier, followed by a colon, comma, '=' or '>', then it is a type parameter. - isTypeParam = parser->LookAheadToken(TokenType::Identifier); - auto nextNextTokenType = peekTokenType(parser, 1); - switch (nextNextTokenType) - { - case TokenType::Colon: - case TokenType::Comma: - case TokenType::OpGreater: - case TokenType::OpAssign: - break; - default: - isTypeParam = false; - break; - } - } - - if (isTypeParam) - { - // Parse as a type parameter. - paramDecl = parser->astBuilder->create<GenericTypeParamDecl>(); - parser->FillPosition(paramDecl); - paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - } - else + // Parse as a traditional syntax value parameter in the form of `type paramName`. + auto valueParamDecl = parser->astBuilder->create<GenericValueParamDecl>(); + parser->FillPosition(valueParamDecl); + valueParamDecl->type = parser->ParseTypeExp(); + valueParamDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + if (AdvanceIf(parser, TokenType::OpAssign)) { - // Parse as a traditional syntax value parameter in the form of `type paramName`. - auto valueParamDecl = parser->astBuilder->create<GenericValueParamDecl>(); - parser->FillPosition(valueParamDecl); - valueParamDecl->type = parser->ParseTypeExp(); - valueParamDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - if (AdvanceIf(parser, TokenType::OpAssign)) - { - valueParamDecl->initExpr = parser->ParseInitExpr(); - } - return valueParamDecl; + valueParamDecl->initExpr = parser->ParseInitExpr(); } + return valueParamDecl; } - if (AdvanceIf(parser, TokenType::Colon)) - { - // The user is apply a constraint to this type parameter... + } + if (AdvanceIf(parser, TokenType::Colon)) + { + // The user is apply a constraint to this type parameter... - auto paramConstraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); - parser->FillPosition(paramConstraint); + auto paramConstraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); + parser->FillPosition(paramConstraint); - auto paramType = DeclRefType::create( - parser->astBuilder, - DeclRef<Decl>(paramDecl)); + auto paramType = DeclRefType::create(parser->astBuilder, DeclRef<Decl>(paramDecl)); - auto paramTypeExpr = parser->astBuilder->create<SharedTypeExpr>(); - paramTypeExpr->loc = paramDecl->loc; - paramTypeExpr->base.type = paramType; - paramTypeExpr->type = QualType(parser->astBuilder->getTypeType(paramType)); + auto paramTypeExpr = parser->astBuilder->create<SharedTypeExpr>(); + paramTypeExpr->loc = paramDecl->loc; + paramTypeExpr->base.type = paramType; + paramTypeExpr->type = QualType(parser->astBuilder->getTypeType(paramType)); - paramConstraint->sub = TypeExp(paramTypeExpr); - paramConstraint->sup = parser->ParseTypeExp(); + paramConstraint->sub = TypeExp(paramTypeExpr); + paramConstraint->sup = parser->ParseTypeExp(); - AddMember(genericDecl, paramConstraint); - } - if (auto typeParameter = as<GenericTypeParamDecl>(paramDecl)) + AddMember(genericDecl, paramConstraint); + } + if (auto typeParameter = as<GenericTypeParamDecl>(paramDecl)) + { + if (AdvanceIf(parser, TokenType::OpAssign)) { - if (AdvanceIf(parser, TokenType::OpAssign)) - { - typeParameter->initType = parser->ParseTypeExp(); - } + typeParameter->initType = parser->ParseTypeExp(); } - return paramDecl; } + return paramDecl; +} - template<typename TFunc> - static void ParseGenericDeclImpl( - Parser* parser, GenericDecl* decl, const TFunc & parseInnerFunc) - { - parser->ReadToken(TokenType::OpLess); - parser->genericDepth++; - bool oldIsInVariadicGenerics = parser->isInVariadicGenerics; - SLANG_DEFER(parser->isInVariadicGenerics = oldIsInVariadicGenerics); +template<typename TFunc> +static void ParseGenericDeclImpl(Parser* parser, GenericDecl* decl, const TFunc& parseInnerFunc) +{ + parser->ReadToken(TokenType::OpLess); + parser->genericDepth++; + bool oldIsInVariadicGenerics = parser->isInVariadicGenerics; + SLANG_DEFER(parser->isInVariadicGenerics = oldIsInVariadicGenerics); - for (;;) + for (;;) + { + const TokenType tokenType = parser->tokenReader.peekTokenType(); + if (tokenType == TokenType::OpGreater || tokenType == TokenType::EndOfFile) { - const TokenType tokenType = parser->tokenReader.peekTokenType(); - if (tokenType == TokenType::OpGreater || - tokenType == TokenType::EndOfFile) - { - break; - } + break; + } - auto currentCursor = parser->tokenReader.getCursor(); + auto currentCursor = parser->tokenReader.getCursor(); - auto genericParam = ParseGenericParamDecl(parser, decl); - AddMember(decl, genericParam); + auto genericParam = ParseGenericParamDecl(parser, decl); + AddMember(decl, genericParam); - if (as<GenericTypePackParamDecl>(genericParam)) - { - parser->isInVariadicGenerics = true; - } + if (as<GenericTypePackParamDecl>(genericParam)) + { + parser->isInVariadicGenerics = true; + } - // Make sure we make forward progress. - if (parser->tokenReader.getCursor() == currentCursor) - advanceToken(parser); + // Make sure we make forward progress. + if (parser->tokenReader.getCursor() == currentCursor) + advanceToken(parser); - if (parser->LookAheadToken(TokenType::OpGreater)) - break; + if (parser->LookAheadToken(TokenType::OpGreater)) + break; - if (!AdvanceIf(parser, TokenType::Comma)) - break; - } - parser->genericDepth--; - parser->ReadToken(TokenType::OpGreater); - decl->inner = parseInnerFunc(decl); - decl->inner->parentDecl = decl; + if (!AdvanceIf(parser, TokenType::Comma)) + break; + } + parser->genericDepth--; + parser->ReadToken(TokenType::OpGreater); + decl->inner = parseInnerFunc(decl); + decl->inner->parentDecl = decl; - // A generic decl hijacks the name of the declaration - // it wraps, so that lookup can find it. - if (decl->inner) - { - decl->nameAndLoc = decl->inner->nameAndLoc; - decl->loc = decl->inner->loc; - } + // A generic decl hijacks the name of the declaration + // it wraps, so that lookup can find it. + if (decl->inner) + { + decl->nameAndLoc = decl->inner->nameAndLoc; + decl->loc = decl->inner->loc; } +} - template<typename ParseFunc> - static Decl* parseOptGenericDecl( - Parser* parser, const ParseFunc& parseInner) +template<typename ParseFunc> +static Decl* parseOptGenericDecl(Parser* parser, const ParseFunc& parseInner) +{ + // TODO: may want more advanced disambiguation than this... + if (parser->LookAheadToken(TokenType::OpLess)) { - // TODO: may want more advanced disambiguation than this... - if (parser->LookAheadToken(TokenType::OpLess)) - { - GenericDecl* genericDecl = parser->astBuilder->create<GenericDecl>(); - parser->FillPosition(genericDecl); - parser->PushScope(genericDecl); - ParseGenericDeclImpl(parser, genericDecl, parseInner); - parser->PopScope(); - return genericDecl; - } - else - { - auto genericParent = parser->currentScope ? as<GenericDecl>(parser->currentScope->containerDecl) : nullptr; - return parseInner(genericParent); - } + GenericDecl* genericDecl = parser->astBuilder->create<GenericDecl>(); + parser->FillPosition(genericDecl); + parser->PushScope(genericDecl); + ParseGenericDeclImpl(parser, genericDecl, parseInner); + parser->PopScope(); + return genericDecl; + } + else + { + auto genericParent = + parser->currentScope ? as<GenericDecl>(parser->currentScope->containerDecl) : nullptr; + return parseInner(genericParent); } +} - static void maybeParseGenericConstraints(Parser* parser, ContainerDecl* genericParent) +static void maybeParseGenericConstraints(Parser* parser, ContainerDecl* genericParent) +{ + if (!genericParent) + return; + Token whereToken; + while (AdvanceIf(parser, "where", &whereToken)) { - if (!genericParent) - return; - Token whereToken; - while (AdvanceIf(parser, "where", &whereToken)) + auto subType = parser->ParseTypeExp(); + if (AdvanceIf(parser, TokenType::Colon)) { - auto subType = parser->ParseTypeExp(); - if (AdvanceIf(parser, TokenType::Colon)) - { - for (;;) - { - auto constraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); - constraint->whereTokenLoc = whereToken.loc; - parser->FillPosition(constraint); - constraint->sub = subType; - constraint->sup = parser->ParseTypeExp(); - AddMember(genericParent, constraint); - if (!AdvanceIf(parser, TokenType::Comma)) - break; - } - } - else if (AdvanceIf(parser, TokenType::OpEql)) + for (;;) { auto constraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); constraint->whereTokenLoc = whereToken.loc; - constraint->isEqualityConstraint = true; parser->FillPosition(constraint); constraint->sub = subType; constraint->sup = parser->ParseTypeExp(); AddMember(genericParent, constraint); + if (!AdvanceIf(parser, TokenType::Comma)) + break; } } + else if (AdvanceIf(parser, TokenType::OpEql)) + { + auto constraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); + constraint->whereTokenLoc = whereToken.loc; + constraint->isEqualityConstraint = true; + parser->FillPosition(constraint); + constraint->sub = subType; + constraint->sup = parser->ParseTypeExp(); + AddMember(genericParent, constraint); + } } +} + +static NodeBase* parseGenericDecl(Parser* parser, void*) +{ + GenericDecl* decl = parser->astBuilder->create<GenericDecl>(); + parser->FillPosition(decl); + parser->PushScope(decl); + ParseGenericDeclImpl( + parser, + decl, + [=](GenericDecl* genDecl) { return ParseSingleDecl(parser, genDecl); }); + parser->PopScope(); + return decl; +} + +static void parseParameterList(Parser* parser, CallableDecl* decl) +{ + parser->ReadToken(TokenType::LParent); - static NodeBase* parseGenericDecl(Parser* parser, void*) + // Allow a declaration to use the keyword `void` for a parameter list, + // since that was required in ancient C, and continues to be supported + // in a bunch of its derivatives even if it is a Bad Design Choice + // + // TODO: conditionalize this so we don't keep this around for "pure" + // Slang code + if (parser->LookAheadToken("void") && parser->LookAheadToken(TokenType::RParent, 1)) { - GenericDecl* decl = parser->astBuilder->create<GenericDecl>(); - parser->FillPosition(decl); - parser->PushScope(decl); - ParseGenericDeclImpl(parser, decl, [=](GenericDecl* genDecl) {return ParseSingleDecl(parser, genDecl); }); - parser->PopScope(); - return decl; + parser->ReadToken("void"); + parser->ReadToken(TokenType::RParent); + return; } - static void parseParameterList( - Parser* parser, - CallableDecl* decl) + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - parser->ReadToken(TokenType::LParent); + AddMember(decl, parser->ParseParameter()); + if (AdvanceIf(parser, TokenType::RParent)) + break; + parser->ReadToken(TokenType::Comma); + } +} - // Allow a declaration to use the keyword `void` for a parameter list, - // since that was required in ancient C, and continues to be supported - // in a bunch of its derivatives even if it is a Bad Design Choice - // - // TODO: conditionalize this so we don't keep this around for "pure" - // Slang code - if( parser->LookAheadToken("void") && parser->LookAheadToken(TokenType::RParent, 1) ) - { - parser->ReadToken("void"); - parser->ReadToken(TokenType::RParent); - return; - } +// systematically replace all scopes in an expression tree +class ReplaceScopeVisitor : public ExprVisitor<ReplaceScopeVisitor> +{ +public: + Scope* scope = nullptr; - while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - { - AddMember(decl, parser->ParseParameter()); - if (AdvanceIf(parser, TokenType::RParent)) - break; - parser->ReadToken(TokenType::Comma); - } + void visitDeclRefExpr(DeclRefExpr* expr) { expr->scope = scope; } + void visitGenericAppExpr(GenericAppExpr* expr) + { + expr->functionExpr->accept(this, nullptr); + for (auto arg : expr->arguments) + arg->accept(this, nullptr); } - - // systematically replace all scopes in an expression tree - class ReplaceScopeVisitor : public ExprVisitor<ReplaceScopeVisitor> + void visitIndexExpr(IndexExpr* expr) { - public: - Scope* scope = nullptr; - - void visitDeclRefExpr(DeclRefExpr* expr) - { - expr->scope = scope; - } - void visitGenericAppExpr(GenericAppExpr * expr) - { - expr->functionExpr->accept(this, nullptr); - for (auto arg : expr->arguments) - arg->accept(this, nullptr); - } - void visitIndexExpr(IndexExpr * expr) - { - expr->baseExpression->accept(this, nullptr); - for (auto arg : expr->indexExprs) - arg->accept(this, nullptr); - } - void visitMemberExpr(MemberExpr * expr) - { - expr->baseExpression->accept(this, nullptr); - expr->scope = scope; - } - void visitStaticMemberExpr(StaticMemberExpr * expr) - { - expr->baseExpression->accept(this, nullptr); - expr->scope = scope; - } - void visitAppExprBase(AppExprBase* expr) - { - expr->functionExpr->accept(this, nullptr); - for (auto arg : expr->arguments) - arg->accept(this, nullptr); - } - void visitIsTypeExpr(IsTypeExpr* expr) - { - if (expr->typeExpr.exp) - expr->typeExpr.exp->accept(this, nullptr); - } - void visitAsTypeExpr(AsTypeExpr* expr) - { - if (expr->typeExpr) - expr->typeExpr->accept(this, nullptr); - } - void visitSizeOfLikeExpr(SizeOfLikeExpr* expr) - { - if (expr->value) - expr->value->accept(this, nullptr); - } - void visitExpr(Expr* /*expr*/) - {} - }; - - /// Parse an optional body statement for a declaration that can have a body. - static Stmt* parseOptBody(Parser* parser) + expr->baseExpression->accept(this, nullptr); + for (auto arg : expr->indexExprs) + arg->accept(this, nullptr); + } + void visitMemberExpr(MemberExpr* expr) { - if (AdvanceIf(parser, TokenType::Semicolon)) - { - // empty body - return nullptr; - } - else - { - return parser->parseBlockStatement(); - } + expr->baseExpression->accept(this, nullptr); + expr->scope = scope; + } + void visitStaticMemberExpr(StaticMemberExpr* expr) + { + expr->baseExpression->accept(this, nullptr); + expr->scope = scope; + } + void visitAppExprBase(AppExprBase* expr) + { + expr->functionExpr->accept(this, nullptr); + for (auto arg : expr->arguments) + arg->accept(this, nullptr); + } + void visitIsTypeExpr(IsTypeExpr* expr) + { + if (expr->typeExpr.exp) + expr->typeExpr.exp->accept(this, nullptr); + } + void visitAsTypeExpr(AsTypeExpr* expr) + { + if (expr->typeExpr) + expr->typeExpr->accept(this, nullptr); + } + void visitSizeOfLikeExpr(SizeOfLikeExpr* expr) + { + if (expr->value) + expr->value->accept(this, nullptr); } + void visitExpr(Expr* /*expr*/) {} +}; - /// Complete parsing of a function using traditional (C-like) declarator syntax - static Decl* parseTraditionalFuncDecl( - Parser* parser, - DeclaratorInfo const& declaratorInfo) +/// Parse an optional body statement for a declaration that can have a body. +static Stmt* parseOptBody(Parser* parser) +{ + if (AdvanceIf(parser, TokenType::Semicolon)) { - FuncDecl* decl = parser->astBuilder->create<FuncDecl>(); - parser->FillPosition(decl); - decl->loc = declaratorInfo.nameAndLoc.loc; - decl->nameAndLoc = declaratorInfo.nameAndLoc; + // empty body + return nullptr; + } + else + { + return parser->parseBlockStatement(); + } +} - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) +/// Complete parsing of a function using traditional (C-like) declarator syntax +static Decl* parseTraditionalFuncDecl(Parser* parser, DeclaratorInfo const& declaratorInfo) +{ + FuncDecl* decl = parser->astBuilder->create<FuncDecl>(); + parser->FillPosition(decl); + decl->loc = declaratorInfo.nameAndLoc.loc; + decl->nameAndLoc = declaratorInfo.nameAndLoc; + + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) { // HACK: The return type of the function will already have been // parsed in a scope that didn't include the function's generic @@ -1858,650 +1809,630 @@ namespace Slang return decl; }); - } +} - static VarDeclBase* CreateVarDeclForContext( - ASTBuilder* astBuilder, - ContainerDecl* containerDecl ) +static VarDeclBase* CreateVarDeclForContext(ASTBuilder* astBuilder, ContainerDecl* containerDecl) +{ + if (as<CallableDecl>(containerDecl)) { - if (as<CallableDecl>(containerDecl)) - { - // Function parameters always use their dedicated syntax class. - // - return astBuilder->create<ParamDecl>(); - } - else - { - // Globals, locals, and member variables all use the same syntax class. - // - return astBuilder->create<VarDecl>(); - } + // Function parameters always use their dedicated syntax class. + // + return astBuilder->create<ParamDecl>(); } - - // Add modifiers to the end of the modifier list for a declaration - static void _addModifiers(Decl* decl, Modifiers const& modifiers) + else { - if (!modifiers.first) - return; - - Modifier** link = &decl->modifiers.first; - while (*link) - { - link = &(*link)->next; - } - *link = modifiers.first; + // Globals, locals, and member variables all use the same syntax class. + // + return astBuilder->create<VarDecl>(); } +} - static Name* generateName(Parser* parser, String const& base) - { - // TODO: somehow mangle the name to avoid clashes - return getName(parser, "SLANG_" + base); - } +// Add modifiers to the end of the modifier list for a declaration +static void _addModifiers(Decl* decl, Modifiers const& modifiers) +{ + if (!modifiers.first) + return; - static Name* generateName(Parser* parser) + Modifier** link = &decl->modifiers.first; + while (*link) { - return generateName(parser, "anonymous_" + String(parser->anonymousCounter++)); + link = &(*link)->next; } + *link = modifiers.first; +} +static Name* generateName(Parser* parser, String const& base) +{ + // TODO: somehow mangle the name to avoid clashes + return getName(parser, "SLANG_" + base); +} - // Set up a variable declaration based on what we saw in its declarator... - static void CompleteVarDecl( - Parser* parser, - VarDeclBase* decl, - DeclaratorInfo const& declaratorInfo) - { - parser->FillPosition(decl); +static Name* generateName(Parser* parser) +{ + return generateName(parser, "anonymous_" + String(parser->anonymousCounter++)); +} - if( !declaratorInfo.nameAndLoc.name ) - { - // HACK(tfoley): we always give a name, even if the declarator didn't include one... :( - decl->nameAndLoc = NameLoc(generateName(parser)); - } - else - { - decl->loc = declaratorInfo.nameAndLoc.loc; - decl->nameAndLoc = declaratorInfo.nameAndLoc; - } - decl->type = TypeExp(declaratorInfo.typeSpec); - _addModifiers(decl, declaratorInfo.semantics); +// Set up a variable declaration based on what we saw in its declarator... +static void CompleteVarDecl(Parser* parser, VarDeclBase* decl, DeclaratorInfo const& declaratorInfo) +{ + parser->FillPosition(decl); - decl->initExpr = declaratorInfo.initializer; + if (!declaratorInfo.nameAndLoc.name) + { + // HACK(tfoley): we always give a name, even if the declarator didn't include one... :( + decl->nameAndLoc = NameLoc(generateName(parser)); } - - typedef unsigned int DeclaratorParseOptions; - enum + else { - kDeclaratorParseOptions_None = 0, - kDeclaratorParseOption_AllowEmpty = 1 << 0, - }; + decl->loc = declaratorInfo.nameAndLoc.loc; + decl->nameAndLoc = declaratorInfo.nameAndLoc; + } + decl->type = TypeExp(declaratorInfo.typeSpec); + + _addModifiers(decl, declaratorInfo.semantics); - static RefPtr<Declarator> parseDeclarator( - Parser* parser, - DeclaratorParseOptions options); + decl->initExpr = declaratorInfo.initializer; +} + +typedef unsigned int DeclaratorParseOptions; +enum +{ + kDeclaratorParseOptions_None = 0, + kDeclaratorParseOption_AllowEmpty = 1 << 0, +}; - static RefPtr<Declarator> parseDirectAbstractDeclarator( - Parser* parser, - DeclaratorParseOptions options) +static RefPtr<Declarator> parseDeclarator(Parser* parser, DeclaratorParseOptions options); + +static RefPtr<Declarator> parseDirectAbstractDeclarator( + Parser* parser, + DeclaratorParseOptions options) +{ + RefPtr<Declarator> declarator; + switch (parser->tokenReader.peekTokenType()) { - RefPtr<Declarator> declarator; - switch( parser->tokenReader.peekTokenType() ) + case TokenType::Identifier: { - case TokenType::Identifier: - { - auto nameDeclarator = new NameDeclarator(); - nameDeclarator->flavor = Declarator::Flavor::name; - nameDeclarator->nameAndLoc = ParseDeclName(parser); - declarator = nameDeclarator; - } - break; + auto nameDeclarator = new NameDeclarator(); + nameDeclarator->flavor = Declarator::Flavor::name; + nameDeclarator->nameAndLoc = ParseDeclName(parser); + declarator = nameDeclarator; + } + break; - case TokenType::LParent: - { - // Note(tfoley): This is a point where disambiguation is required. - // We could be looking at an abstract declarator for a function-type - // parameter: - // - // void F( int(int) ); - // - // Or we could be looking at the use of parenthesese in an ordinary - // declarator: - // - // void (*f)(int); - // - // The difference really doesn't matter right now, but we err in - // the direction of assuming the second case. - // - // TODO: We should consider just not supporting this case at all, - // since it can't come up in current Slang (no pointer or function-type - // support), and we might be able to introduce alternative syntax - // to get around these issues when those features come online. - // - parser->ReadToken(TokenType::LParent); - declarator = parseDeclarator(parser, options); - parser->ReadMatchingToken(TokenType::RParent); - } - break; + case TokenType::LParent: + { + // Note(tfoley): This is a point where disambiguation is required. + // We could be looking at an abstract declarator for a function-type + // parameter: + // + // void F( int(int) ); + // + // Or we could be looking at the use of parenthesese in an ordinary + // declarator: + // + // void (*f)(int); + // + // The difference really doesn't matter right now, but we err in + // the direction of assuming the second case. + // + // TODO: We should consider just not supporting this case at all, + // since it can't come up in current Slang (no pointer or function-type + // support), and we might be able to introduce alternative syntax + // to get around these issues when those features come online. + // + parser->ReadToken(TokenType::LParent); + declarator = parseDeclarator(parser, options); + parser->ReadMatchingToken(TokenType::RParent); + } + break; - default: - if(options & kDeclaratorParseOption_AllowEmpty) - { - // an empty declarator is allowed - } - else - { - // If an empty declarator is now allowed, then we - // will give the user an error message saying that - // an identifier was expected. - // - expectIdentifier(parser); - } - return nullptr; + default: + if (options & kDeclaratorParseOption_AllowEmpty) + { + // an empty declarator is allowed + } + else + { + // If an empty declarator is now allowed, then we + // will give the user an error message saying that + // an identifier was expected. + // + expectIdentifier(parser); } + return nullptr; + } - // postifx additions - for( ;;) + // postifx additions + for (;;) + { + switch (parser->tokenReader.peekTokenType()) { - switch( parser->tokenReader.peekTokenType() ) + case TokenType::LBracket: { - case TokenType::LBracket: - { - auto arrayDeclarator = new ArrayDeclarator(); - arrayDeclarator->openBracketLoc = parser->tokenReader.peekLoc(); - arrayDeclarator->flavor = Declarator::Flavor::Array; - arrayDeclarator->inner = declarator; - - parser->ReadToken(TokenType::LBracket); - if( parser->tokenReader.peekTokenType() != TokenType::RBracket ) - { - arrayDeclarator->elementCountExpr = parser->ParseExpression(); - } - parser->ReadToken(TokenType::RBracket); + auto arrayDeclarator = new ArrayDeclarator(); + arrayDeclarator->openBracketLoc = parser->tokenReader.peekLoc(); + arrayDeclarator->flavor = Declarator::Flavor::Array; + arrayDeclarator->inner = declarator; - declarator = arrayDeclarator; - continue; + parser->ReadToken(TokenType::LBracket); + if (parser->tokenReader.peekTokenType() != TokenType::RBracket) + { + arrayDeclarator->elementCountExpr = parser->ParseExpression(); } + parser->ReadToken(TokenType::RBracket); - case TokenType::LParent: - break; + declarator = arrayDeclarator; + continue; + } + + case TokenType::LParent: break; - case TokenType::OpLess: + case TokenType::OpLess: + { + if (parser->options.enableEffectAnnotations) { - if (parser->options.enableEffectAnnotations) + // If we are in a context where effect annotations are allowed, + // then we need to disambiguate the content after "<" to see if it + // should be parsed as an annotation or generic argument list. + // If we can determine the content inside a `<>` is an annotation, + // we will skip the tokens inside the angle brackets. + // + if (parser->tokenReader.peekTokenType() == TokenType::OpLess) { - // If we are in a context where effect annotations are allowed, - // then we need to disambiguate the content after "<" to see if it - // should be parsed as an annotation or generic argument list. - // If we can determine the content inside a `<>` is an annotation, - // we will skip the tokens inside the angle brackets. - // - if (parser->tokenReader.peekTokenType() == TokenType::OpLess) + if (parser->LookAheadToken("let", 1)) { - if (parser->LookAheadToken("let", 1)) - { - // If we see `<let` then we are looking at a generic arg list. - } - else if (parser->LookAheadToken(":", 2)) + // If we see `<let` then we are looking at a generic arg list. + } + else if (parser->LookAheadToken(":", 2)) + { + // If we see a "< xxx :", we can also parse it as a generic arg list. + } + else + { + // Otherwise, we may be looking at an effect annotation. + // For now we just skip tokens until we see `>`, if we see any `;` in + // between, we can conclude that this is an annotation. + TokenReader tempReader = parser->tokenReader; + bool foundSemicolon = false; + while (tempReader.peekTokenType() != TokenType::OpGreater && + tempReader.peekTokenType() != TokenType::EndOfFile) { - // If we see a "< xxx :", we can also parse it as a generic arg list. + if (tempReader.peekTokenType() == TokenType::Semicolon) + foundSemicolon = true; + tempReader.advanceToken(); } - else + if (foundSemicolon) { - // Otherwise, we may be looking at an effect annotation. - // For now we just skip tokens until we see `>`, if we see any `;` in between, - // we can conclude that this is an annotation. - TokenReader tempReader = parser->tokenReader; - bool foundSemicolon = false; - while (tempReader.peekTokenType() != TokenType::OpGreater && - tempReader.peekTokenType() != TokenType::EndOfFile) - { - if (tempReader.peekTokenType() == TokenType::Semicolon) - foundSemicolon = true; - tempReader.advanceToken(); - } - if (foundSemicolon) - { - parser->tokenReader = tempReader; - parser->ReadToken(TokenType::OpGreater); - } + parser->tokenReader = tempReader; + parser->ReadToken(TokenType::OpGreater); } } } - } - break; - default: - break; } - break; + default: break; } - return declarator; + break; } - // Parse a declarator (or at least as much of one as we support) - static RefPtr<Declarator> parseDeclarator( - Parser* parser, - DeclaratorParseOptions options) + return declarator; +} + +// Parse a declarator (or at least as much of one as we support) +static RefPtr<Declarator> parseDeclarator(Parser* parser, DeclaratorParseOptions options) +{ + if (parser->tokenReader.peekTokenType() == TokenType::OpMul) { - if( parser->tokenReader.peekTokenType() == TokenType::OpMul ) - { - auto ptrDeclarator = new PointerDeclarator(); - ptrDeclarator->starLoc = parser->tokenReader.peekLoc(); - ptrDeclarator->flavor = Declarator::Flavor::Pointer; + auto ptrDeclarator = new PointerDeclarator(); + ptrDeclarator->starLoc = parser->tokenReader.peekLoc(); + ptrDeclarator->flavor = Declarator::Flavor::Pointer; - parser->ReadToken(TokenType::OpMul); + parser->ReadToken(TokenType::OpMul); - // TODO(tfoley): allow qualifiers like `const` here? + // TODO(tfoley): allow qualifiers like `const` here? - ptrDeclarator->inner = parseDeclarator(parser, options); - return ptrDeclarator; - } - else - { - return parseDirectAbstractDeclarator(parser, options); - } + ptrDeclarator->inner = parseDeclarator(parser, options); + return ptrDeclarator; } - - // A declarator plus optional semantics and initializer - struct InitDeclarator + else { - RefPtr<Declarator> declarator; - Modifiers semantics; - Expr* initializer = nullptr; - }; - - // Parse a declarator plus optional semantics - static InitDeclarator parseSemanticDeclarator( - Parser* parser, - DeclaratorParseOptions options) - { - InitDeclarator result; - result.declarator = parseDeclarator(parser, options); - result.semantics = _parseOptSemantics(parser); - return result; + return parseDirectAbstractDeclarator(parser, options); } +} + +// A declarator plus optional semantics and initializer +struct InitDeclarator +{ + RefPtr<Declarator> declarator; + Modifiers semantics; + Expr* initializer = nullptr; +}; - // Parse a declarator plus optional semantics and initializer - static InitDeclarator parseInitDeclarator( - Parser* parser, - DeclaratorParseOptions options) +// Parse a declarator plus optional semantics +static InitDeclarator parseSemanticDeclarator(Parser* parser, DeclaratorParseOptions options) +{ + InitDeclarator result; + result.declarator = parseDeclarator(parser, options); + result.semantics = _parseOptSemantics(parser); + return result; +} + +// Parse a declarator plus optional semantics and initializer +static InitDeclarator parseInitDeclarator(Parser* parser, DeclaratorParseOptions options) +{ + InitDeclarator result = parseSemanticDeclarator(parser, options); + if (AdvanceIf(parser, TokenType::OpAssign)) { - InitDeclarator result = parseSemanticDeclarator(parser, options); - if (AdvanceIf(parser, TokenType::OpAssign)) - { - result.initializer = parser->ParseInitExpr(); - } - return result; + result.initializer = parser->ParseInitExpr(); } + return result; +} - static void UnwrapDeclarator( - ASTBuilder* astBuilder, - RefPtr<Declarator> declarator, - DeclaratorInfo* ioInfo) +static void UnwrapDeclarator( + ASTBuilder* astBuilder, + RefPtr<Declarator> declarator, + DeclaratorInfo* ioInfo) +{ + while (declarator) { - while( declarator ) + switch (declarator->flavor) { - switch(declarator->flavor) + case Declarator::Flavor::name: { - case Declarator::Flavor::name: - { - auto nameDeclarator = (NameDeclarator*) declarator.Ptr(); - ioInfo->nameAndLoc = nameDeclarator->nameAndLoc; - return; - } - break; - - case Declarator::Flavor::Pointer: - { - auto ptrDeclarator = (PointerDeclarator*) declarator.Ptr(); - auto ptrTypeExpr = astBuilder->create<PointerTypeExpr>(); - ptrTypeExpr->loc = ptrDeclarator->starLoc; - ptrTypeExpr->base.exp = ioInfo->typeSpec; - ioInfo->typeSpec = ptrTypeExpr; + auto nameDeclarator = (NameDeclarator*)declarator.Ptr(); + ioInfo->nameAndLoc = nameDeclarator->nameAndLoc; + return; + } + break; - declarator = ptrDeclarator->inner; - } - break; + case Declarator::Flavor::Pointer: + { + auto ptrDeclarator = (PointerDeclarator*)declarator.Ptr(); + auto ptrTypeExpr = astBuilder->create<PointerTypeExpr>(); + ptrTypeExpr->loc = ptrDeclarator->starLoc; + ptrTypeExpr->base.exp = ioInfo->typeSpec; + ioInfo->typeSpec = ptrTypeExpr; - case Declarator::Flavor::Array: - { - // TODO(tfoley): we don't support pointers for now - auto arrayDeclarator = (ArrayDeclarator*) declarator.Ptr(); + declarator = ptrDeclarator->inner; + } + break; - auto arrayTypeExpr = astBuilder->create<IndexExpr>(); - arrayTypeExpr->loc = arrayDeclarator->openBracketLoc; - arrayTypeExpr->baseExpression = ioInfo->typeSpec; - if (arrayDeclarator->elementCountExpr) - arrayTypeExpr->indexExprs.add(arrayDeclarator->elementCountExpr); - ioInfo->typeSpec = arrayTypeExpr; + case Declarator::Flavor::Array: + { + // TODO(tfoley): we don't support pointers for now + auto arrayDeclarator = (ArrayDeclarator*)declarator.Ptr(); - declarator = arrayDeclarator->inner; - } - break; + auto arrayTypeExpr = astBuilder->create<IndexExpr>(); + arrayTypeExpr->loc = arrayDeclarator->openBracketLoc; + arrayTypeExpr->baseExpression = ioInfo->typeSpec; + if (arrayDeclarator->elementCountExpr) + arrayTypeExpr->indexExprs.add(arrayDeclarator->elementCountExpr); + ioInfo->typeSpec = arrayTypeExpr; - default: - SLANG_UNREACHABLE("all cases handled"); - break; + declarator = arrayDeclarator->inner; } + break; + + default: SLANG_UNREACHABLE("all cases handled"); break; } } +} - static void UnwrapDeclarator( - ASTBuilder* astBuilder, - InitDeclarator const& initDeclarator, - DeclaratorInfo* ioInfo) - { - UnwrapDeclarator(astBuilder, initDeclarator.declarator, ioInfo); - ioInfo->semantics = initDeclarator.semantics; - ioInfo->initializer = initDeclarator.initializer; - } +static void UnwrapDeclarator( + ASTBuilder* astBuilder, + InitDeclarator const& initDeclarator, + DeclaratorInfo* ioInfo) +{ + UnwrapDeclarator(astBuilder, initDeclarator.declarator, ioInfo); + ioInfo->semantics = initDeclarator.semantics; + ioInfo->initializer = initDeclarator.initializer; +} - // Either a single declaration, or a group of them - struct DeclGroupBuilder +// Either a single declaration, or a group of them +struct DeclGroupBuilder +{ + SourceLoc startPosition; + Decl* decl = nullptr; + DeclGroup* group = nullptr; + ASTBuilder* astBuilder = nullptr; + + // Add a new declaration to the potential group + void addDecl(Decl* newDecl) { - SourceLoc startPosition; - Decl* decl = nullptr; - DeclGroup* group = nullptr; - ASTBuilder* astBuilder = nullptr; + SLANG_ASSERT(newDecl); - // Add a new declaration to the potential group - void addDecl( - Decl* newDecl) + if (decl) { - SLANG_ASSERT(newDecl); - - if( decl ) - { - group = astBuilder->create<DeclGroup>(); - group->loc = startPosition; - group->decls.add(decl); - decl = nullptr; - } - - if( group ) - { - group->decls.add(newDecl); - } - else - { - decl = newDecl; - } + group = astBuilder->create<DeclGroup>(); + group->loc = startPosition; + group->decls.add(decl); + decl = nullptr; } - DeclBase* getResult() + if (group) { - if(group) return group; - return decl; + group->decls.add(newDecl); } - }; + else + { + decl = newDecl; + } + } - // Create a type expression that will refer to the given declaration - static Expr* - createDeclRefType(Parser* parser, Decl* decl) + DeclBase* getResult() { - // For now we just construct an expression that - // will look up the given declaration by name. - // - // TODO: do this better, e.g. by filling in the `declRef` field directly - - auto expr = parser->astBuilder->create<VarExpr>(); - expr->scope = parser->currentScope; - expr->loc = decl->getNameLoc(); - expr->name = decl->getName(); - return expr; + if (group) + return group; + return decl; } +}; - // Representation for a parsed type specifier, which might - // include a declaration (e.g., of a `struct` type) - struct TypeSpec - { - // If the type-spec declared something, then put it here - Decl* decl = nullptr; +// Create a type expression that will refer to the given declaration +static Expr* createDeclRefType(Parser* parser, Decl* decl) +{ + // For now we just construct an expression that + // will look up the given declaration by name. + // + // TODO: do this better, e.g. by filling in the `declRef` field directly - // Put the resulting expression (which should evaluate to a type) here - Expr* expr = nullptr; - }; + auto expr = parser->astBuilder->create<VarExpr>(); + expr->scope = parser->currentScope; + expr->loc = decl->getNameLoc(); + expr->name = decl->getName(); + return expr; +} - static Expr* parseGenericApp( - Parser* parser, - Expr* base) - { - GenericAppExpr* genericApp = parser->astBuilder->create<GenericAppExpr>(); +// Representation for a parsed type specifier, which might +// include a declaration (e.g., of a `struct` type) +struct TypeSpec +{ + // If the type-spec declared something, then put it here + Decl* decl = nullptr; - genericApp->loc = base->loc; - genericApp->functionExpr = base; - parser->ReadToken(TokenType::OpLess); - parser->genericDepth++; - // For now assume all generics have at least one argument - genericApp->arguments.add(_parseGenericArg(parser)); - while (AdvanceIf(parser, TokenType::Comma)) - { - genericApp->arguments.add(_parseGenericArg(parser)); - } - parser->genericDepth--; + // Put the resulting expression (which should evaluate to a type) here + Expr* expr = nullptr; +}; - if (parser->tokenReader.peekToken().type == TokenType::OpRsh) - { - parser->tokenReader.peekToken().type = TokenType::OpGreater; - parser->tokenReader.peekToken().loc.setRaw(parser->tokenReader.peekToken().loc.getRaw() + 1); - } - else if (parser->LookAheadToken(TokenType::OpGreater)) - parser->ReadToken(TokenType::OpGreater); - else - parser->sink->diagnose(parser->tokenReader.peekToken(), Diagnostics::tokenTypeExpected, "'>'"); - return genericApp; - } +static Expr* parseGenericApp(Parser* parser, Expr* base) +{ + GenericAppExpr* genericApp = parser->astBuilder->create<GenericAppExpr>(); - static bool isGenericName(Parser* parser, Name* name) + genericApp->loc = base->loc; + genericApp->functionExpr = base; + parser->ReadToken(TokenType::OpLess); + parser->genericDepth++; + // For now assume all generics have at least one argument + genericApp->arguments.add(_parseGenericArg(parser)); + while (AdvanceIf(parser, TokenType::Comma)) { - auto lookupResult = lookUp( - parser->astBuilder, - nullptr, // no semantics visitor available yet - name, - parser->currentScope); - if (!lookupResult.isValid() || lookupResult.isOverloaded()) - return false; - - return lookupResult.item.declRef.is<GenericDecl>(); + genericApp->arguments.add(_parseGenericArg(parser)); } + parser->genericDepth--; - static Expr* tryParseGenericApp( - Parser* parser, - Expr* base) + if (parser->tokenReader.peekToken().type == TokenType::OpRsh) { - Name * baseName = nullptr; - if (auto varExpr = as<VarExpr>(base)) - baseName = varExpr->name; - // if base is a known generics, parse as generics - if (baseName && isGenericName(parser, baseName)) - return parseGenericApp(parser, base); + parser->tokenReader.peekToken().type = TokenType::OpGreater; + parser->tokenReader.peekToken().loc.setRaw( + parser->tokenReader.peekToken().loc.getRaw() + 1); + } + else if (parser->LookAheadToken(TokenType::OpGreater)) + parser->ReadToken(TokenType::OpGreater); + else + parser->sink->diagnose( + parser->tokenReader.peekToken(), + Diagnostics::tokenTypeExpected, + "'>'"); + return genericApp; +} - // otherwise, we speculate as generics, and fallback to comparison when parsing failed - TokenSpan tokenSpan; - tokenSpan.m_begin = parser->tokenReader.m_cursor; - tokenSpan.m_end = parser->tokenReader.m_end; +static bool isGenericName(Parser* parser, Name* name) +{ + auto lookupResult = lookUp( + parser->astBuilder, + nullptr, // no semantics visitor available yet + name, + parser->currentScope); + if (!lookupResult.isValid() || lookupResult.isOverloaded()) + return false; - // Setup without diagnostic lexer, or SourceLocationLine output - // as this sink is just to *try* generic application - DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr); + return lookupResult.item.declRef.is<GenericDecl>(); +} + +static Expr* tryParseGenericApp(Parser* parser, Expr* base) +{ + Name* baseName = nullptr; + if (auto varExpr = as<VarExpr>(base)) + baseName = varExpr->name; + // if base is a known generics, parse as generics + if (baseName && isGenericName(parser, baseName)) + return parseGenericApp(parser, base); + + // otherwise, we speculate as generics, and fallback to comparison when parsing failed + TokenSpan tokenSpan; + tokenSpan.m_begin = parser->tokenReader.m_cursor; + tokenSpan.m_end = parser->tokenReader.m_end; - Parser newParser(*parser); - newParser.sink = &newSink; + // Setup without diagnostic lexer, or SourceLocationLine output + // as this sink is just to *try* generic application + DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr); - /* auto speculateParseRs = */parseGenericApp(&newParser, base); + Parser newParser(*parser); + newParser.sink = &newSink; - if (newSink.getErrorCount() == 0) + /* auto speculateParseRs = */ parseGenericApp(&newParser, base); + + if (newSink.getErrorCount() == 0) + { + // disambiguate based on FOLLOW set + switch (peekTokenType(&newParser)) { - // disambiguate based on FOLLOW set - switch (peekTokenType(&newParser)) - { - case TokenType::Scope: - case TokenType::Dot: - case TokenType::LParent: - case TokenType::RParent: - case TokenType::RBracket: - case TokenType::Colon: - case TokenType::Comma: - case TokenType::QuestionMark: - case TokenType::Semicolon: - case TokenType::OpEql: - case TokenType::OpNeq: - case TokenType::OpGreater: - case TokenType::EndOfFile: + case TokenType::Scope: + case TokenType::Dot: + case TokenType::LParent: + case TokenType::RParent: + case TokenType::RBracket: + case TokenType::Colon: + case TokenType::Comma: + case TokenType::QuestionMark: + case TokenType::Semicolon: + case TokenType::OpEql: + case TokenType::OpNeq: + case TokenType::OpGreater: + case TokenType::EndOfFile: { return parseGenericApp(parser, base); } - } } - return base; - } - static Expr* parseMemberType(Parser * parser, Expr* base, SourceLoc opLoc) - { - // When called the :: or . have been consumed, so don't need to consume here. - - MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>(); - memberExpr->memberOperatorLoc = opLoc; - parser->FillPosition(memberExpr); - memberExpr->baseExpression = base; - memberExpr->name = expectIdentifier(parser).name; - return memberExpr; } - static Expr* parseStaticMemberType(Parser* parser, Expr* base, SourceLoc opLoc) - { - // When called the :: or . have been consumed, so don't need to consume here. + return base; +} +static Expr* parseMemberType(Parser* parser, Expr* base, SourceLoc opLoc) +{ + // When called the :: or . have been consumed, so don't need to consume here. + + MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>(); + memberExpr->memberOperatorLoc = opLoc; + parser->FillPosition(memberExpr); + memberExpr->baseExpression = base; + memberExpr->name = expectIdentifier(parser).name; + return memberExpr; +} +static Expr* parseStaticMemberType(Parser* parser, Expr* base, SourceLoc opLoc) +{ + // When called the :: or . have been consumed, so don't need to consume here. + + StaticMemberExpr* memberExpr = parser->astBuilder->create<StaticMemberExpr>(); + memberExpr->memberOperatorLoc = opLoc; + parser->FillPosition(memberExpr); + memberExpr->baseExpression = base; + memberExpr->name = expectIdentifier(parser).name; + return memberExpr; +} - StaticMemberExpr* memberExpr = parser->astBuilder->create<StaticMemberExpr>(); - memberExpr->memberOperatorLoc = opLoc; - parser->FillPosition(memberExpr); - memberExpr->baseExpression = base; - memberExpr->name = expectIdentifier(parser).name; - return memberExpr; - } - - // Parse optional `[]` braces after a type expression, that indicate an array type - static Expr* parseBracketTypeSuffix(Parser* parser, Expr* inTypeExpr) +// Parse optional `[]` braces after a type expression, that indicate an array type +static Expr* parseBracketTypeSuffix(Parser* parser, Expr* inTypeExpr) +{ + auto typeExpr = inTypeExpr; + for (;;) { - auto typeExpr = inTypeExpr; - for (;;) + Token token; + if (parser->LookAheadToken(TokenType::LBracket)) { - Token token; - if (parser->LookAheadToken(TokenType::LBracket)) + IndexExpr* arrType = parser->astBuilder->create<IndexExpr>(); + arrType->loc = typeExpr->loc; + arrType->baseExpression = typeExpr; + parser->ReadToken(TokenType::LBracket); + if (!parser->LookAheadToken(TokenType::RBracket)) { - IndexExpr* arrType = parser->astBuilder->create<IndexExpr>(); - arrType->loc = typeExpr->loc; - arrType->baseExpression = typeExpr; - parser->ReadToken(TokenType::LBracket); - if (!parser->LookAheadToken(TokenType::RBracket)) - { - arrType->indexExprs.add(parser->ParseExpression()); - } - parser->ReadToken(TokenType::RBracket); - typeExpr = arrType; + arrType->indexExprs.add(parser->ParseExpression()); } - else - break; + parser->ReadToken(TokenType::RBracket); + typeExpr = arrType; } - return typeExpr; + else + break; } + return typeExpr; +} - // Parse option `[]` or `*` braces after a type expression, that indicate an array or pointer type - static Expr* parsePostfixTypeSuffix( - Parser* parser, - Expr* inTypeExpr) +// Parse option `[]` or `*` braces after a type expression, that indicate an array or pointer type +static Expr* parsePostfixTypeSuffix(Parser* parser, Expr* inTypeExpr) +{ + auto typeExpr = inTypeExpr; + for (;;) { - auto typeExpr = inTypeExpr; - for (;;) + Token token; + if (parser->LookAheadToken(TokenType::LBracket)) { - Token token; - if (parser->LookAheadToken(TokenType::LBracket)) - { - IndexExpr* arrType = parser->astBuilder->create<IndexExpr>(); - arrType->loc = typeExpr->loc; - arrType->baseExpression = typeExpr; - parser->ReadToken(TokenType::LBracket); - if (!parser->LookAheadToken(TokenType::RBracket)) - { - arrType->indexExprs.add(parser->ParseExpression()); - } - parser->ReadToken(TokenType::RBracket); - typeExpr = arrType; - } - else if (AdvanceIf(parser, TokenType::OpMul, &token)) + IndexExpr* arrType = parser->astBuilder->create<IndexExpr>(); + arrType->loc = typeExpr->loc; + arrType->baseExpression = typeExpr; + parser->ReadToken(TokenType::LBracket); + if (!parser->LookAheadToken(TokenType::RBracket)) { - PointerTypeExpr* ptrType = parser->astBuilder->create<PointerTypeExpr>(); - ptrType->loc = token.loc; - ptrType->base.exp = typeExpr; - typeExpr = ptrType; + arrType->indexExprs.add(parser->ParseExpression()); } - else - break; + parser->ReadToken(TokenType::RBracket); + typeExpr = arrType; } - return typeExpr; + else if (AdvanceIf(parser, TokenType::OpMul, &token)) + { + PointerTypeExpr* ptrType = parser->astBuilder->create<PointerTypeExpr>(); + ptrType->loc = token.loc; + ptrType->base.exp = typeExpr; + typeExpr = ptrType; + } + else + break; } - /// Parse an expression of the form __fwd_diff(fn) where fn is an - /// identifier pointing to a function. - static Expr* parseForwardDifferentiate(Parser* parser) - { - ForwardDifferentiateExpr* jvpExpr = parser->astBuilder->create<ForwardDifferentiateExpr>(); + return typeExpr; +} +/// Parse an expression of the form __fwd_diff(fn) where fn is an +/// identifier pointing to a function. +static Expr* parseForwardDifferentiate(Parser* parser) +{ + ForwardDifferentiateExpr* jvpExpr = parser->astBuilder->create<ForwardDifferentiateExpr>(); - parser->ReadToken(TokenType::LParent); + parser->ReadToken(TokenType::LParent); - jvpExpr->baseFunction = parser->ParseExpression(); + jvpExpr->baseFunction = parser->ParseExpression(); - parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::RParent); - return jvpExpr; - } + return jvpExpr; +} - static NodeBase* parseForwardDifferentiate(Parser* parser, void* /* unused */) - { - return parseForwardDifferentiate(parser); - } +static NodeBase* parseForwardDifferentiate(Parser* parser, void* /* unused */) +{ + return parseForwardDifferentiate(parser); +} - /// Parse an expression of the form __bwd_diff(fn) where fn is an - /// identifier pointing to a function. - static Expr* parseBackwardDifferentiate(Parser* parser) - { - BackwardDifferentiateExpr* bwdDiffExpr = parser->astBuilder->create<BackwardDifferentiateExpr>(); +/// Parse an expression of the form __bwd_diff(fn) where fn is an +/// identifier pointing to a function. +static Expr* parseBackwardDifferentiate(Parser* parser) +{ + BackwardDifferentiateExpr* bwdDiffExpr = + parser->astBuilder->create<BackwardDifferentiateExpr>(); - parser->ReadToken(TokenType::LParent); + parser->ReadToken(TokenType::LParent); - bwdDiffExpr->baseFunction = parser->ParseExpression(); + bwdDiffExpr->baseFunction = parser->ParseExpression(); - parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::RParent); - return bwdDiffExpr; - } + return bwdDiffExpr; +} - static NodeBase* parseBackwardDifferentiate(Parser* parser, void* /* unused */) - { - return parseBackwardDifferentiate(parser); - } +static NodeBase* parseBackwardDifferentiate(Parser* parser, void* /* unused */) +{ + return parseBackwardDifferentiate(parser); +} - static Expr* parseDispatchKernel(Parser* parser) - { - DispatchKernelExpr* dispatchExpr = parser->astBuilder->create<DispatchKernelExpr>(); +static Expr* parseDispatchKernel(Parser* parser) +{ + DispatchKernelExpr* dispatchExpr = parser->astBuilder->create<DispatchKernelExpr>(); - parser->ReadToken(TokenType::LParent); + parser->ReadToken(TokenType::LParent); - dispatchExpr->baseFunction = parser->ParseArgExpr(); - parser->ReadToken(TokenType::Comma); - dispatchExpr->dispatchSize = parser->ParseArgExpr(); - parser->ReadToken(TokenType::Comma); - dispatchExpr->threadGroupSize = parser->ParseArgExpr(); - parser->ReadToken(TokenType::RParent); + dispatchExpr->baseFunction = parser->ParseArgExpr(); + parser->ReadToken(TokenType::Comma); + dispatchExpr->dispatchSize = parser->ParseArgExpr(); + parser->ReadToken(TokenType::Comma); + dispatchExpr->threadGroupSize = parser->ParseArgExpr(); + parser->ReadToken(TokenType::RParent); - return dispatchExpr; - } + return dispatchExpr; +} - static NodeBase* parseDispatchKernel(Parser* parser, void* /* unused */) - { - return parseDispatchKernel(parser); - } +static NodeBase* parseDispatchKernel(Parser* parser, void* /* unused */) +{ + return parseDispatchKernel(parser); +} - // (a,b,c) style tuples, curently unused +// (a,b,c) style tuples, curently unused #if 0 static Expr* parseTupleTypeExpr(Parser* parser) { @@ -2518,1622 +2449,1627 @@ namespace Slang } #endif - static Expr* parseFuncTypeExpr(Parser* parser) +static Expr* parseFuncTypeExpr(Parser* parser) +{ + parser->ReadToken(TokenType::LParent); + auto expr = parser->astBuilder->create<FuncTypeExpr>(); + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - parser->ReadToken(TokenType::LParent); - auto expr = parser->astBuilder->create<FuncTypeExpr>(); - while(!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - { - expr->parameters.add(parser->ParseTypeExp()); - if(AdvanceIf(parser, TokenType::RParent)) - break; - parser->ReadToken(TokenType::Comma); - } - parser->ReadToken(TokenType::RightArrow); - expr->result = parser->ParseTypeExp(); - return expr; + expr->parameters.add(parser->ParseTypeExp()); + if (AdvanceIf(parser, TokenType::RParent)) + break; + parser->ReadToken(TokenType::Comma); } + parser->ReadToken(TokenType::RightArrow); + expr->result = parser->ParseTypeExp(); + return expr; +} - /// Apply the given `modifiers` (if any) to the given `typeExpr` - static Expr* _applyModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers const& modifiers) +/// Apply the given `modifiers` (if any) to the given `typeExpr` +static Expr* _applyModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers const& modifiers) +{ + if (modifiers.first) { - if(modifiers.first) - { - // Currently, we represent a type with modifiers applied to it as - // an AST node of the `ModifiedTypeExpr` class. We will create - // one here and make it be the home for our `typeModifiers`. - // - ModifiedTypeExpr* modifiedTypeExpr = parser->astBuilder->create<ModifiedTypeExpr>(); - modifiedTypeExpr->base.exp = typeExpr; - modifiedTypeExpr->modifiers = modifiers; - return modifiedTypeExpr; - } - else - { - // If none of the modifiers were type modifiers, we can leave - // the existing type expression alone. - return typeExpr; - } + // Currently, we represent a type with modifiers applied to it as + // an AST node of the `ModifiedTypeExpr` class. We will create + // one here and make it be the home for our `typeModifiers`. + // + ModifiedTypeExpr* modifiedTypeExpr = parser->astBuilder->create<ModifiedTypeExpr>(); + modifiedTypeExpr->base.exp = typeExpr; + modifiedTypeExpr->modifiers = modifiers; + return modifiedTypeExpr; + } + else + { + // If none of the modifiers were type modifiers, we can leave + // the existing type expression alone. + return typeExpr; } +} - /// Apply any type modifier in `ioBaseModifiers` to the given `typeExpr`. - /// - /// If any type modifiers were present, `ioBaseModifiers` will be updated - /// to only include those modifiers that were not type modifiers (if any). - /// - /// If no type modifiers were present, `ioBaseModifiers` will remain unchanged. - /// - static Expr* _applyTypeModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers& ioBaseModifiers) +/// Apply any type modifier in `ioBaseModifiers` to the given `typeExpr`. +/// +/// If any type modifiers were present, `ioBaseModifiers` will be updated +/// to only include those modifiers that were not type modifiers (if any). +/// +/// If no type modifiers were present, `ioBaseModifiers` will remain unchanged. +/// +static Expr* _applyTypeModifiersToTypeExpr( + Parser* parser, + Expr* typeExpr, + Modifiers& ioBaseModifiers) +{ + // The `Modifiers` that were passed in as `ioBaseModifiers` comprise + // a singly-linked list of `Modifier` nodes. + // + // It is possible that some of these modifiers represent type modifiers and, + // if so, we want to transfer those modifiers to apply to the type given + // by `typeExpr`. Any remaining modifiers that are not type modifiers will + // be left in the `ioBaseModifiers` list. + // + // The type modifiers will be collected into their own `Modifiers` list, + // and we will retain a poiner to the final pointer in the linked list + // (the one that is null), so that we can append to the end. + // + Modifiers typeModifiers; + Modifier** typeModifierLink = &typeModifiers.first; + + // While iterating over the base modifiers, we need to be able to remove + // a linked-list node while inspecting it, so we will similarly keep a "link" + // variable that points at whatever location points to the current node + // (either the head of the list, or the `next` pointer in the previous modifier) + // + Modifier** baseModifierLink = &ioBaseModifiers.first; + while (auto baseModifier = *baseModifierLink) { - // The `Modifiers` that were passed in as `ioBaseModifiers` comprise - // a singly-linked list of `Modifier` nodes. + // We want to detect whether we have a type modifier or not. // - // It is possible that some of these modifiers represent type modifiers and, - // if so, we want to transfer those modifiers to apply to the type given - // by `typeExpr`. Any remaining modifiers that are not type modifiers will - // be left in the `ioBaseModifiers` list. - // - // The type modifiers will be collected into their own `Modifiers` list, - // and we will retain a poiner to the final pointer in the linked list - // (the one that is null), so that we can append to the end. - // - Modifiers typeModifiers; - Modifier** typeModifierLink = &typeModifiers.first; + auto typeModifier = as<TypeModifier>(baseModifier); - // While iterating over the base modifiers, we need to be able to remove - // a linked-list node while inspecting it, so we will similarly keep a "link" - // variable that points at whatever location points to the current node - // (either the head of the list, or the `next` pointer in the previous modifier) + // The easy case is when we *don't* have a type modifier. // - Modifier** baseModifierLink = &ioBaseModifiers.first; - while(auto baseModifier = *baseModifierLink) + if (!typeModifier) { - // We want to detect whether we have a type modifier or not. + // We want to leave the modifier where it is (in the list + // of "base" modifiers), and advance to the next one in order. // - auto typeModifier = as<TypeModifier>(baseModifier); - - // The easy case is when we *don't* have a type modifier. + baseModifierLink = &baseModifier->next; + } + else + { + // If we have a type modifier, we need to graft it onto + // the list of type modifiers. This is done by writing + // a pointer to the type modifier into the "link" for + // the type modifier list, and updating the link to point + // to the `next` field of the current modifier (since that + // fill be the location any further type modifiers need + // to be linked). // - if(!typeModifier) - { - // We want to leave the modifier where it is (in the list - // of "base" modifiers), and advance to the next one in order. - // - baseModifierLink = &baseModifier->next; - } - else - { - // If we have a type modifier, we need to graft it onto - // the list of type modifiers. This is done by writing - // a pointer to the type modifier into the "link" for - // the type modifier list, and updating the link to point - // to the `next` field of the current modifier (since that - // fill be the location any further type modifiers need - // to be linked). - // - *typeModifierLink = typeModifier; - typeModifierLink = &typeModifier->next; - - // The above logic puts `typeModifier` into the type modifer - // list, but it doesn't remove it from the base modifier list. - // In order to do that we must replace the pointer to `typeModifer` - // with a pointer to whatever is next in the base list, and also - // null out the `next` field of `typeModifier` so that it no - // longer points to the base modifiers that come after it. - // - *baseModifierLink = typeModifier->next; - typeModifier->next = nullptr; + *typeModifierLink = typeModifier; + typeModifierLink = &typeModifier->next; + + // The above logic puts `typeModifier` into the type modifer + // list, but it doesn't remove it from the base modifier list. + // In order to do that we must replace the pointer to `typeModifer` + // with a pointer to whatever is next in the base list, and also + // null out the `next` field of `typeModifier` so that it no + // longer points to the base modifiers that come after it. + // + *baseModifierLink = typeModifier->next; + typeModifier->next = nullptr; - // Note: We do *not* need to update `baseModifierLink` before - // the next loop iteration, because `*baseModifierLink` has - // already been updated so that it points to the next node - // we want to visit. - } + // Note: We do *not* need to update `baseModifierLink` before + // the next loop iteration, because `*baseModifierLink` has + // already been updated so that it points to the next node + // we want to visit. } + } + + // If we ended up finding any type modifiers, we want to apply them + // to the type expression. + // + return _applyModifiersToTypeExpr(parser, typeExpr, typeModifiers); +} - // If we ended up finding any type modifiers, we want to apply them - // to the type expression. +static TypeSpec _applyModifiersToTypeSpec( + Parser* parser, + TypeSpec typeSpec, + Modifiers const& inModifiers) +{ + // It is possible that the form of the type specifier will have + // included a declaration directly (e.g., using `struct { ... }` + // as a type specifier to declare both a type and value(s) of that + // type in one go). + // + if (auto decl = typeSpec.decl) + { + // In the case where there *is* a declaration, we want to apply + // any modifiers that logically belong to the type to the type, + // and any modifiers that logically belong to the declaration to + // the declaration. // - return _applyModifiersToTypeExpr(parser, typeExpr, typeModifiers); - } + Modifiers modifiers = inModifiers; + typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, modifiers); - static TypeSpec _applyModifiersToTypeSpec(Parser* parser, TypeSpec typeSpec, Modifiers const& inModifiers) + // Any remaining modifiers should instead be applied to the declaration. + _addModifiers(decl, modifiers); + } + else { - // It is possible that the form of the type specifier will have - // included a declaration directly (e.g., using `struct { ... }` - // as a type specifier to declare both a type and value(s) of that - // type in one go). + // If there are modifiers, then we apply *all* of them to the type expression. + // This may result in modifiers being applied that do not belong on a type; + // in that case we rely on downstream semantic checking to diagnose any error. // - if(auto decl = typeSpec.decl) - { - // In the case where there *is* a declaration, we want to apply - // any modifiers that logically belong to the type to the type, - // and any modifiers that logically belong to the declaration to - // the declaration. - // - Modifiers modifiers = inModifiers; - typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, modifiers); + typeSpec.expr = _applyModifiersToTypeExpr(parser, typeSpec.expr, inModifiers); + } - // Any remaining modifiers should instead be applied to the declaration. - _addModifiers(decl, modifiers); - } - else - { - // If there are modifiers, then we apply *all* of them to the type expression. - // This may result in modifiers being applied that do not belong on a type; - // in that case we rely on downstream semantic checking to diagnose any error. - // - typeSpec.expr = _applyModifiersToTypeExpr(parser, typeSpec.expr, inModifiers); - } + return typeSpec; +} +/// Parse a type specifier, without dealing with modifiers. +static TypeSpec _parseSimpleTypeSpec(Parser* parser) +{ + TypeSpec typeSpec; + + // We may see a `struct` (or `enum` or `class`) tag specified here, and need to act accordingly + // + // TODO(tfoley): Handle the case where the user is just using `struct` + // as a way to name an existing struct "tag" (e.g., `struct Foo foo;`) + // + // TODO: We should really make these keywords be registered like any other + // syntax category, rather than be special-cased here. The main issue here + // is that we need to allow them to be used as type specifiers, as in: + // + // struct Foo { int x } foo; + // + // The ideal answer would be to register certain keywords as being able + // to parse a type specifier, and look for those keywords here. + // We should ideally add special case logic that bails out of declarator + // parsing iff we have one of these kinds of type specifiers and the + // closing `}` is at the end of its line, as a bit of a special case + // to allow the common idiom. + // + if (parser->LookAheadToken("struct")) + { + auto decl = parser->ParseStruct(); + typeSpec.decl = decl; + typeSpec.expr = createDeclRefType(parser, decl); return typeSpec; } - - /// Parse a type specifier, without dealing with modifiers. - static TypeSpec _parseSimpleTypeSpec(Parser* parser) + else if (parser->LookAheadToken("class")) + { + auto decl = parser->ParseClass(); + typeSpec.decl = decl; + typeSpec.expr = createDeclRefType(parser, decl); + return typeSpec; + } + else if (parser->LookAheadToken("enum")) + { + auto decl = parseEnumDecl(parser); + typeSpec.decl = decl; + typeSpec.expr = createDeclRefType(parser, decl); + return typeSpec; + } + else if (parser->LookAheadToken("expand") || parser->LookAheadToken("each")) { - TypeSpec typeSpec; + typeSpec.expr = parsePrefixExpr(parser); + return typeSpec; + } + // Uncomment should we decide to enable (a,b,c) tuple types + // else if(parser->LookAheadToken(TokenType::LParent)) + // { + // typeSpec.expr = parseTupleTypeExpr(parser); + // return typeSpec; + // } + else if (AdvanceIf(parser, "functype")) + { + typeSpec.expr = parseFuncTypeExpr(parser); + return typeSpec; + } - // We may see a `struct` (or `enum` or `class`) tag specified here, and need to act accordingly - // - // TODO(tfoley): Handle the case where the user is just using `struct` - // as a way to name an existing struct "tag" (e.g., `struct Foo foo;`) - // - // TODO: We should really make these keywords be registered like any other - // syntax category, rather than be special-cased here. The main issue here - // is that we need to allow them to be used as type specifiers, as in: - // - // struct Foo { int x } foo; - // - // The ideal answer would be to register certain keywords as being able - // to parse a type specifier, and look for those keywords here. - // We should ideally add special case logic that bails out of declarator - // parsing iff we have one of these kinds of type specifiers and the - // closing `}` is at the end of its line, as a bit of a special case - // to allow the common idiom. - // - if( parser->LookAheadToken("struct") ) - { - auto decl = parser->ParseStruct(); - typeSpec.decl = decl; - typeSpec.expr = createDeclRefType(parser, decl); - return typeSpec; - } - else if( parser->LookAheadToken("class") ) - { - auto decl = parser->ParseClass(); - typeSpec.decl = decl; - typeSpec.expr = createDeclRefType(parser, decl); - return typeSpec; - } - else if(parser->LookAheadToken("enum")) - { - auto decl = parseEnumDecl(parser); - typeSpec.decl = decl; - typeSpec.expr = createDeclRefType(parser, decl); - return typeSpec; - } - else if (parser->LookAheadToken("expand") || parser->LookAheadToken("each")) - { - typeSpec.expr = parsePrefixExpr(parser); - return typeSpec; - } - // Uncomment should we decide to enable (a,b,c) tuple types - // else if(parser->LookAheadToken(TokenType::LParent)) - // { - // typeSpec.expr = parseTupleTypeExpr(parser); - // return typeSpec; - // } - else if(AdvanceIf(parser, "functype")) - { - typeSpec.expr = parseFuncTypeExpr(parser); - return typeSpec; - } - - bool inGlobalScope = false; - if (AdvanceIf(parser, TokenType::Scope)) - { - inGlobalScope = true; - } + bool inGlobalScope = false; + if (AdvanceIf(parser, TokenType::Scope)) + { + inGlobalScope = true; + } - Token typeName = parser->ReadToken(TokenType::Identifier); + Token typeName = parser->ReadToken(TokenType::Identifier); - auto basicType = parser->astBuilder->create<VarExpr>(); - if (inGlobalScope) - basicType->scope = parser->currentModule->ownedScope; - else - basicType->scope = parser->currentLookupScope; - basicType->loc = typeName.loc; - basicType->name = typeName.getNameOrNull(); + auto basicType = parser->astBuilder->create<VarExpr>(); + if (inGlobalScope) + basicType->scope = parser->currentModule->ownedScope; + else + basicType->scope = parser->currentLookupScope; + basicType->loc = typeName.loc; + basicType->name = typeName.getNameOrNull(); - Expr* typeExpr = basicType; + Expr* typeExpr = basicType; - bool shouldLoop = true; - while (shouldLoop) + bool shouldLoop = true; + while (shouldLoop) + { + switch (peekTokenType(parser)) { - switch (peekTokenType(parser)) + case TokenType::OpLess: typeExpr = parseGenericApp(parser, typeExpr); break; + case TokenType::Scope: { - case TokenType::OpLess: - typeExpr = parseGenericApp(parser, typeExpr); + auto opToken = parser->ReadToken(TokenType::Scope); + typeExpr = parseStaticMemberType(parser, typeExpr, opToken.loc); + break; + } + case TokenType::Dot: + { + auto opToken = parser->ReadToken(TokenType::Dot); + typeExpr = parseMemberType(parser, typeExpr, opToken.loc); break; - case TokenType::Scope: - { - auto opToken = parser->ReadToken(TokenType::Scope); - typeExpr = parseStaticMemberType(parser, typeExpr, opToken.loc); - break; - } - case TokenType::Dot: - { - auto opToken = parser->ReadToken(TokenType::Dot); - typeExpr = parseMemberType(parser, typeExpr, opToken.loc); - break; - } - default: - shouldLoop = false; } + default: shouldLoop = false; } - - typeSpec.expr = typeExpr; - return typeSpec; } - static Modifier* findPotentialGLSLInterfaceBlockModifier(Parser* parser, Modifiers& mods) - { - if (!parser->options.allowGLSLInput) - return nullptr; + typeSpec.expr = typeExpr; + return typeSpec; +} - for (auto mod : mods) - { - if (as<HLSLUniformModifier>(mod) || as<InModifier>(mod) || as<OutModifier>(mod)) - return mod; - } +static Modifier* findPotentialGLSLInterfaceBlockModifier(Parser* parser, Modifiers& mods) +{ + if (!parser->options.allowGLSLInput) return nullptr; - } - /// Parse a type specifier, following the given list of modifiers. - /// - /// If there are any modifiers in `ioModifiers`, this function may modify it - /// by stripping out any type modifiers and attaching them to the `TypeSpec`. - /// Any modifiers that are not type modifiers will be left where they were. - /// - static TypeSpec _parseTypeSpec(Parser* parser, Modifiers& ioModifiers) + for (auto mod : mods) { - TypeSpec typeSpec = _parseSimpleTypeSpec(parser); + if (as<HLSLUniformModifier>(mod) || as<InModifier>(mod) || as<OutModifier>(mod)) + return mod; + } + return nullptr; +} - // We don't know whether `ioModifiers` has any modifiers in it, - // or which of them might be type modifiers, so we will delegate - // figuring that out to a subroutine. - // - typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, ioModifiers); +/// Parse a type specifier, following the given list of modifiers. +/// +/// If there are any modifiers in `ioModifiers`, this function may modify it +/// by stripping out any type modifiers and attaching them to the `TypeSpec`. +/// Any modifiers that are not type modifiers will be left where they were. +/// +static TypeSpec _parseTypeSpec(Parser* parser, Modifiers& ioModifiers) +{ + TypeSpec typeSpec = _parseSimpleTypeSpec(parser); - return typeSpec; - } + // We don't know whether `ioModifiers` has any modifiers in it, + // or which of them might be type modifiers, so we will delegate + // figuring that out to a subroutine. + // + typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, ioModifiers); - /// Parse a type specifier, including any leading modifiers. - /// - /// Note that all the modifiers that precede the type specifier - /// will end up as modifiers for the type specifier even if they - /// should *not* be allowed as modifiers on a type. - /// - /// This function should not be used in contexts where a type specifier - /// is being parsed as part of a declaration, such that a subset of - /// the modifiers might inhere to the declaration rather than the - /// type specifier. - /// - static TypeSpec _parseTypeSpec(Parser* parser) - { - Modifiers modifiers = ParseModifiers(parser); - TypeSpec typeSpec = _parseSimpleTypeSpec(parser); + return typeSpec; +} - typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers); +/// Parse a type specifier, including any leading modifiers. +/// +/// Note that all the modifiers that precede the type specifier +/// will end up as modifiers for the type specifier even if they +/// should *not* be allowed as modifiers on a type. +/// +/// This function should not be used in contexts where a type specifier +/// is being parsed as part of a declaration, such that a subset of +/// the modifiers might inhere to the declaration rather than the +/// type specifier. +/// +static TypeSpec _parseTypeSpec(Parser* parser) +{ + Modifiers modifiers = ParseModifiers(parser); + TypeSpec typeSpec = _parseSimpleTypeSpec(parser); - return typeSpec; - } + typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers); + return typeSpec; +} - static DeclBase* ParseDeclaratorDecl( - Parser* parser, - ContainerDecl* containerDecl, - Modifiers const& inModifiers) - { - SourceLoc startPosition = parser->tokenReader.peekLoc(); - Modifiers modifiers = inModifiers; - auto typeSpec = _parseTypeSpec(parser, modifiers); +static DeclBase* ParseDeclaratorDecl( + Parser* parser, + ContainerDecl* containerDecl, + Modifiers const& inModifiers) +{ + SourceLoc startPosition = parser->tokenReader.peekLoc(); - if (typeSpec.expr == nullptr && typeSpec.decl == nullptr) - { - return nullptr; - } + Modifiers modifiers = inModifiers; + auto typeSpec = _parseTypeSpec(parser, modifiers); + if (typeSpec.expr == nullptr && typeSpec.decl == nullptr) + { + return nullptr; + } - // We may need to build up multiple declarations in a group, - // but the common case will be when we have just a single - // declaration - DeclGroupBuilder declGroupBuilder; - declGroupBuilder.startPosition = startPosition; - declGroupBuilder.astBuilder = parser->astBuilder; - // The type specifier may include a declaration. E.g., - // it might declare a `struct` type. - if(typeSpec.decl) - declGroupBuilder.addDecl(typeSpec.decl); - else - { - // Allow using brackets directly after type name to declare an array typed variable. - typeSpec.expr = parseBracketTypeSuffix(parser, typeSpec.expr); - } + // We may need to build up multiple declarations in a group, + // but the common case will be when we have just a single + // declaration + DeclGroupBuilder declGroupBuilder; + declGroupBuilder.startPosition = startPosition; + declGroupBuilder.astBuilder = parser->astBuilder; - if( AdvanceIf(parser, TokenType::Semicolon) ) - { - // No actual variable is being declared here, but - // that might not be an error. + // The type specifier may include a declaration. E.g., + // it might declare a `struct` type. + if (typeSpec.decl) + declGroupBuilder.addDecl(typeSpec.decl); + else + { + // Allow using brackets directly after type name to declare an array typed variable. + typeSpec.expr = parseBracketTypeSuffix(parser, typeSpec.expr); + } - auto result = declGroupBuilder.getResult(); - if( !result ) - { - parser->sink->diagnose(startPosition, Diagnostics::declarationDidntDeclareAnything); - } - return result; + if (AdvanceIf(parser, TokenType::Semicolon)) + { + // No actual variable is being declared here, but + // that might not be an error. + + auto result = declGroupBuilder.getResult(); + if (!result) + { + parser->sink->diagnose(startPosition, Diagnostics::declarationDidntDeclareAnything); } + return result; + } - // It is possible that we have a plain `struct`, `enum`, - // or similar declaration that isn't being used to declare - // any variable, and the user didn't put a trailing - // semicolon on it: - // - // struct Batman - // { - // int cape; - // } - // - // We want to allow this syntax (rather than give an - // inscrutable error), but also support the less common - // idiom where that declaration is used as part of - // a variable declaration: - // - // struct Robin - // { - // float tights; - // } boyWonder; - // - // As a bit of a hack (insofar as it means we aren't - // *really* compatible with arbitrary HLSL code), we - // will check if there are any more tokens on the - // same line as the closing `}`, and if not, we - // will treat it like the end of the declaration. - // - // Just as a safety net, only apply this logic for - // a file that is being passed in as "true" Slang code. - // - if(parser->getSourceLanguage() == SourceLanguage::Slang) + // It is possible that we have a plain `struct`, `enum`, + // or similar declaration that isn't being used to declare + // any variable, and the user didn't put a trailing + // semicolon on it: + // + // struct Batman + // { + // int cape; + // } + // + // We want to allow this syntax (rather than give an + // inscrutable error), but also support the less common + // idiom where that declaration is used as part of + // a variable declaration: + // + // struct Robin + // { + // float tights; + // } boyWonder; + // + // As a bit of a hack (insofar as it means we aren't + // *really* compatible with arbitrary HLSL code), we + // will check if there are any more tokens on the + // same line as the closing `}`, and if not, we + // will treat it like the end of the declaration. + // + // Just as a safety net, only apply this logic for + // a file that is being passed in as "true" Slang code. + // + if (parser->getSourceLanguage() == SourceLanguage::Slang) + { + if (typeSpec.decl) { - if(typeSpec.decl) + if (peekToken(parser).type == TokenType::EndOfFile || + (peekToken(parser).flags & TokenFlag::AtStartOfLine)) { - if( peekToken(parser).type == TokenType::EndOfFile || - (peekToken(parser).flags & TokenFlag::AtStartOfLine)) - { - // The token after the `}` is at the start of its - // own line, which means it can't be on the same line. - // - // This means the programmer probably wants to - // just treat this as a declaration. - return declGroupBuilder.getResult(); - } + // The token after the `}` is at the start of its + // own line, which means it can't be on the same line. + // + // This means the programmer probably wants to + // just treat this as a declaration. + return declGroupBuilder.getResult(); } } + } - InitDeclarator initDeclarator = parseInitDeclarator(parser, kDeclaratorParseOptions_None); - - DeclaratorInfo declaratorInfo; - declaratorInfo.typeSpec = typeSpec.expr; + InitDeclarator initDeclarator = parseInitDeclarator(parser, kDeclaratorParseOptions_None); + DeclaratorInfo declaratorInfo; + declaratorInfo.typeSpec = typeSpec.expr; - // Rather than parse function declarators properly for now, - // we'll just do a quick disambiguation here. This won't - // matter unless we actually decide to support function-type parameters, - // using C syntax. - // - if ((parser->tokenReader.peekTokenType() == TokenType::LParent || - parser->tokenReader.peekTokenType() == TokenType::OpLess) - // Only parse as a function if we didn't already see mutually-exclusive - // constructs when parsing the declarator. - && !initDeclarator.initializer - && !initDeclarator.semantics.first) - { - UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); - return parseTraditionalFuncDecl(parser, declaratorInfo); - } + // Rather than parse function declarators properly for now, + // we'll just do a quick disambiguation here. This won't + // matter unless we actually decide to support function-type parameters, + // using C syntax. + // + if ((parser->tokenReader.peekTokenType() == TokenType::LParent || + parser->tokenReader.peekTokenType() == TokenType::OpLess) - // Otherwise we are looking at a variable declaration, which could be one in a sequence... + // Only parse as a function if we didn't already see mutually-exclusive + // constructs when parsing the declarator. + && !initDeclarator.initializer && !initDeclarator.semantics.first) + { + UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); + return parseTraditionalFuncDecl(parser, declaratorInfo); + } - if( AdvanceIf(parser, TokenType::Semicolon) ) - { - // easy case: we only had a single declaration! - UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); - VarDeclBase* firstDecl = CreateVarDeclForContext(parser->astBuilder, containerDecl); - CompleteVarDecl(parser, firstDecl, declaratorInfo); + // Otherwise we are looking at a variable declaration, which could be one in a sequence... - declGroupBuilder.addDecl(firstDecl); - return declGroupBuilder.getResult(); - } + if (AdvanceIf(parser, TokenType::Semicolon)) + { + // easy case: we only had a single declaration! + UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); + VarDeclBase* firstDecl = CreateVarDeclForContext(parser->astBuilder, containerDecl); + CompleteVarDecl(parser, firstDecl, declaratorInfo); - // Otherwise we have multiple declarations in a sequence, and these - // declarations need to somehow share both the type spec and modifiers. - // - // If there are any errors in the type specifier, we only want to hear - // about it once, so we need to share structure rather than just - // clone syntax. + declGroupBuilder.addDecl(firstDecl); + return declGroupBuilder.getResult(); + } - auto sharedTypeSpec = parser->astBuilder->create<SharedTypeExpr>(); - sharedTypeSpec->loc = typeSpec.expr->loc; - sharedTypeSpec->base = TypeExp(typeSpec.expr); + // Otherwise we have multiple declarations in a sequence, and these + // declarations need to somehow share both the type spec and modifiers. + // + // If there are any errors in the type specifier, we only want to hear + // about it once, so we need to share structure rather than just + // clone syntax. - for(;;) - { - declaratorInfo.typeSpec = sharedTypeSpec; - UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); + auto sharedTypeSpec = parser->astBuilder->create<SharedTypeExpr>(); + sharedTypeSpec->loc = typeSpec.expr->loc; + sharedTypeSpec->base = TypeExp(typeSpec.expr); - VarDeclBase* varDecl = CreateVarDeclForContext(parser->astBuilder, containerDecl); - CompleteVarDecl(parser, varDecl, declaratorInfo); + for (;;) + { + declaratorInfo.typeSpec = sharedTypeSpec; + UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); - declGroupBuilder.addDecl(varDecl); + VarDeclBase* varDecl = CreateVarDeclForContext(parser->astBuilder, containerDecl); + CompleteVarDecl(parser, varDecl, declaratorInfo); - // end of the sequence? - if(AdvanceIf(parser, TokenType::Semicolon)) - return declGroupBuilder.getResult(); + declGroupBuilder.addDecl(varDecl); - // ad-hoc recovery, to avoid infinite loops - if( parser->isRecovering ) - { - parser->ReadToken(TokenType::Semicolon); - return declGroupBuilder.getResult(); - } + // end of the sequence? + if (AdvanceIf(parser, TokenType::Semicolon)) + return declGroupBuilder.getResult(); - // Let's default to assuming that a missing `,` - // indicates the end of a declaration, - // where a `;` would be expected, and not - // a continuation of this declaration, where - // a `,` would be expected (this is tailoring - // the diagnostic message a bit). - // - // TODO: a more advanced heuristic here might - // look at whether the next token is on the - // same line, to predict whether `,` or `;` - // would be more likely... + // ad-hoc recovery, to avoid infinite loops + if (parser->isRecovering) + { + parser->ReadToken(TokenType::Semicolon); + return declGroupBuilder.getResult(); + } - if (!AdvanceIf(parser, TokenType::Comma)) - { - parser->ReadToken(TokenType::Semicolon); - // We don't need to enter recovering mode if next token isn't semicolon. - // In this case we just continue parsing the token as the next decl. - parser->isRecovering = false; - return declGroupBuilder.getResult(); - } + // Let's default to assuming that a missing `,` + // indicates the end of a declaration, + // where a `;` would be expected, and not + // a continuation of this declaration, where + // a `,` would be expected (this is tailoring + // the diagnostic message a bit). + // + // TODO: a more advanced heuristic here might + // look at whether the next token is on the + // same line, to predict whether `,` or `;` + // would be more likely... - // expect another variable declaration... - initDeclarator = parseInitDeclarator(parser, kDeclaratorParseOptions_None); + if (!AdvanceIf(parser, TokenType::Comma)) + { + parser->ReadToken(TokenType::Semicolon); + // We don't need to enter recovering mode if next token isn't semicolon. + // In this case we just continue parsing the token as the next decl. + parser->isRecovering = false; + return declGroupBuilder.getResult(); } + + // expect another variable declaration... + initDeclarator = parseInitDeclarator(parser, kDeclaratorParseOptions_None); } +} - /// Parse the "register name" part of a `register` or `packoffset` semantic. - /// - /// The syntax matched is: - /// - /// register-name-and-component-mask ::= register-name component-mask? - /// register-name ::= identifier - /// component-mask ::= '.' identifier - /// - static void parseHLSLRegisterNameAndOptionalComponentMask( - Parser* parser, - HLSLLayoutSemantic* semantic) +/// Parse the "register name" part of a `register` or `packoffset` semantic. +/// +/// The syntax matched is: +/// +/// register-name-and-component-mask ::= register-name component-mask? +/// register-name ::= identifier +/// component-mask ::= '.' identifier +/// +static void parseHLSLRegisterNameAndOptionalComponentMask( + Parser* parser, + HLSLLayoutSemantic* semantic) +{ + semantic->registerName = parser->ReadToken(TokenType::Identifier); + if (AdvanceIf(parser, TokenType::Dot)) { - semantic->registerName = parser->ReadToken(TokenType::Identifier); - if (AdvanceIf(parser, TokenType::Dot)) - { - semantic->componentMask = parser->ReadToken(TokenType::Identifier); - } + semantic->componentMask = parser->ReadToken(TokenType::Identifier); } +} - /// Parse an HLSL `register` semantic. - /// - /// The syntax matched is: - /// - /// register-semantic ::= 'register' '(' register-name-and-component-mask register-space? ')' - /// register-space ::= ',' identifier - /// - static void parseHLSLRegisterSemantic( - Parser* parser, - HLSLRegisterSemantic* semantic) +/// Parse an HLSL `register` semantic. +/// +/// The syntax matched is: +/// +/// register-semantic ::= 'register' '(' register-name-and-component-mask register-space? ')' +/// register-space ::= ',' identifier +/// +static void parseHLSLRegisterSemantic(Parser* parser, HLSLRegisterSemantic* semantic) +{ + // Read the `register` keyword + semantic->name = parser->ReadToken(TokenType::Identifier); + + // Expect a parenthized list of additional arguments + parser->ReadToken(TokenType::LParent); + + // First argument is a required register name and optional component mask + parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); + + // Second argument is an optional register space + if (AdvanceIf(parser, TokenType::Comma)) { - // Read the `register` keyword - semantic->name = parser->ReadToken(TokenType::Identifier); + semantic->spaceName = parser->ReadToken(TokenType::Identifier); + } - // Expect a parenthized list of additional arguments - parser->ReadToken(TokenType::LParent); + parser->ReadToken(TokenType::RParent); +} - // First argument is a required register name and optional component mask - parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); +/// Parse an HLSL `packoffset` semantic. +/// +/// The syntax matched is: +/// +/// packoffset-semantic ::= 'packoffset' '(' register-name-and-component-mask ')' +/// +static void parseHLSLPackOffsetSemantic(Parser* parser, HLSLPackOffsetSemantic* semantic) +{ + // Read the `packoffset` keyword + semantic->name = parser->ReadToken(TokenType::Identifier); - // Second argument is an optional register space - if(AdvanceIf(parser, TokenType::Comma)) - { - semantic->spaceName = parser->ReadToken(TokenType::Identifier); - } + // Expect a parenthized list of additional arguments + parser->ReadToken(TokenType::LParent); - parser->ReadToken(TokenType::RParent); - } + // First and only argument is a required register name and optional component mask + parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); - /// Parse an HLSL `packoffset` semantic. - /// - /// The syntax matched is: - /// - /// packoffset-semantic ::= 'packoffset' '(' register-name-and-component-mask ')' - /// - static void parseHLSLPackOffsetSemantic( - Parser* parser, - HLSLPackOffsetSemantic* semantic) - { - // Read the `packoffset` keyword - semantic->name = parser->ReadToken(TokenType::Identifier); + parser->ReadToken(TokenType::RParent); +} - // Expect a parenthized list of additional arguments - parser->ReadToken(TokenType::LParent); +static RayPayloadAccessSemantic* _parseRayPayloadAccessSemantic( + Parser* parser, + RayPayloadAccessSemantic* semantic) +{ + parser->FillPosition(semantic); - // First and only argument is a required register name and optional component mask - parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); + // Read the keyword that introduced the semantic + semantic->name = parser->ReadToken(TokenType::Identifier); - parser->ReadToken(TokenType::RParent); - } + parser->ReadToken(TokenType::LParent); - static RayPayloadAccessSemantic* _parseRayPayloadAccessSemantic(Parser* parser, RayPayloadAccessSemantic* semantic) + for (;;) { - parser->FillPosition(semantic); + if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + break; - // Read the keyword that introduced the semantic - semantic->name = parser->ReadToken(TokenType::Identifier); + auto stageName = parser->ReadToken(TokenType::Identifier); + semantic->stageNameTokens.add(stageName); - parser->ReadToken(TokenType::LParent); - - for(;;) - { - if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - break; + if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + break; - auto stageName = parser->ReadToken(TokenType::Identifier); - semantic->stageNameTokens.add(stageName); + expect(parser, TokenType::Comma); + } - if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - break; + return semantic; +} - expect(parser, TokenType::Comma); - } +template<typename T> +static T* _parseRayPayloadAccessSemantic(Parser* parser) +{ + T* semantic = parser->astBuilder->create<T>(); + _parseRayPayloadAccessSemantic(parser, semantic); + return semantic; +} +// +// semantic ::= identifier ( '(' args ')' )? +// +static Modifier* ParseSemantic(Parser* parser) +{ + if (parser->LookAheadToken("register")) + { + HLSLRegisterSemantic* semantic = parser->astBuilder->create<HLSLRegisterSemantic>(); + parser->FillPosition(semantic); + parseHLSLRegisterSemantic(parser, semantic); return semantic; } - - template<typename T> - static T* _parseRayPayloadAccessSemantic(Parser* parser) + else if (parser->LookAheadToken("packoffset")) { - T* semantic = parser->astBuilder->create<T>(); - _parseRayPayloadAccessSemantic(parser, semantic); + HLSLPackOffsetSemantic* semantic = parser->astBuilder->create<HLSLPackOffsetSemantic>(); + parser->FillPosition(semantic); + parseHLSLPackOffsetSemantic(parser, semantic); return semantic; } - - // - // semantic ::= identifier ( '(' args ')' )? - // - static Modifier* ParseSemantic( - Parser* parser) + else if (parser->LookAheadToken("read") && parser->LookAheadToken(TokenType::LParent, 1)) { - if (parser->LookAheadToken("register")) - { - HLSLRegisterSemantic* semantic = parser->astBuilder->create<HLSLRegisterSemantic>(); - parser->FillPosition(semantic); - parseHLSLRegisterSemantic(parser, semantic); - return semantic; - } - else if (parser->LookAheadToken("packoffset")) - { - HLSLPackOffsetSemantic* semantic = parser->astBuilder->create<HLSLPackOffsetSemantic>(); - parser->FillPosition(semantic); - parseHLSLPackOffsetSemantic(parser, semantic); - return semantic; - } - else if( parser->LookAheadToken("read") && parser->LookAheadToken(TokenType::LParent, 1) ) - { - return _parseRayPayloadAccessSemantic<RayPayloadReadSemantic>(parser); - } - else if( parser->LookAheadToken("write") && parser->LookAheadToken(TokenType::LParent, 1) ) - { - return _parseRayPayloadAccessSemantic<RayPayloadWriteSemantic>(parser); - } - else if (parser->LookAheadToken(TokenType::Identifier)) - { - HLSLSimpleSemantic* semantic = parser->astBuilder->create<HLSLSimpleSemantic>(); - parser->FillPosition(semantic); - semantic->name = parser->ReadToken(TokenType::Identifier); - return semantic; - } - else if (parser->LookAheadToken(TokenType::IntegerLiteral)) - { - BitFieldModifier* bitWidthMod = parser->astBuilder->create<BitFieldModifier>(); - parser->FillPosition(bitWidthMod); - const auto token = parser->tokenReader.advanceToken(); - UnownedStringSlice suffix; - bitWidthMod->width = getIntegerLiteralValue(token, &suffix); - return bitWidthMod; - } - else if (parser->LookAheadToken(TokenType::CompletionRequest)) - { - HLSLSimpleSemantic* semantic = parser->astBuilder->create<HLSLSimpleSemantic>(); - parser->FillPosition(semantic); - semantic->name = parser->ReadToken(); - return semantic; - } - else - { - // expect an identifier, just to produce an error message - parser->ReadToken(TokenType::Identifier); - return nullptr; - } + return _parseRayPayloadAccessSemantic<RayPayloadReadSemantic>(parser); } - - // - // opt-semantics ::= (':' semantic)* - // - static Modifiers _parseOptSemantics( - Parser* parser) + else if (parser->LookAheadToken("write") && parser->LookAheadToken(TokenType::LParent, 1)) + { + return _parseRayPayloadAccessSemantic<RayPayloadWriteSemantic>(parser); + } + else if (parser->LookAheadToken(TokenType::Identifier)) + { + HLSLSimpleSemantic* semantic = parser->astBuilder->create<HLSLSimpleSemantic>(); + parser->FillPosition(semantic); + semantic->name = parser->ReadToken(TokenType::Identifier); + return semantic; + } + else if (parser->LookAheadToken(TokenType::IntegerLiteral)) { - Modifiers modifiers; + BitFieldModifier* bitWidthMod = parser->astBuilder->create<BitFieldModifier>(); + parser->FillPosition(bitWidthMod); + const auto token = parser->tokenReader.advanceToken(); + UnownedStringSlice suffix; + bitWidthMod->width = getIntegerLiteralValue(token, &suffix); + return bitWidthMod; + } + else if (parser->LookAheadToken(TokenType::CompletionRequest)) + { + HLSLSimpleSemantic* semantic = parser->astBuilder->create<HLSLSimpleSemantic>(); + parser->FillPosition(semantic); + semantic->name = parser->ReadToken(); + return semantic; + } + else + { + // expect an identifier, just to produce an error message + parser->ReadToken(TokenType::Identifier); + return nullptr; + } +} - if (!AdvanceIf(parser, TokenType::Colon)) - return modifiers; +// +// opt-semantics ::= (':' semantic)* +// +static Modifiers _parseOptSemantics(Parser* parser) +{ + Modifiers modifiers; - Modifier** link = &modifiers.first; - SLANG_ASSERT(!*link); + if (!AdvanceIf(parser, TokenType::Colon)) + return modifiers; - for (;;) - { - Modifier* semantic = ParseSemantic(parser); - if (semantic) - { - *link = semantic; - link = &semantic->next; - } + Modifier** link = &modifiers.first; + SLANG_ASSERT(!*link); - // If we see a '<', ignore the remaining. - if (AdvanceIf(parser, TokenType::OpLess)) - { - for (;;) - { - auto token = parser->tokenReader.peekToken(); - if (token.type == TokenType::EndOfFile) - break; - else if (token.type == TokenType::OpGreater) - break; - else - parser->tokenReader.advanceToken(); - } - parser->ReadToken(TokenType::OpGreater); - } + for (;;) + { + Modifier* semantic = ParseSemantic(parser); + if (semantic) + { + *link = semantic; + link = &semantic->next; + } - // If we see another `:`, then that means there - // is yet another semantic to be processed. - // Otherwise we assume we are at the end of the list. - // - // TODO: This could produce sub-optimal diagnostics - // when the user *meant* to apply multiple semantics - // to a single declaration: - // - // Foo foo : register(t0) register(s0); - // ^ - // missing ':' here | - // - // However, that is an uncommon occurence, and trying - // to continue parsing semantics here even if we didn't - // see a colon forces us to be careful about - // avoiding an infinite loop here. - if (!AdvanceIf(parser, TokenType::Colon)) + // If we see a '<', ignore the remaining. + if (AdvanceIf(parser, TokenType::OpLess)) + { + for (;;) { - return modifiers; + auto token = parser->tokenReader.peekToken(); + if (token.type == TokenType::EndOfFile) + break; + else if (token.type == TokenType::OpGreater) + break; + else + parser->tokenReader.advanceToken(); } + parser->ReadToken(TokenType::OpGreater); } + // If we see another `:`, then that means there + // is yet another semantic to be processed. + // Otherwise we assume we are at the end of the list. + // + // TODO: This could produce sub-optimal diagnostics + // when the user *meant* to apply multiple semantics + // to a single declaration: + // + // Foo foo : register(t0) register(s0); + // ^ + // missing ':' here | + // + // However, that is an uncommon occurence, and trying + // to continue parsing semantics here even if we didn't + // see a colon forces us to be careful about + // avoiding an infinite loop here. + if (!AdvanceIf(parser, TokenType::Colon)) + { + return modifiers; + } } +} - static void _parseOptSemantics( - Parser* parser, - Decl* decl) - { - _addModifiers(decl, _parseOptSemantics(parser)); - } +static void _parseOptSemantics(Parser* parser, Decl* decl) +{ + _addModifiers(decl, _parseOptSemantics(parser)); +} - static Decl* ParseBufferBlockDecl( - Parser* parser, - String bufferWrapperTypeName, - String *additionalTypeArg = nullptr) - { - // An HLSL declaration of a constant buffer like this: - // - // cbuffer Foo : register(b0) { int a; float b; }; - // - // is treated as syntax sugar for a type declaration - // and then a global variable declaration using that type: - // - // struct $anonymous { int a; float b; }; - // ConstantBuffer<$anonymous> Foo; - // - // where `$anonymous` is a fresh name, and the variable - // declaration is made to be "transparent" so that lookup - // will see through it to the members inside. +static Decl* ParseBufferBlockDecl( + Parser* parser, + String bufferWrapperTypeName, + String* additionalTypeArg = nullptr) +{ + // An HLSL declaration of a constant buffer like this: + // + // cbuffer Foo : register(b0) { int a; float b; }; + // + // is treated as syntax sugar for a type declaration + // and then a global variable declaration using that type: + // + // struct $anonymous { int a; float b; }; + // ConstantBuffer<$anonymous> Foo; + // + // where `$anonymous` is a fresh name, and the variable + // declaration is made to be "transparent" so that lookup + // will see through it to the members inside. - auto bufferWrapperTypeNamePos = parser->tokenReader.peekLoc(); + auto bufferWrapperTypeNamePos = parser->tokenReader.peekLoc(); - // We are going to represent each buffer as a pair of declarations. - // The first is a type declaration that holds all the members, while - // the second is a variable declaration that uses the buffer type. - StructDecl* bufferDataTypeDecl = parser->astBuilder->create<StructDecl>(); + // We are going to represent each buffer as a pair of declarations. + // The first is a type declaration that holds all the members, while + // the second is a variable declaration that uses the buffer type. + StructDecl* bufferDataTypeDecl = parser->astBuilder->create<StructDecl>(); - if (parser->pendingModifiers) + if (parser->pendingModifiers) + { + // Clone visibility modifier from cbuffer decl to the internal struct type decl. + // For example, if cbuffer is public, we want the element buffer type to also be + // public. + if (auto visModifier = parser->pendingModifiers->findModifier<VisibilityModifier>()) { - // Clone visibility modifier from cbuffer decl to the internal struct type decl. - // For example, if cbuffer is public, we want the element buffer type to also be - // public. - if (auto visModifier = parser->pendingModifiers->findModifier<VisibilityModifier>()) - { - auto cloneVisModifier = (VisibilityModifier*)parser->astBuilder->createByNodeType(visModifier->astNodeType); - cloneVisModifier->keywordName = visModifier->keywordName; - cloneVisModifier->loc = visModifier->loc; - addModifier(bufferDataTypeDecl, cloneVisModifier); - } + auto cloneVisModifier = + (VisibilityModifier*)parser->astBuilder->createByNodeType(visModifier->astNodeType); + cloneVisModifier->keywordName = visModifier->keywordName; + cloneVisModifier->loc = visModifier->loc; + addModifier(bufferDataTypeDecl, cloneVisModifier); } + } - VarDecl* bufferVarDecl = parser->astBuilder->create<VarDecl>(); + VarDecl* bufferVarDecl = parser->astBuilder->create<VarDecl>(); - // Both declarations will have a location that points to the name - parser->FillPosition(bufferDataTypeDecl); - parser->FillPosition(bufferVarDecl); + // Both declarations will have a location that points to the name + parser->FillPosition(bufferDataTypeDecl); + parser->FillPosition(bufferVarDecl); - auto reflectionNameToken = parser->ReadToken(TokenType::Identifier); + auto reflectionNameToken = parser->ReadToken(TokenType::Identifier); - // Attach the reflection name to the block so we can use it - auto reflectionNameModifier = parser->astBuilder->create<ParameterGroupReflectionName>(); - reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken); - addModifier(bufferVarDecl, reflectionNameModifier); + // Attach the reflection name to the block so we can use it + auto reflectionNameModifier = parser->astBuilder->create<ParameterGroupReflectionName>(); + reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken); + addModifier(bufferVarDecl, reflectionNameModifier); - // The parameter group type need to have its name generated. - bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + String(reflectionNameToken.getContent())); + // The parameter group type need to have its name generated. + bufferDataTypeDecl->nameAndLoc.name = + generateName(parser, "ParameterGroup_" + String(reflectionNameToken.getContent())); - // TODO(tfoley): We end up constructing unchecked syntax here that - // is expected to type check into the right form, but it might be - // cleaner to have a more explicit desugaring pass where we parse - // these constructs directly into the AST and *then* desugar them. + // TODO(tfoley): We end up constructing unchecked syntax here that + // is expected to type check into the right form, but it might be + // cleaner to have a more explicit desugaring pass where we parse + // these constructs directly into the AST and *then* desugar them. - // Construct a type expression to reference the buffer data type - auto bufferDataTypeExpr = parser->astBuilder->create<VarExpr>(); - bufferDataTypeExpr->loc = bufferDataTypeDecl->loc; - bufferDataTypeExpr->name = bufferDataTypeDecl->nameAndLoc.name; - bufferDataTypeExpr->scope = parser->currentScope; + // Construct a type expression to reference the buffer data type + auto bufferDataTypeExpr = parser->astBuilder->create<VarExpr>(); + bufferDataTypeExpr->loc = bufferDataTypeDecl->loc; + bufferDataTypeExpr->name = bufferDataTypeDecl->nameAndLoc.name; + bufferDataTypeExpr->scope = parser->currentScope; - // Construct a type expression that represents the type for the variable, - // which is the wrapper type applied to the data type - if (bufferWrapperTypeName.getLength()) - { - // Construct a type expression to reference the type constructor - auto bufferWrapperTypeExpr = parser->astBuilder->create<VarExpr>(); - bufferWrapperTypeExpr->loc = bufferWrapperTypeNamePos; - bufferWrapperTypeExpr->name = getName(parser, bufferWrapperTypeName); + // Construct a type expression that represents the type for the variable, + // which is the wrapper type applied to the data type + if (bufferWrapperTypeName.getLength()) + { + // Construct a type expression to reference the type constructor + auto bufferWrapperTypeExpr = parser->astBuilder->create<VarExpr>(); + bufferWrapperTypeExpr->loc = bufferWrapperTypeNamePos; + bufferWrapperTypeExpr->name = getName(parser, bufferWrapperTypeName); - // Always need to look this up in the outer scope, - // so that it won't collide with, e.g., a local variable called `ConstantBuffer` - bufferWrapperTypeExpr->scope = parser->outerScope; + // Always need to look this up in the outer scope, + // so that it won't collide with, e.g., a local variable called `ConstantBuffer` + bufferWrapperTypeExpr->scope = parser->outerScope; - auto bufferVarTypeExpr = parser->astBuilder->create<GenericAppExpr>(); - bufferVarTypeExpr->loc = bufferVarDecl->loc; - bufferVarTypeExpr->functionExpr = bufferWrapperTypeExpr; - bufferVarTypeExpr->arguments.add(bufferDataTypeExpr); - if (additionalTypeArg) - { - auto additionalArgExpr = parser->astBuilder->create<VarExpr>(); - additionalArgExpr->scope = parser->outerScope; - additionalArgExpr->loc = SourceLoc(); - additionalArgExpr->name = getName(parser, *additionalTypeArg); - bufferVarTypeExpr->arguments.add(additionalArgExpr); - } - bufferVarDecl->type.exp = bufferVarTypeExpr; - } - else + auto bufferVarTypeExpr = parser->astBuilder->create<GenericAppExpr>(); + bufferVarTypeExpr->loc = bufferVarDecl->loc; + bufferVarTypeExpr->functionExpr = bufferWrapperTypeExpr; + bufferVarTypeExpr->arguments.add(bufferDataTypeExpr); + if (additionalTypeArg) { - bufferVarDecl->type.exp = bufferDataTypeExpr; + auto additionalArgExpr = parser->astBuilder->create<VarExpr>(); + additionalArgExpr->scope = parser->outerScope; + additionalArgExpr->loc = SourceLoc(); + additionalArgExpr->name = getName(parser, *additionalTypeArg); + bufferVarTypeExpr->arguments.add(additionalArgExpr); } + bufferVarDecl->type.exp = bufferVarTypeExpr; + } + else + { + bufferVarDecl->type.exp = bufferDataTypeExpr; + } - // Any semantics applied to the buffer declaration are taken as applying - // to the variable instead. - _parseOptSemantics(parser, bufferVarDecl); + // Any semantics applied to the buffer declaration are taken as applying + // to the variable instead. + _parseOptSemantics(parser, bufferVarDecl); - // The declarations in the body belong to the data type. - parseDeclBody(parser, bufferDataTypeDecl); + // The declarations in the body belong to the data type. + parseDeclBody(parser, bufferDataTypeDecl); - if (parser->LookAheadToken(TokenType::Identifier) && - parser->LookAheadToken(TokenType::Semicolon, 1)) + if (parser->LookAheadToken(TokenType::Identifier) && + parser->LookAheadToken(TokenType::Semicolon, 1)) + { + // If the user specified an explicit name of the buffer var, use it. + bufferVarDecl->nameAndLoc = ParseDeclName(parser); + reflectionNameModifier->nameAndLoc = bufferVarDecl->nameAndLoc; + parser->ReadToken(TokenType::Semicolon); + } + else + { + // Otherwise, we need to generate a name for the buffer variable. + if (parser->options.optionSet.getBoolOption(CompilerOptionName::NoMangle)) { - // If the user specified an explicit name of the buffer var, use it. - bufferVarDecl->nameAndLoc = ParseDeclName(parser); - reflectionNameModifier->nameAndLoc = bufferVarDecl->nameAndLoc; - parser->ReadToken(TokenType::Semicolon); + // If no-mangle option is set, use the reflection name as the variable name, + // and mark all members of the buffer object as no mangle. + bufferVarDecl->nameAndLoc.name = reflectionNameToken.getName(); + for (auto m : bufferDataTypeDecl->getMembersOfType<VarDecl>()) + { + addModifier(m, parser->astBuilder->create<ExternCppModifier>()); + } } else { - // Otherwise, we need to generate a name for the buffer variable. - if (parser->options.optionSet.getBoolOption(CompilerOptionName::NoMangle)) - { - // If no-mangle option is set, use the reflection name as the variable name, - // and mark all members of the buffer object as no mangle. - bufferVarDecl->nameAndLoc.name = reflectionNameToken.getName(); - for (auto m : bufferDataTypeDecl->getMembersOfType<VarDecl>()) - { - addModifier(m, parser->astBuilder->create<ExternCppModifier>()); - } - } - else - { - bufferVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + String(reflectionNameToken.getContent())); - } + bufferVarDecl->nameAndLoc.name = + generateName(parser, "parameterGroup_" + String(reflectionNameToken.getContent())); + } - // We also need to make the declaration "transparent" so that their - // members are implicitly made visible in the parent scope. - // We achieve this by applying the transparent modifier to the variable. - auto transparentModifier = parser->astBuilder->create<TransparentModifier>(); - transparentModifier->next = bufferVarDecl->modifiers.first; - bufferVarDecl->modifiers.first = transparentModifier; + // We also need to make the declaration "transparent" so that their + // members are implicitly made visible in the parent scope. + // We achieve this by applying the transparent modifier to the variable. + auto transparentModifier = parser->astBuilder->create<TransparentModifier>(); + transparentModifier->next = bufferVarDecl->modifiers.first; + bufferVarDecl->modifiers.first = transparentModifier; - addModifier(bufferVarDecl, parser->astBuilder->create<ImplicitParameterGroupVariableModifier>()); - addModifier(bufferDataTypeDecl, parser->astBuilder->create<ImplicitParameterGroupElementTypeModifier>()); - } + addModifier( + bufferVarDecl, + parser->astBuilder->create<ImplicitParameterGroupVariableModifier>()); + addModifier( + bufferDataTypeDecl, + parser->astBuilder->create<ImplicitParameterGroupElementTypeModifier>()); + } - // Because we are constructing two declarations, we have a thorny - // issue that were are only supposed to return one. - // For now we handle this by adding the type declaration to - // the current scope manually, and then returning the variable - // declaration. - // - // Note: this means that any modifiers that have already been parsed - // will get attached to the variable declaration, not the type. - // There might be cases where we need to shuffle things around. + // Because we are constructing two declarations, we have a thorny + // issue that were are only supposed to return one. + // For now we handle this by adding the type declaration to + // the current scope manually, and then returning the variable + // declaration. + // + // Note: this means that any modifiers that have already been parsed + // will get attached to the variable declaration, not the type. + // There might be cases where we need to shuffle things around. - AddMember(parser->currentScope, bufferDataTypeDecl); + AddMember(parser->currentScope, bufferDataTypeDecl); - return bufferVarDecl; - } + return bufferVarDecl; +} - static NodeBase* parseHLSLCBufferDecl( - Parser* parser, void* /*userData*/) - { - return ParseBufferBlockDecl(parser, "ConstantBuffer"); - } +static NodeBase* parseHLSLCBufferDecl(Parser* parser, void* /*userData*/) +{ + return ParseBufferBlockDecl(parser, "ConstantBuffer"); +} - static NodeBase* parseHLSLTBufferDecl( - Parser* parser, void* /*userData*/) - { - return ParseBufferBlockDecl(parser, "TextureBuffer"); - } +static NodeBase* parseHLSLTBufferDecl(Parser* parser, void* /*userData*/) +{ + return ParseBufferBlockDecl(parser, "TextureBuffer"); +} - static NodeBase* parseGLSLShaderStorageBufferDecl( - Parser* parser, String layoutType) - { - return ParseBufferBlockDecl(parser, "GLSLShaderStorageBuffer", &layoutType); - } +static NodeBase* parseGLSLShaderStorageBufferDecl(Parser* parser, String layoutType) +{ + return ParseBufferBlockDecl(parser, "GLSLShaderStorageBuffer", &layoutType); +} - static void parseOptionalInheritanceClause(Parser* parser, AggTypeDeclBase* decl) +static void parseOptionalInheritanceClause(Parser* parser, AggTypeDeclBase* decl) +{ + if (AdvanceIf(parser, TokenType::Colon)) { - if (AdvanceIf(parser, TokenType::Colon)) + do { - do - { - auto base = parser->ParseTypeExp(); + auto base = parser->ParseTypeExp(); - auto inheritanceDecl = parser->astBuilder->create<InheritanceDecl>(); - inheritanceDecl->loc = base.exp->loc; - inheritanceDecl->nameAndLoc.name = getName(parser, "$inheritance"); - inheritanceDecl->base = base; + auto inheritanceDecl = parser->astBuilder->create<InheritanceDecl>(); + inheritanceDecl->loc = base.exp->loc; + inheritanceDecl->nameAndLoc.name = getName(parser, "$inheritance"); + inheritanceDecl->base = base; - AddMember(decl, inheritanceDecl); - - if (parser->pendingModifiers->hasModifier<ExternModifier>()) - addModifier(inheritanceDecl, parser->astBuilder->create<ExternModifier>()); + AddMember(decl, inheritanceDecl); - } while (AdvanceIf(parser, TokenType::Comma)); - } - } + if (parser->pendingModifiers->hasModifier<ExternModifier>()) + addModifier(inheritanceDecl, parser->astBuilder->create<ExternModifier>()); - static NodeBase* parseExtensionDecl(Parser* parser, void* /*userData*/) - { - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) - { - ExtensionDecl* decl = parser->astBuilder->create<ExtensionDecl>(); - parser->FillPosition(decl); - decl->targetType = parser->ParseTypeExp(); - parseOptionalInheritanceClause(parser, decl); - maybeParseGenericConstraints(parser, genericParent); - parseDeclBody(parser, decl); - return decl; - }); + } while (AdvanceIf(parser, TokenType::Comma)); } +} + +static NodeBase* parseExtensionDecl(Parser* parser, void* /*userData*/) +{ + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) + { + ExtensionDecl* decl = parser->astBuilder->create<ExtensionDecl>(); + parser->FillPosition(decl); + decl->targetType = parser->ParseTypeExp(); + parseOptionalInheritanceClause(parser, decl); + maybeParseGenericConstraints(parser, genericParent); + parseDeclBody(parser, decl); + return decl; + }); +} - static void parseOptionalGenericConstraints(Parser* parser, ContainerDecl* decl) +static void parseOptionalGenericConstraints(Parser* parser, ContainerDecl* decl) +{ + if (AdvanceIf(parser, TokenType::Colon)) { - if (AdvanceIf(parser, TokenType::Colon)) + do { - do - { - GenericTypeConstraintDecl* paramConstraint = parser->astBuilder->create<GenericTypeConstraintDecl>(); - parser->FillPosition(paramConstraint); + GenericTypeConstraintDecl* paramConstraint = + parser->astBuilder->create<GenericTypeConstraintDecl>(); + parser->FillPosition(paramConstraint); - // substitution needs to be filled during check - Type* paramType = nullptr; - if (as<GenericTypeParamDeclBase>(decl)) - { - paramType = DeclRefType::create(parser->astBuilder, DeclRef<Decl>(decl)); + // substitution needs to be filled during check + Type* paramType = nullptr; + if (as<GenericTypeParamDeclBase>(decl)) + { + paramType = DeclRefType::create(parser->astBuilder, DeclRef<Decl>(decl)); - SharedTypeExpr* paramTypeExpr = parser->astBuilder->create<SharedTypeExpr>(); - paramTypeExpr->loc = decl->loc; - paramTypeExpr->base.type = paramType; - paramTypeExpr->type = QualType(parser->astBuilder->getTypeType(paramType)); + SharedTypeExpr* paramTypeExpr = parser->astBuilder->create<SharedTypeExpr>(); + paramTypeExpr->loc = decl->loc; + paramTypeExpr->base.type = paramType; + paramTypeExpr->type = QualType(parser->astBuilder->getTypeType(paramType)); - paramConstraint->sub = TypeExp(paramTypeExpr); - } - else if (as<AssocTypeDecl>(decl)) - { - auto varExpr = parser->astBuilder->create<VarExpr>(); - varExpr->scope = parser->currentScope; - varExpr->name = decl->getName(); - paramConstraint->sub.exp = varExpr; - } + paramConstraint->sub = TypeExp(paramTypeExpr); + } + else if (as<AssocTypeDecl>(decl)) + { + auto varExpr = parser->astBuilder->create<VarExpr>(); + varExpr->scope = parser->currentScope; + varExpr->name = decl->getName(); + paramConstraint->sub.exp = varExpr; + } - paramConstraint->sup = parser->ParseTypeExp(); - AddMember(decl, paramConstraint); - } while (AdvanceIf(parser, TokenType::Comma)); - } + paramConstraint->sup = parser->ParseTypeExp(); + AddMember(decl, paramConstraint); + } while (AdvanceIf(parser, TokenType::Comma)); } +} - static NodeBase* parseAssocType(Parser* parser, void *) - { - AssocTypeDecl* assocTypeDecl = parser->astBuilder->create<AssocTypeDecl>(); +static NodeBase* parseAssocType(Parser* parser, void*) +{ + AssocTypeDecl* assocTypeDecl = parser->astBuilder->create<AssocTypeDecl>(); + + auto nameToken = parser->ReadToken(TokenType::Identifier); + assocTypeDecl->nameAndLoc = NameLoc(nameToken); + assocTypeDecl->loc = nameToken.loc; + parseOptionalGenericConstraints(parser, assocTypeDecl); + maybeParseGenericConstraints(parser, assocTypeDecl); + parser->ReadToken(TokenType::Semicolon); + return assocTypeDecl; +} - auto nameToken = parser->ReadToken(TokenType::Identifier); - assocTypeDecl->nameAndLoc = NameLoc(nameToken); - assocTypeDecl->loc = nameToken.loc; - parseOptionalGenericConstraints(parser, assocTypeDecl); - maybeParseGenericConstraints(parser, assocTypeDecl); - parser->ReadToken(TokenType::Semicolon); - return assocTypeDecl; - } +static NodeBase* parseGlobalGenericTypeParamDecl(Parser* parser, void*) +{ + GlobalGenericParamDecl* genParamDecl = parser->astBuilder->create<GlobalGenericParamDecl>(); + auto nameToken = parser->ReadToken(TokenType::Identifier); + genParamDecl->nameAndLoc = NameLoc(nameToken); + genParamDecl->loc = nameToken.loc; + parseOptionalGenericConstraints(parser, genParamDecl); + parser->ReadToken(TokenType::Semicolon); + return genParamDecl; +} + +static NodeBase* parseGlobalGenericValueParamDecl(Parser* parser, void*) +{ + GlobalGenericValueParamDecl* genericParamDecl = + parser->astBuilder->create<GlobalGenericValueParamDecl>(); + auto nameToken = parser->ReadToken(TokenType::Identifier); + genericParamDecl->nameAndLoc = NameLoc(nameToken); + genericParamDecl->loc = nameToken.loc; - static NodeBase* parseGlobalGenericTypeParamDecl(Parser * parser, void *) + if (AdvanceIf(parser, TokenType::Colon)) { - GlobalGenericParamDecl* genParamDecl = parser->astBuilder->create<GlobalGenericParamDecl>(); - auto nameToken = parser->ReadToken(TokenType::Identifier); - genParamDecl->nameAndLoc = NameLoc(nameToken); - genParamDecl->loc = nameToken.loc; - parseOptionalGenericConstraints(parser, genParamDecl); - parser->ReadToken(TokenType::Semicolon); - return genParamDecl; + genericParamDecl->type = parser->ParseTypeExp(); } - static NodeBase* parseGlobalGenericValueParamDecl(Parser * parser, void *) + if (AdvanceIf(parser, TokenType::OpAssign)) { - GlobalGenericValueParamDecl* genericParamDecl = parser->astBuilder->create<GlobalGenericValueParamDecl>(); - auto nameToken = parser->ReadToken(TokenType::Identifier); - genericParamDecl->nameAndLoc = NameLoc(nameToken); - genericParamDecl->loc = nameToken.loc; - - if(AdvanceIf(parser, TokenType::Colon)) - { - genericParamDecl->type = parser->ParseTypeExp(); - } - - if(AdvanceIf(parser, TokenType::OpAssign)) - { - genericParamDecl->initExpr = parser->ParseInitExpr(); - } - - parser->ReadToken(TokenType::Semicolon); - return genericParamDecl; + genericParamDecl->initExpr = parser->ParseInitExpr(); } - static NodeBase* parseInterfaceDecl(Parser* parser, void* /*userData*/) - { - InterfaceDecl* decl = parser->astBuilder->createInterfaceDecl(parser->tokenReader.peekLoc()); - parser->FillPosition(decl); + parser->ReadToken(TokenType::Semicolon); + return genericParamDecl; +} - AdvanceIf(parser, TokenType::CompletionRequest); +static NodeBase* parseInterfaceDecl(Parser* parser, void* /*userData*/) +{ + InterfaceDecl* decl = parser->astBuilder->createInterfaceDecl(parser->tokenReader.peekLoc()); + parser->FillPosition(decl); - decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) - { - // We allow for an inheritance clause on a `struct` - // so that it can conform to interfaces. - parseOptionalInheritanceClause(parser, decl); - maybeParseGenericConstraints(parser, genericParent); - parseDeclBody(parser, decl); - return decl; - }); - } + AdvanceIf(parser, TokenType::CompletionRequest); - static NodeBase* parseNamespaceDecl(Parser* parser, void* /*userData*/) - { - // We start by parsing the name of the namespace that is being opened. - // - // We support a qualified name for a namespace declaration: - // - // namespace A.B { ... } + decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) + { + // We allow for an inheritance clause on a `struct` + // so that it can conform to interfaces. + parseOptionalInheritanceClause(parser, decl); + maybeParseGenericConstraints(parser, genericParent); + parseDeclBody(parser, decl); + return decl; + }); +} + +static NodeBase* parseNamespaceDecl(Parser* parser, void* /*userData*/) +{ + // We start by parsing the name of the namespace that is being opened. + // + // We support a qualified name for a namespace declaration: + // + // namespace A.B { ... } + // + // which should expand as if the user had written nested + // namespace declarations: + // + // namespace A { namespace B { ... } } + // + auto parentDecl = parser->currentScope->containerDecl; + SLANG_ASSERT(parentDecl); + NamespaceDecl* result = nullptr; + NamespaceDecl* namespaceDecl = nullptr; + List<NamespaceDecl*> nestedNamespaceDecls; + do + { + namespaceDecl = nullptr; + NameLoc nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + // Once we have the name for the namespace, we face a challenge: + // either the namespace hasn't been seen before (in which case + // we need to create it and start filling it in), or we've seen + // the same namespace before inside the same module, such that + // we should be adding the declarations we parse to the existing + // declarations (so that they share a common scope/parent). // - // which should expand as if the user had written nested - // namespace declarations: + // In each case we will find a namespace that we want to fill in, + // but depending on the case we may or may not want to return + // a declaration to the caller (since they will try to add + // any non-null pointer we return to the AST). // - // namespace A { namespace B { ... } } + + // In order to find out what case we are in, we start by looking + // for a namespace declaration of the same name in the parent + // declaration. // - auto parentDecl = parser->currentScope->containerDecl; - SLANG_ASSERT(parentDecl); - NamespaceDecl* result = nullptr; - NamespaceDecl* namespaceDecl = nullptr; - List<NamespaceDecl*> nestedNamespaceDecls; - do { - namespaceDecl = nullptr; - NameLoc nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - // Once we have the name for the namespace, we face a challenge: - // either the namespace hasn't been seen before (in which case - // we need to create it and start filling it in), or we've seen - // the same namespace before inside the same module, such that - // we should be adding the declarations we parse to the existing - // declarations (so that they share a common scope/parent). + + // We meed to make sure that the member dictionary of + // the parent declaration has been built/rebuilt so that + // lookup by name will work. // - // In each case we will find a namespace that we want to fill in, - // but depending on the case we may or may not want to return - // a declaration to the caller (since they will try to add - // any non-null pointer we return to the AST). + // TODO: The current way we rebuild the member dictionary + // would make for O(N^2) parsing time in a file that + // consisted of N back-to-back `namespace`s, since each + // would trigger a rebuild of the member dictionary that + // would take O(N) time. // - // In order to find out what case we are in, we start by looking - // for a namespace declaration of the same name in the parent - // declaration. + // There might be multiple members of the same name + // (if we define a namespace `foo` after an overloaded + // function `foo` has been defined), and direct member + // lookup will only give us the first. + // + Decl* firstDecl = nullptr; + parentDecl->getMemberDictionary().tryGetValue(nameAndLoc.name, firstDecl); + // + // We will search through the declarations of the name + // and find the first that is a namespace (if any). + // + // Note: we do not issue diagnostics here based on + // the potential conflicts between these declarations, + // because we want to do as little semantic analysis + // as possible in the parser, and we'd rather be + // as permissive as possible right now. // + for (Decl* d = firstDecl; d; d = d->nextInContainerWithSameName) { + namespaceDecl = as<NamespaceDecl>(d); + if (namespaceDecl) + break; + } - // We meed to make sure that the member dictionary of - // the parent declaration has been built/rebuilt so that - // lookup by name will work. - // - // TODO: The current way we rebuild the member dictionary - // would make for O(N^2) parsing time in a file that - // consisted of N back-to-back `namespace`s, since each - // would trigger a rebuild of the member dictionary that - // would take O(N) time. - // - - // There might be multiple members of the same name - // (if we define a namespace `foo` after an overloaded - // function `foo` has been defined), and direct member - // lookup will only give us the first. - // - Decl* firstDecl = nullptr; - parentDecl->getMemberDictionary().tryGetValue(nameAndLoc.name, firstDecl); - // - // We will search through the declarations of the name - // and find the first that is a namespace (if any). - // - // Note: we do not issue diagnostics here based on - // the potential conflicts between these declarations, - // because we want to do as little semantic analysis - // as possible in the parser, and we'd rather be - // as permissive as possible right now. - // - for (Decl* d = firstDecl; d; d = d->nextInContainerWithSameName) - { - namespaceDecl = as<NamespaceDecl>(d); - if (namespaceDecl) - break; - } - - // If we didn't find a pre-existing namespace, then - // we will go ahead and create one now. - // - if (!namespaceDecl) + // If we didn't find a pre-existing namespace, then + // we will go ahead and create one now. + // + if (!namespaceDecl) + { + namespaceDecl = parser->astBuilder->create<NamespaceDecl>(); + namespaceDecl->nameAndLoc = nameAndLoc; + namespaceDecl->loc = nameAndLoc.loc; + AddMember(parentDecl, namespaceDecl); + if (auto parentNamespace = as<NamespaceDecl>(parentDecl)) { - namespaceDecl = parser->astBuilder->create<NamespaceDecl>(); - namespaceDecl->nameAndLoc = nameAndLoc; - namespaceDecl->loc = nameAndLoc.loc; - AddMember(parentDecl, namespaceDecl); - if (auto parentNamespace = as<NamespaceDecl>(parentDecl)) - { - parser->PushScope(parentDecl); - nestedNamespaceDecls.add(parentNamespace); - } + parser->PushScope(parentDecl); + nestedNamespaceDecls.add(parentNamespace); } } - if (!result) - { - result = namespaceDecl; - } - parentDecl = namespaceDecl; - } while (AdvanceIf(parser, TokenType::Dot) || AdvanceIf(parser, TokenType::Scope)); - - // Now that we have a namespace declaration to fill in - // (whether a new or existing one), we can parse the - // `{}`-enclosed body to add declarations as children - // of the namespace. - // - parseDeclBody(parser, namespaceDecl); - - for (auto ns : nestedNamespaceDecls) + } + if (!result) { - ns->loc = ns->nameAndLoc.loc; - ns->closingSourceLoc = namespaceDecl->closingSourceLoc; - parser->PopScope(); + result = namespaceDecl; } - return result; - } + parentDecl = namespaceDecl; + } while (AdvanceIf(parser, TokenType::Dot) || AdvanceIf(parser, TokenType::Scope)); - static NodeBase* parseUsingDecl(Parser* parser, void* /*userData*/) + // Now that we have a namespace declaration to fill in + // (whether a new or existing one), we can parse the + // `{}`-enclosed body to add declarations as children + // of the namespace. + // + parseDeclBody(parser, namespaceDecl); + + for (auto ns : nestedNamespaceDecls) { - UsingDecl* decl = parser->astBuilder->create<UsingDecl>(); - parser->FillPosition(decl); + ns->loc = ns->nameAndLoc.loc; + ns->closingSourceLoc = namespaceDecl->closingSourceLoc; + parser->PopScope(); + } + return result; +} - // A `using` declaration will need to know about the current - // scope at the point where it appears, so that it can know - // the scope it is attempting to extend. - // - decl->scope = parser->currentScope; +static NodeBase* parseUsingDecl(Parser* parser, void* /*userData*/) +{ + UsingDecl* decl = parser->astBuilder->create<UsingDecl>(); + parser->FillPosition(decl); - // TODO: We may eventually want to support declarations - // of the form `using <id> = <expr>;` which introduce - // a shorthand alias for a namespace/type/whatever. - // - // For now we are just sticking to the most basic form. + // A `using` declaration will need to know about the current + // scope at the point where it appears, so that it can know + // the scope it is attempting to extend. + // + decl->scope = parser->currentScope; - // As a compatibility feature for programmers used to C++, - // we allow the `namespace` keyword to come after `using`, - // where it has no effect. - // - if(parser->LookAheadToken("namespace")) - { - advanceToken(parser); - } + // TODO: We may eventually want to support declarations + // of the form `using <id> = <expr>;` which introduce + // a shorthand alias for a namespace/type/whatever. + // + // For now we are just sticking to the most basic form. - // The entity that is going to be used is identified - // using an arbitrary expression (although we expect - // that valid code will not typically use the full - // freedom of what the expression grammar supports. - // - decl->arg = parser->ParseExpression(); + // As a compatibility feature for programmers used to C++, + // we allow the `namespace` keyword to come after `using`, + // where it has no effect. + // + if (parser->LookAheadToken("namespace")) + { + advanceToken(parser); + } - expect(parser, TokenType::Semicolon); + // The entity that is going to be used is identified + // using an arbitrary expression (although we expect + // that valid code will not typically use the full + // freedom of what the expression grammar supports. + // + decl->arg = parser->ParseExpression(); - return decl; - } + expect(parser, TokenType::Semicolon); + + return decl; +} - static NodeBase* parseIgnoredBlockDecl(Parser* parser, void*) +static NodeBase* parseIgnoredBlockDecl(Parser* parser, void*) +{ + parser->ReadToken(TokenType::LBrace); + int remaingingBraceToClose = 1; + for (;;) { - parser->ReadToken(TokenType::LBrace); - int remaingingBraceToClose = 1; - for (;;) + auto token = parser->ReadToken(); + if (token.type == TokenType::RBrace) { - auto token = parser->ReadToken(); - if (token.type == TokenType::RBrace) - { - remaingingBraceToClose--; - if (remaingingBraceToClose == 0) - break; - } - else if (token.type == TokenType::LBrace) - { - remaingingBraceToClose++; - } - else if (token.type == TokenType::EndOfFile) - { + remaingingBraceToClose--; + if (remaingingBraceToClose == 0) break; - } } - auto decl = parser->astBuilder->create<EmptyDecl>(); - parser->FillPosition(decl); - return decl; - } - - static NodeBase* parseTransparentBlockDecl(Parser* parser, void*) - { - if (parser->currentScope && parser->currentScope->containerDecl) + else if (token.type == TokenType::LBrace) { - parseDeclBody(parser, parser->currentScope->containerDecl); - return parser->astBuilder->create<EmptyDecl>(); + remaingingBraceToClose++; } - else + else if (token.type == TokenType::EndOfFile) { - SLANG_UNEXPECTED("parseTransparentBlock should be called with a valid scope."); + break; } } + auto decl = parser->astBuilder->create<EmptyDecl>(); + parser->FillPosition(decl); + return decl; +} - static NodeBase* parseFileDecl(Parser* parser, void*) +static NodeBase* parseTransparentBlockDecl(Parser* parser, void*) +{ + if (parser->currentScope && parser->currentScope->containerDecl) { - auto fileDecl = parser->astBuilder->create<FileDecl>(); - parser->FillPosition(fileDecl); - parseDeclBody(parser, fileDecl); - return fileDecl; + parseDeclBody(parser, parser->currentScope->containerDecl); + return parser->astBuilder->create<EmptyDecl>(); } - - static NodeBase* parseRequireCapabilityDecl(Parser* parser, void*) + else { - auto decl = parser->astBuilder->create<RequireCapabilityDecl>(); - parser->FillPosition(decl); - List<CapabilityName> capNames; - while (parser->LookAheadToken(TokenType::Identifier)) - { - auto capNameToken = parser->ReadToken(TokenType::Identifier); - CapabilityName capName = findCapabilityName(capNameToken.getContent()); - if (capName != CapabilityName::Invalid) - capNames.add(capName); - else - parser->sink->diagnose(capNameToken, Diagnostics::unknownCapability, capNameToken.getContent()); - if (AdvanceIf(parser, "+") || AdvanceIf(parser, ",")) - continue; - break; - } - decl->inferredCapabilityRequirements = CapabilitySet(capNames); - parser->ReadToken(TokenType::Semicolon); - return decl; + SLANG_UNEXPECTED("parseTransparentBlock should be called with a valid scope."); } +} - static NodeBase* parseConstructorDecl(Parser* parser, void* /*userData*/) - { - ConstructorDecl* decl = parser->astBuilder->create<ConstructorDecl>(); - - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) - { - // Note: we leave the source location of this decl as invalid, to - // trigger the fallback logic that fills in the location of the - // `__init` keyword later. - - parser->PushScope(decl); +static NodeBase* parseFileDecl(Parser* parser, void*) +{ + auto fileDecl = parser->astBuilder->create<FileDecl>(); + parser->FillPosition(fileDecl); + parseDeclBody(parser, fileDecl); + return fileDecl; +} - // TODO: we need to make sure that all initializers have - // the same name, but that this name doesn't conflict - // with any user-defined names. - // Giving them a name (rather than leaving it null) - // ensures that we can use name-based lookup to find - // all of the initializers on a type (and has - // the potential to unify initializer lookup with - // ordinary member lookup). - decl->nameAndLoc.name = getName(parser, "$init"); +static NodeBase* parseRequireCapabilityDecl(Parser* parser, void*) +{ + auto decl = parser->astBuilder->create<RequireCapabilityDecl>(); + parser->FillPosition(decl); + List<CapabilityName> capNames; + while (parser->LookAheadToken(TokenType::Identifier)) + { + auto capNameToken = parser->ReadToken(TokenType::Identifier); + CapabilityName capName = findCapabilityName(capNameToken.getContent()); + if (capName != CapabilityName::Invalid) + capNames.add(capName); + else + parser->sink->diagnose( + capNameToken, + Diagnostics::unknownCapability, + capNameToken.getContent()); + if (AdvanceIf(parser, "+") || AdvanceIf(parser, ",")) + continue; + break; + } + decl->inferredCapabilityRequirements = CapabilitySet(capNames); + parser->ReadToken(TokenType::Semicolon); + return decl; +} - parseParameterList(parser, decl); - auto funcScope = parser->currentScope; - parser->PopScope(); - maybeParseGenericConstraints(parser, genericParent); - parser->PushScope(funcScope); +static NodeBase* parseConstructorDecl(Parser* parser, void* /*userData*/) +{ + ConstructorDecl* decl = parser->astBuilder->create<ConstructorDecl>(); - decl->body = parseOptBody(parser); + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) + { + // Note: we leave the source location of this decl as invalid, to + // trigger the fallback logic that fills in the location of the + // `__init` keyword later. - if (auto block = as<BlockStmt>(decl->body)) - decl->closingSourceLoc = block->closingSourceLoc; + parser->PushScope(decl); - parser->PopScope(); - return decl; - }); - } + // TODO: we need to make sure that all initializers have + // the same name, but that this name doesn't conflict + // with any user-defined names. + // Giving them a name (rather than leaving it null) + // ensures that we can use name-based lookup to find + // all of the initializers on a type (and has + // the potential to unify initializer lookup with + // ordinary member lookup). + decl->nameAndLoc.name = getName(parser, "$init"); - static AccessorDecl* parseAccessorDecl(Parser* parser) - { - Modifiers modifiers = ParseModifiers(parser); + parseParameterList(parser, decl); + auto funcScope = parser->currentScope; + parser->PopScope(); + maybeParseGenericConstraints(parser, genericParent); + parser->PushScope(funcScope); - AccessorDecl* decl = nullptr; - auto loc = peekToken(parser).loc; - auto name = peekToken(parser).getName(); - if( AdvanceIf(parser, "get") ) - { - decl = parser->astBuilder->create<GetterDecl>(); - } - else if( AdvanceIf(parser, "set") ) - { - decl = parser->astBuilder->create<SetterDecl>(); - } - else if( AdvanceIf(parser, "ref") ) - { - decl = parser->astBuilder->create<RefAccessorDecl>(); - } - else - { - Unexpected(parser); - return nullptr; - } - decl->loc = loc; - decl->nameAndLoc.name = name; - decl->nameAndLoc.loc = loc; + decl->body = parseOptBody(parser); - _addModifiers(decl, modifiers); + if (auto block = as<BlockStmt>(decl->body)) + decl->closingSourceLoc = block->closingSourceLoc; - parser->PushScope(decl); + parser->PopScope(); + return decl; + }); +} - // A `set` declaration should support declaring an explicit - // name for the parameter representing the new value. - // - // We handle this by supporting an arbitrary parameter list - // on any accessor, and then assume that semantic checking - // will diagnose any cases that aren't allowed. - // - if(parser->tokenReader.peekTokenType() == TokenType::LParent) - { - parseModernParamList(parser, decl); - } +static AccessorDecl* parseAccessorDecl(Parser* parser) +{ + Modifiers modifiers = ParseModifiers(parser); - if( parser->tokenReader.peekTokenType() == TokenType::LBrace ) - { - decl->body = parser->parseBlockStatement(); - if (auto block = as<BlockStmt>(decl->body)) - { - decl->closingSourceLoc = block->closingSourceLoc; - } - } - else - { - decl->closingSourceLoc = parser->tokenReader.peekLoc(); - parser->ReadToken(TokenType::Semicolon); - } + AccessorDecl* decl = nullptr; + auto loc = peekToken(parser).loc; + auto name = peekToken(parser).getName(); + if (AdvanceIf(parser, "get")) + { + decl = parser->astBuilder->create<GetterDecl>(); + } + else if (AdvanceIf(parser, "set")) + { + decl = parser->astBuilder->create<SetterDecl>(); + } + else if (AdvanceIf(parser, "ref")) + { + decl = parser->astBuilder->create<RefAccessorDecl>(); + } + else + { + Unexpected(parser); + return nullptr; + } + decl->loc = loc; + decl->nameAndLoc.name = name; + decl->nameAndLoc.loc = loc; - parser->PopScope(); + _addModifiers(decl, modifiers); + parser->PushScope(decl); - return decl; + // A `set` declaration should support declaring an explicit + // name for the parameter representing the new value. + // + // We handle this by supporting an arbitrary parameter list + // on any accessor, and then assume that semantic checking + // will diagnose any cases that aren't allowed. + // + if (parser->tokenReader.peekTokenType() == TokenType::LParent) + { + parseModernParamList(parser, decl); } - static void parseStorageDeclBody(Parser* parser, ContainerDecl* decl) + if (parser->tokenReader.peekTokenType() == TokenType::LBrace) { - if( AdvanceIf(parser, TokenType::LBrace) ) + decl->body = parser->parseBlockStatement(); + if (auto block = as<BlockStmt>(decl->body)) { - // We want to parse nested "accessor" declarations - Token closingToken; - while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingToken)) - { - auto accessor = parseAccessorDecl(parser); - AddMember(decl, accessor); - } - decl->closingSourceLoc = closingToken.loc; - } - else - { - decl->closingSourceLoc = parser->tokenReader.peekLoc(); - - parser->ReadToken(TokenType::Semicolon); - - // empty body should be treated like `{ get; }` + decl->closingSourceLoc = block->closingSourceLoc; } } - - static NodeBase* parseSubscriptDecl(Parser* parser, void* /*userData*/) + else { - SubscriptDecl* decl = parser->astBuilder->create<SubscriptDecl>(); - parser->FillPosition(decl); - parser->PushScope(decl); + decl->closingSourceLoc = parser->tokenReader.peekLoc(); + parser->ReadToken(TokenType::Semicolon); + } - // TODO: the use of this name here is a bit magical... - decl->nameAndLoc.name = getName(parser, "operator[]"); + parser->PopScope(); - parseParameterList(parser, decl); - if( AdvanceIf(parser, TokenType::RightArrow) ) - { - decl->returnType = parser->ParseTypeExp(); - } - else + return decl; +} + +static void parseStorageDeclBody(Parser* parser, ContainerDecl* decl) +{ + if (AdvanceIf(parser, TokenType::LBrace)) + { + // We want to parse nested "accessor" declarations + Token closingToken; + while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingToken)) { - decl->returnType.exp = parser->astBuilder->create<IncompleteExpr>(); + auto accessor = parseAccessorDecl(parser); + AddMember(decl, accessor); } + decl->closingSourceLoc = closingToken.loc; + } + else + { + decl->closingSourceLoc = parser->tokenReader.peekLoc(); - parseStorageDeclBody(parser, decl); + parser->ReadToken(TokenType::Semicolon); - parser->PopScope(); - return decl; + // empty body should be treated like `{ get; }` } +} - /// Peek in the token stream and return `true` if it looks like a modern-style variable declaration is coming up. - static bool _peekModernStyleVarDecl(Parser* parser) - { - // A modern-style variable declaration always starts with an identifier - if(peekTokenType(parser) != TokenType::Identifier) - return false; +static NodeBase* parseSubscriptDecl(Parser* parser, void* /*userData*/) +{ + SubscriptDecl* decl = parser->astBuilder->create<SubscriptDecl>(); + parser->FillPosition(decl); + parser->PushScope(decl); - switch(peekTokenType(parser, 1)) - { - default: - return false; + // TODO: the use of this name here is a bit magical... + decl->nameAndLoc.name = getName(parser, "operator[]"); - case TokenType::Colon: - case TokenType::Comma: - case TokenType::RParent: - case TokenType::RBrace: - case TokenType::RBracket: - case TokenType::LBrace: - return true; - } - } + parseParameterList(parser, decl); - static NodeBase* parsePropertyDecl(Parser* parser, void* /*userData*/) + if (AdvanceIf(parser, TokenType::RightArrow)) { - PropertyDecl* decl = parser->astBuilder->create<PropertyDecl>(); - parser->PushScope(decl); - - // We want to support property declarations with two - // different syntaxes. - // - // First, we want to support a syntax that is consistent - // with C-style ("traditional") variable declarations: - // - // int myVar = 2; - // proprerty int myProp { ... } - // - // Second we want to support a syntax that is - // consistent with `let` and `var` declarations: - // - // let myVar : int = 2; - // property myProp : int { ... } - // - // The latter case is more constrained, and we will - // detect with two tokens of lookahead. If the - // next token (after `property`) is an identifier, - // and the token after that is a colon (`:`), then - // we assume we are in the `let`/`var`-style case. - // - if(_peekModernStyleVarDecl(parser)) - { - parser->FillPosition(decl); - decl->nameAndLoc = expectIdentifier(parser); - expect(parser, TokenType::Colon); - decl->type = parser->ParseTypeExp(); - } - else - { - // The traditional syntax requires a bit more - // care to parse, since it needs to support - // C declarator syntax. - // - DeclaratorInfo declaratorInfo; - declaratorInfo.typeSpec = parser->ParseType(); + decl->returnType = parser->ParseTypeExp(); + } + else + { + decl->returnType.exp = parser->astBuilder->create<IncompleteExpr>(); + } - auto declarator = parseDeclarator(parser, kDeclaratorParseOptions_None); - UnwrapDeclarator(parser->astBuilder, declarator, &declaratorInfo); + parseStorageDeclBody(parser, decl); - // TODO: We might want to handle the case where the - // resulting declarator is not valid to use for - // declaring a property (e.g., it has function parameters). + parser->PopScope(); + return decl; +} - decl->nameAndLoc = declaratorInfo.nameAndLoc; - decl->type = TypeExp(declaratorInfo.typeSpec); - decl->loc = decl->nameAndLoc.loc; - } +/// Peek in the token stream and return `true` if it looks like a modern-style variable declaration +/// is coming up. +static bool _peekModernStyleVarDecl(Parser* parser) +{ + // A modern-style variable declaration always starts with an identifier + if (peekTokenType(parser) != TokenType::Identifier) + return false; - parseStorageDeclBody(parser, decl); + switch (peekTokenType(parser, 1)) + { + default: return false; - parser->PopScope(); - return decl; + case TokenType::Colon: + case TokenType::Comma: + case TokenType::RParent: + case TokenType::RBrace: + case TokenType::RBracket: + case TokenType::LBrace: return true; } +} + +static NodeBase* parsePropertyDecl(Parser* parser, void* /*userData*/) +{ + PropertyDecl* decl = parser->astBuilder->create<PropertyDecl>(); + parser->PushScope(decl); - static void parseModernVarDeclBaseCommon( - Parser* parser, - VarDeclBase* decl) + // We want to support property declarations with two + // different syntaxes. + // + // First, we want to support a syntax that is consistent + // with C-style ("traditional") variable declarations: + // + // int myVar = 2; + // proprerty int myProp { ... } + // + // Second we want to support a syntax that is + // consistent with `let` and `var` declarations: + // + // let myVar : int = 2; + // property myProp : int { ... } + // + // The latter case is more constrained, and we will + // detect with two tokens of lookahead. If the + // next token (after `property`) is an identifier, + // and the token after that is a colon (`:`), then + // we assume we are in the `let`/`var`-style case. + // + if (_peekModernStyleVarDecl(parser)) { parser->FillPosition(decl); - decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + decl->nameAndLoc = expectIdentifier(parser); + expect(parser, TokenType::Colon); + decl->type = parser->ParseTypeExp(); + } + else + { + // The traditional syntax requires a bit more + // care to parse, since it needs to support + // C declarator syntax. + // + DeclaratorInfo declaratorInfo; + declaratorInfo.typeSpec = parser->ParseType(); - if(AdvanceIf(parser, TokenType::Colon)) - { - decl->type = parser->ParseTypeExp(); - } + auto declarator = parseDeclarator(parser, kDeclaratorParseOptions_None); + UnwrapDeclarator(parser->astBuilder, declarator, &declaratorInfo); - if(AdvanceIf(parser, TokenType::OpAssign)) - { - decl->initExpr = parser->ParseInitExpr(); - } - } + // TODO: We might want to handle the case where the + // resulting declarator is not valid to use for + // declaring a property (e.g., it has function parameters). - static void parseModernVarDeclCommon( - Parser* parser, - VarDecl* decl) - { - parseModernVarDeclBaseCommon(parser, decl); - expect(parser, TokenType::Semicolon); + decl->nameAndLoc = declaratorInfo.nameAndLoc; + decl->type = TypeExp(declaratorInfo.typeSpec); + decl->loc = decl->nameAndLoc.loc; } - static NodeBase* parseLetDecl( - Parser* parser, void* /*userData*/) + parseStorageDeclBody(parser, decl); + + parser->PopScope(); + return decl; +} + +static void parseModernVarDeclBaseCommon(Parser* parser, VarDeclBase* decl) +{ + parser->FillPosition(decl); + decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + + if (AdvanceIf(parser, TokenType::Colon)) { - LetDecl* decl = parser->astBuilder->create<LetDecl>(); - parseModernVarDeclCommon(parser, decl); - return decl; + decl->type = parser->ParseTypeExp(); } - static NodeBase* parseVarDecl( - Parser* parser, void* /*userData*/) + if (AdvanceIf(parser, TokenType::OpAssign)) { - VarDecl* decl = parser->astBuilder->create<VarDecl>(); - parseModernVarDeclCommon(parser, decl); - return decl; + decl->initExpr = parser->ParseInitExpr(); } +} - /// Parse the common structured of a traditional-style parameter declaration (excluding the trailing semicolon) - static void _parseTraditionalParamDeclCommonBase(Parser* parser, VarDeclBase* decl, DeclaratorParseOptions options = kDeclaratorParseOptions_None) - { - DeclaratorInfo declaratorInfo; - declaratorInfo.typeSpec = parser->ParseType(); +static void parseModernVarDeclCommon(Parser* parser, VarDecl* decl) +{ + parseModernVarDeclBaseCommon(parser, decl); + expect(parser, TokenType::Semicolon); +} - InitDeclarator initDeclarator = parseInitDeclarator(parser, options); - UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); +static NodeBase* parseLetDecl(Parser* parser, void* /*userData*/) +{ + LetDecl* decl = parser->astBuilder->create<LetDecl>(); + parseModernVarDeclCommon(parser, decl); + return decl; +} - // Assume it is a variable-like declarator - CompleteVarDecl(parser, decl, declaratorInfo); - } +static NodeBase* parseVarDecl(Parser* parser, void* /*userData*/) +{ + VarDecl* decl = parser->astBuilder->create<VarDecl>(); + parseModernVarDeclCommon(parser, decl); + return decl; +} - static ParamDecl* parseModernParamDecl( - Parser* parser) - { - // TODO: For "modern" parameters, we should probably - // not allow arbitrary keyword-based modifiers (only allowing - // `[attribute]`s), and should require that direction modifiers - // like `in`, `out`, and `in out`/`inout` be applied to the - // type (after the colon). - // - auto modifiers = ParseModifiers(parser); +/// Parse the common structured of a traditional-style parameter declaration (excluding the trailing +/// semicolon) +static void _parseTraditionalParamDeclCommonBase( + Parser* parser, + VarDeclBase* decl, + DeclaratorParseOptions options = kDeclaratorParseOptions_None) +{ + DeclaratorInfo declaratorInfo; + declaratorInfo.typeSpec = parser->ParseType(); - // We want to allow both "modern"-style and traditional-style - // parameters to appear in any modern-style parameter list, - // in order to allow programmers the flexibility to code in - // a way that feels natural and not run into lots of - // errors. - // - if(_peekModernStyleVarDecl(parser)) - { - ParamDecl* decl = parser->astBuilder->create<ModernParamDecl>(); - decl->modifiers = modifiers; - parseModernVarDeclBaseCommon(parser, decl); - return decl; - } - else - { - ParamDecl* decl = parser->astBuilder->create<ParamDecl>(); - decl->modifiers = modifiers; - _parseTraditionalParamDeclCommonBase(parser, decl); - return decl; - } - } + InitDeclarator initDeclarator = parseInitDeclarator(parser, options); + UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); - static void parseModernParamList( - Parser* parser, - CallableDecl* decl) - { - parser->ReadToken(TokenType::LParent); + // Assume it is a variable-like declarator + CompleteVarDecl(parser, decl, declaratorInfo); +} - while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - { - AddMember(decl, parseModernParamDecl(parser)); - if (AdvanceIf(parser, TokenType::RParent)) - break; - parser->ReadToken(TokenType::Comma); - } +static ParamDecl* parseModernParamDecl(Parser* parser) +{ + // TODO: For "modern" parameters, we should probably + // not allow arbitrary keyword-based modifiers (only allowing + // `[attribute]`s), and should require that direction modifiers + // like `in`, `out`, and `in out`/`inout` be applied to the + // type (after the colon). + // + auto modifiers = ParseModifiers(parser); + + // We want to allow both "modern"-style and traditional-style + // parameters to appear in any modern-style parameter list, + // in order to allow programmers the flexibility to code in + // a way that feels natural and not run into lots of + // errors. + // + if (_peekModernStyleVarDecl(parser)) + { + ParamDecl* decl = parser->astBuilder->create<ModernParamDecl>(); + decl->modifiers = modifiers; + parseModernVarDeclBaseCommon(parser, decl); + return decl; } + else + { + ParamDecl* decl = parser->astBuilder->create<ParamDecl>(); + decl->modifiers = modifiers; + _parseTraditionalParamDeclCommonBase(parser, decl); + return decl; + } +} - static NodeBase* parseFuncDecl( - Parser* parser, void* /*userData*/) +static void parseModernParamList(Parser* parser, CallableDecl* decl) +{ + parser->ReadToken(TokenType::LParent); + + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - FuncDecl* decl = parser->astBuilder->create<FuncDecl>(); + AddMember(decl, parseModernParamDecl(parser)); + if (AdvanceIf(parser, TokenType::RParent)) + break; + parser->ReadToken(TokenType::Comma); + } +} - parser->FillPosition(decl); - decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); +static NodeBase* parseFuncDecl(Parser* parser, void* /*userData*/) +{ + FuncDecl* decl = parser->astBuilder->create<FuncDecl>(); + + parser->FillPosition(decl); + decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) { parser->PushScope(decl); parseModernParamList(parser, decl); @@ -4141,7 +4077,7 @@ namespace Slang { decl->errorType = parser->ParseTypeExp(); } - if(AdvanceIf(parser, TokenType::RightArrow)) + if (AdvanceIf(parser, TokenType::RightArrow)) { decl->returnType = parser->ParseTypeExp(); } @@ -4155,853 +4091,839 @@ namespace Slang parser->PopScope(); return decl; }); - } +} - static NodeBase* parseTypeAliasDecl( - Parser* parser, void* /*userData*/) - { - TypeAliasDecl* decl = parser->astBuilder->create<TypeAliasDecl>(); +static NodeBase* parseTypeAliasDecl(Parser* parser, void* /*userData*/) +{ + TypeAliasDecl* decl = parser->astBuilder->create<TypeAliasDecl>(); - parser->FillPosition(decl); - decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); + parser->FillPosition(decl); + decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) { maybeParseGenericConstraints(parser, genericParent); - if( expect(parser, TokenType::OpAssign) ) + if (expect(parser, TokenType::OpAssign)) { decl->type = parser->ParseTypeExp(); } expect(parser, TokenType::Semicolon); return decl; }); - } +} + +// This is a catch-all syntax-construction callback to handle cases where +// a piece of syntax is fully defined by the keyword to use, along with +// the class of AST node to construct. +NodeBase* parseSimpleSyntax(Parser* parser, void* userData) +{ + SyntaxClassBase syntaxClass((ReflectClassInfo*)userData); + return (NodeBase*)syntaxClass.createInstanceImpl(parser->astBuilder); +} - // This is a catch-all syntax-construction callback to handle cases where - // a piece of syntax is fully defined by the keyword to use, along with - // the class of AST node to construct. - NodeBase* parseSimpleSyntax(Parser* parser, void* userData) +// Parse a declaration of a keyword that can be used to define further syntax. +static NodeBase* parseSyntaxDecl(Parser* parser, void* /*userData*/) +{ + // Right now the basic form is: + // + // syntax <name:id> [: <syntaxClass:id>] [= <existingKeyword:id>]; + // + // - `name` gives the name of the keyword to define. + // - `syntaxClass` is the name of an AST node class that we expect + // this syntax to construct when parsed. + // - `existingKeyword` is the name of an existing keyword that + // the new syntax should be an alias for. + + // First we parse the keyword name. + auto nameAndLoc = expectIdentifier(parser); + + // Next we look for a clause that specified the AST node class. + SyntaxClass<NodeBase> syntaxClass; + if (AdvanceIf(parser, TokenType::Colon)) { - SyntaxClassBase syntaxClass((ReflectClassInfo*) userData); - return (NodeBase*)syntaxClass.createInstanceImpl(parser->astBuilder); + // User is specifying the class that should be construted + auto classNameAndLoc = expectIdentifier(parser); + + syntaxClass = parser->astBuilder->findSyntaxClass(classNameAndLoc.name); } - // Parse a declaration of a keyword that can be used to define further syntax. - static NodeBase* parseSyntaxDecl(Parser* parser, void* /*userData*/) - { - // Right now the basic form is: - // - // syntax <name:id> [: <syntaxClass:id>] [= <existingKeyword:id>]; - // - // - `name` gives the name of the keyword to define. - // - `syntaxClass` is the name of an AST node class that we expect - // this syntax to construct when parsed. - // - `existingKeyword` is the name of an existing keyword that - // the new syntax should be an alias for. + // If the user specified a syntax class, then we will default + // to the `parseSimpleSyntax` callback that will just construct + // an instance of that type to represent the keyword in the AST. + SyntaxParseCallback parseCallback = &parseSimpleSyntax; + void* parseUserData = (void*)syntaxClass.classInfo; - // First we parse the keyword name. - auto nameAndLoc = expectIdentifier(parser); + // Next we look for an initializer that will make this keyword + // an alias for some existing keyword. + if (AdvanceIf(parser, TokenType::OpAssign)) + { + auto existingKeywordNameAndLoc = expectIdentifier(parser); - // Next we look for a clause that specified the AST node class. - SyntaxClass<NodeBase> syntaxClass; - if (AdvanceIf(parser, TokenType::Colon)) + auto existingSyntax = tryLookUpSyntaxDecl(parser, existingKeywordNameAndLoc.name); + if (!existingSyntax) + { + // TODO: diagnose: keyword did not name syntax + } + else { - // User is specifying the class that should be construted - auto classNameAndLoc = expectIdentifier(parser); + // The user is expecting us to parse our new syntax like + // the existing syntax given, so we need to override + // the callback. + parseCallback = existingSyntax->parseCallback; + parseUserData = existingSyntax->parseUserData; - syntaxClass = parser->astBuilder->findSyntaxClass(classNameAndLoc.name); + // If we don't already have a syntax class specified, then + // we will crib the one from the existing syntax, to ensure + // that we are creating a drop-in alias. + if (!syntaxClass.classInfo) + syntaxClass = existingSyntax->syntaxClass; } + } - // If the user specified a syntax class, then we will default - // to the `parseSimpleSyntax` callback that will just construct - // an instance of that type to represent the keyword in the AST. - SyntaxParseCallback parseCallback = &parseSimpleSyntax; - void* parseUserData = (void*) syntaxClass.classInfo; + // It is an error if the user didn't give us either an existing keyword + // to use to the define the callback, or a valid AST node class to construct. + // + // TODO: down the line this should be expanded so that the user can reference + // an existing *function* to use to parse the chosen syntax. + if (!syntaxClass.classInfo) + { + // TODO: diagnose: either a type or an existing keyword needs to be specified + } - // Next we look for an initializer that will make this keyword - // an alias for some existing keyword. - if (AdvanceIf(parser, TokenType::OpAssign)) - { - auto existingKeywordNameAndLoc = expectIdentifier(parser); + expect(parser, TokenType::Semicolon); - auto existingSyntax = tryLookUpSyntaxDecl(parser, existingKeywordNameAndLoc.name); - if (!existingSyntax) - { - // TODO: diagnose: keyword did not name syntax - } - else - { - // The user is expecting us to parse our new syntax like - // the existing syntax given, so we need to override - // the callback. - parseCallback = existingSyntax->parseCallback; - parseUserData = existingSyntax->parseUserData; - - // If we don't already have a syntax class specified, then - // we will crib the one from the existing syntax, to ensure - // that we are creating a drop-in alias. - if (!syntaxClass.classInfo) - syntaxClass = existingSyntax->syntaxClass; - } - } + // TODO: skip creating the declaration if anything failed, just to not screw things + // up for downstream code? - // It is an error if the user didn't give us either an existing keyword - // to use to the define the callback, or a valid AST node class to construct. - // - // TODO: down the line this should be expanded so that the user can reference - // an existing *function* to use to parse the chosen syntax. - if (!syntaxClass.classInfo) - { - // TODO: diagnose: either a type or an existing keyword needs to be specified - } + SyntaxDecl* syntaxDecl = parser->astBuilder->create<SyntaxDecl>(); + syntaxDecl->nameAndLoc = nameAndLoc; + syntaxDecl->loc = nameAndLoc.loc; + syntaxDecl->syntaxClass = syntaxClass; + syntaxDecl->parseCallback = parseCallback; + syntaxDecl->parseUserData = parseUserData; + return syntaxDecl; +} - expect(parser, TokenType::Semicolon); +// A parameter declaration in an attribute declaration. +// +// We are going to use `name: type` syntax just for simplicty, and let the type +// be optional, because we don't actually need it in all cases. +// +static ParamDecl* parseAttributeParamDecl(Parser* parser) +{ + auto nameAndLoc = expectIdentifier(parser); - // TODO: skip creating the declaration if anything failed, just to not screw things - // up for downstream code? + ParamDecl* paramDecl = parser->astBuilder->create<ParamDecl>(); + paramDecl->nameAndLoc = nameAndLoc; - SyntaxDecl* syntaxDecl = parser->astBuilder->create<SyntaxDecl>(); - syntaxDecl->nameAndLoc = nameAndLoc; - syntaxDecl->loc = nameAndLoc.loc; - syntaxDecl->syntaxClass = syntaxClass; - syntaxDecl->parseCallback = parseCallback; - syntaxDecl->parseUserData = parseUserData; - return syntaxDecl; + if (AdvanceIf(parser, TokenType::Colon)) + { + paramDecl->type = parser->ParseTypeExp(); } - // A parameter declaration in an attribute declaration. - // - // We are going to use `name: type` syntax just for simplicty, and let the type - // be optional, because we don't actually need it in all cases. - // - static ParamDecl* parseAttributeParamDecl(Parser* parser) + if (AdvanceIf(parser, TokenType::OpAssign)) { - auto nameAndLoc = expectIdentifier(parser); - - ParamDecl* paramDecl = parser->astBuilder->create<ParamDecl>(); - paramDecl->nameAndLoc = nameAndLoc; - - if(AdvanceIf(parser, TokenType::Colon)) - { - paramDecl->type = parser->ParseTypeExp(); - } + paramDecl->initExpr = parser->ParseInitExpr(); + } - if(AdvanceIf(parser, TokenType::OpAssign)) - { - paramDecl->initExpr = parser->ParseInitExpr(); - } + return paramDecl; +} - return paramDecl; +static bool shouldDeclBeCheckedForNestingValidity(ASTNodeType declType) +{ + switch (declType) + { + case ASTNodeType::ExtensionDecl: + case ASTNodeType::StructDecl: + case ASTNodeType::ClassDecl: + case ASTNodeType::GLSLInterfaceBlockDecl: + case ASTNodeType::EnumDecl: + case ASTNodeType::InterfaceDecl: + case ASTNodeType::ConstructorDecl: + case ASTNodeType::AccessorDecl: + case ASTNodeType::GetterDecl: + case ASTNodeType::SetterDecl: + case ASTNodeType::RefAccessorDecl: + case ASTNodeType::FuncDecl: + case ASTNodeType::SubscriptDecl: + case ASTNodeType::PropertyDecl: + case ASTNodeType::NamespaceDecl: + case ASTNodeType::ModuleDecl: + case ASTNodeType::FileDecl: + case ASTNodeType::GenericDecl: + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: + case ASTNodeType::TypeDefDecl: + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::UsingDecl: + case ASTNodeType::ImportDecl: + case ASTNodeType::IncludeDeclBase: + case ASTNodeType::IncludeDecl: + case ASTNodeType::ImplementingDecl: + case ASTNodeType::ModuleDeclarationDecl: + case ASTNodeType::AssocTypeDecl: return true; + default: return false; } +} + +// Can a decl of `declType` be allowed as a children of `parentType`? +static bool isDeclAllowed(bool languageServer, ASTNodeType parentType, ASTNodeType declType) +{ + // If decl is not known as a decl that can be written by the user (e.g. a synthesized decl + // type), then we just allow it. + if (!shouldDeclBeCheckedForNestingValidity(declType)) + return true; - static bool shouldDeclBeCheckedForNestingValidity(ASTNodeType declType) + switch (parentType) { + case ASTNodeType::ExtensionDecl: switch (declType) { - case ASTNodeType::ExtensionDecl: + case ASTNodeType::FuncDecl: + case ASTNodeType::SubscriptDecl: + case ASTNodeType::PropertyDecl: + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::TypeDefDecl: + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: case ASTNodeType::StructDecl: case ASTNodeType::ClassDecl: - case ASTNodeType::GLSLInterfaceBlockDecl: case ASTNodeType::EnumDecl: - case ASTNodeType::InterfaceDecl: - case ASTNodeType::ConstructorDecl: - case ASTNodeType::AccessorDecl: - case ASTNodeType::GetterDecl: - case ASTNodeType::SetterDecl: - case ASTNodeType::RefAccessorDecl: + case ASTNodeType::GenericDecl: + case ASTNodeType::ConstructorDecl: return true; + default: return false; + } + case ASTNodeType::StructDecl: + case ASTNodeType::ClassDecl: + case ASTNodeType::EnumDecl: + switch (declType) + { case ASTNodeType::FuncDecl: case ASTNodeType::SubscriptDecl: case ASTNodeType::PropertyDecl: - case ASTNodeType::NamespaceDecl: - case ASTNodeType::ModuleDecl: - case ASTNodeType::FileDecl: - case ASTNodeType::GenericDecl: + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::TypeDefDecl: case ASTNodeType::VarDecl: case ASTNodeType::LetDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::UsingDecl: - case ASTNodeType::ImportDecl: - case ASTNodeType::IncludeDeclBase: - case ASTNodeType::IncludeDecl: - case ASTNodeType::ImplementingDecl: - case ASTNodeType::ModuleDeclarationDecl: + case ASTNodeType::StructDecl: + case ASTNodeType::ClassDecl: + case ASTNodeType::EnumDecl: + case ASTNodeType::EnumCaseDecl: + case ASTNodeType::GenericDecl: + case ASTNodeType::ConstructorDecl: return true; + default: return false; + } + case ASTNodeType::InterfaceDecl: + switch (declType) + { + case ASTNodeType::FuncDecl: + case ASTNodeType::SubscriptDecl: + case ASTNodeType::PropertyDecl: case ASTNodeType::AssocTypeDecl: - return true; - default: - return false; + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: + case ASTNodeType::GenericDecl: + case ASTNodeType::ConstructorDecl: return true; + default: return false; } - } - - // Can a decl of `declType` be allowed as a children of `parentType`? - static bool isDeclAllowed(bool languageServer, ASTNodeType parentType, ASTNodeType declType) - { - // If decl is not known as a decl that can be written by the user (e.g. a synthesized decl type), - // then we just allow it. - if (!shouldDeclBeCheckedForNestingValidity(declType)) - return true; - - switch (parentType) + case ASTNodeType::GLSLInterfaceBlockDecl: + switch (declType) { - case ASTNodeType::ExtensionDecl: - switch (declType) - { - case ASTNodeType::FuncDecl: - case ASTNodeType::SubscriptDecl: - case ASTNodeType::PropertyDecl: - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - case ASTNodeType::StructDecl: - case ASTNodeType::ClassDecl: - case ASTNodeType::EnumDecl: - case ASTNodeType::GenericDecl: - case ASTNodeType::ConstructorDecl: - return true; - default: - return false; - } + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: return true; + default: return false; + } + case ASTNodeType::ConstructorDecl: + case ASTNodeType::AccessorDecl: + case ASTNodeType::GetterDecl: + case ASTNodeType::SetterDecl: + case ASTNodeType::RefAccessorDecl: + case ASTNodeType::FuncDecl: + switch (declType) + { + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::TypeDefDecl: + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: case ASTNodeType::StructDecl: case ASTNodeType::ClassDecl: case ASTNodeType::EnumDecl: - switch (declType) - { - case ASTNodeType::FuncDecl: - case ASTNodeType::SubscriptDecl: - case ASTNodeType::PropertyDecl: - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - case ASTNodeType::StructDecl: - case ASTNodeType::ClassDecl: - case ASTNodeType::EnumDecl: - case ASTNodeType::EnumCaseDecl: - case ASTNodeType::GenericDecl: - case ASTNodeType::ConstructorDecl: - return true; - default: - return false; - } - case ASTNodeType::InterfaceDecl: - switch (declType) - { - case ASTNodeType::FuncDecl: - case ASTNodeType::SubscriptDecl: - case ASTNodeType::PropertyDecl: - case ASTNodeType::AssocTypeDecl: - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - case ASTNodeType::GenericDecl: - case ASTNodeType::ConstructorDecl: - return true; - default: - return false; - } - case ASTNodeType::GLSLInterfaceBlockDecl: - switch (declType) - { - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - return true; - default: - return false; - } - case ASTNodeType::ConstructorDecl: + case ASTNodeType::GenericDecl: return true; + default: return false; + } + case ASTNodeType::SubscriptDecl: + case ASTNodeType::PropertyDecl: + switch (declType) + { case ASTNodeType::AccessorDecl: case ASTNodeType::GetterDecl: case ASTNodeType::SetterDecl: - case ASTNodeType::RefAccessorDecl: - case ASTNodeType::FuncDecl: - switch (declType) - { - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - case ASTNodeType::StructDecl: - case ASTNodeType::ClassDecl: - case ASTNodeType::EnumDecl: - case ASTNodeType::GenericDecl: - return true; - default: - return false; - } - case ASTNodeType::SubscriptDecl: - case ASTNodeType::PropertyDecl: - switch (declType) - { - case ASTNodeType::AccessorDecl: - case ASTNodeType::GetterDecl: - case ASTNodeType::SetterDecl: - case ASTNodeType::RefAccessorDecl: - return true; - default: - return false; - } - case ASTNodeType::ModuleDecl: - case ASTNodeType::FileDecl: + case ASTNodeType::RefAccessorDecl: return true; + default: return false; + } + case ASTNodeType::ModuleDecl: + case ASTNodeType::FileDecl: + case ASTNodeType::NamespaceDecl: + switch (declType) + { + case ASTNodeType::ImplementingDecl: + return parentType == ASTNodeType::FileDecl || + languageServer && parentType == ASTNodeType::ModuleDecl; + case ASTNodeType::ModuleDeclarationDecl: + return parentType == ASTNodeType::ModuleDecl || + languageServer && parentType == ASTNodeType::FileDecl; case ASTNodeType::NamespaceDecl: - switch (declType) - { - case ASTNodeType::ImplementingDecl: - return parentType == ASTNodeType::FileDecl || languageServer && parentType == ASTNodeType::ModuleDecl; - case ASTNodeType::ModuleDeclarationDecl: - return parentType == ASTNodeType::ModuleDecl || languageServer && parentType == ASTNodeType::FileDecl; - case ASTNodeType::NamespaceDecl: - case ASTNodeType::FileDecl: - case ASTNodeType::UsingDecl: - case ASTNodeType::ImportDecl: - case ASTNodeType::IncludeDecl: - case ASTNodeType::GenericDecl: - case ASTNodeType::VarDecl: - case ASTNodeType::LetDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::FuncDecl: - case ASTNodeType::SubscriptDecl: - case ASTNodeType::PropertyDecl: - case ASTNodeType::StructDecl: - case ASTNodeType::ClassDecl: - case ASTNodeType::EnumDecl: - case ASTNodeType::InterfaceDecl: - case ASTNodeType::GLSLInterfaceBlockDecl: - case ASTNodeType::ExtensionDecl: - return true; - default: - return false; - } + case ASTNodeType::FileDecl: + case ASTNodeType::UsingDecl: + case ASTNodeType::ImportDecl: + case ASTNodeType::IncludeDecl: case ASTNodeType::GenericDecl: - switch (declType) - { - case ASTNodeType::StructDecl: - case ASTNodeType::ClassDecl: - case ASTNodeType::EnumDecl: - case ASTNodeType::InterfaceDecl: - case ASTNodeType::FuncDecl: - case ASTNodeType::ConstructorDecl: - case ASTNodeType::TypeAliasDecl: - case ASTNodeType::TypeDefDecl: - case ASTNodeType::ExtensionDecl: - case ASTNodeType::SubscriptDecl: - return true; - default: - return false; - } case ASTNodeType::VarDecl: case ASTNodeType::LetDecl: case ASTNodeType::TypeDefDecl: case ASTNodeType::TypeAliasDecl: - case ASTNodeType::UsingDecl: - case ASTNodeType::ImportDecl: - case ASTNodeType::IncludeDecl: - case ASTNodeType::ImplementingDecl: - case ASTNodeType::ModuleDeclarationDecl: - case ASTNodeType::AssocTypeDecl: - return true; - default: - return true; + case ASTNodeType::FuncDecl: + case ASTNodeType::SubscriptDecl: + case ASTNodeType::PropertyDecl: + case ASTNodeType::StructDecl: + case ASTNodeType::ClassDecl: + case ASTNodeType::EnumDecl: + case ASTNodeType::InterfaceDecl: + case ASTNodeType::GLSLInterfaceBlockDecl: + case ASTNodeType::ExtensionDecl: return true; + default: return false; } + case ASTNodeType::GenericDecl: + switch (declType) + { + case ASTNodeType::StructDecl: + case ASTNodeType::ClassDecl: + case ASTNodeType::EnumDecl: + case ASTNodeType::InterfaceDecl: + case ASTNodeType::FuncDecl: + case ASTNodeType::ConstructorDecl: + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::TypeDefDecl: + case ASTNodeType::ExtensionDecl: + case ASTNodeType::SubscriptDecl: return true; + default: return false; + } + case ASTNodeType::VarDecl: + case ASTNodeType::LetDecl: + case ASTNodeType::TypeDefDecl: + case ASTNodeType::TypeAliasDecl: + case ASTNodeType::UsingDecl: + case ASTNodeType::ImportDecl: + case ASTNodeType::IncludeDecl: + case ASTNodeType::ImplementingDecl: + case ASTNodeType::ModuleDeclarationDecl: + case ASTNodeType::AssocTypeDecl: return true; + default: return true; } +} - // Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers. - // - // These are distinct from `syntax` declarations, because their names don't get added - // to the current scope using their default name. +// Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers. +// +// These are distinct from `syntax` declarations, because their names don't get added +// to the current scope using their default name. +// +// Also, attribute-specific code doesn't get invokved during parsing. We always parse +// using the default attribute-parsing logic and then all specialized behavior takes +// place during semantic checking. +// +static NodeBase* parseAttributeSyntaxDecl(Parser* parser, void* /*userData*/) +{ + // Right now the basic form is: // - // Also, attribute-specific code doesn't get invokved during parsing. We always parse - // using the default attribute-parsing logic and then all specialized behavior takes - // place during semantic checking. + // attribute_syntax <name:id> : <syntaxClass:id>; // - static NodeBase* parseAttributeSyntaxDecl(Parser* parser, void* /*userData*/) - { - // Right now the basic form is: - // - // attribute_syntax <name:id> : <syntaxClass:id>; - // - // - `name` gives the name of the attribute to define. - // - `syntaxClass` is the name of an AST node class that we expect - // this attribute to create when checked. - // - `existingKeyword` is the name of an existing keyword that - // the new syntax should be an alias for. + // - `name` gives the name of the attribute to define. + // - `syntaxClass` is the name of an AST node class that we expect + // this attribute to create when checked. + // - `existingKeyword` is the name of an existing keyword that + // the new syntax should be an alias for. - expect(parser, TokenType::LBracket); + expect(parser, TokenType::LBracket); - // First we parse the attribute name. - auto nameAndLoc = expectIdentifier(parser); + // First we parse the attribute name. + auto nameAndLoc = expectIdentifier(parser); - AttributeDecl* attrDecl = parser->astBuilder->create<AttributeDecl>(); - if(AdvanceIf(parser, TokenType::LParent)) + AttributeDecl* attrDecl = parser->astBuilder->create<AttributeDecl>(); + if (AdvanceIf(parser, TokenType::LParent)) + { + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - while(!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - { - auto param = parseAttributeParamDecl(parser); + auto param = parseAttributeParamDecl(parser); - AddMember(attrDecl, param); + AddMember(attrDecl, param); - if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) - break; + if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + break; - expect(parser, TokenType::Comma); - } + expect(parser, TokenType::Comma); } + } - expect(parser, TokenType::RBracket); + expect(parser, TokenType::RBracket); - // TODO: we should allow parameters to be specified here, to cut down - // on the amount of per-attribute-type logic that has to occur later. + // TODO: we should allow parameters to be specified here, to cut down + // on the amount of per-attribute-type logic that has to occur later. - // Next we look for a clause that specified the AST node class. - SyntaxClass<NodeBase> syntaxClass; - if (AdvanceIf(parser, TokenType::Colon)) - { - // User is specifying the class that should be construted - auto classNameAndLoc = expectIdentifier(parser); - syntaxClass = parser->astBuilder->findSyntaxClass(classNameAndLoc.name); + // Next we look for a clause that specified the AST node class. + SyntaxClass<NodeBase> syntaxClass; + if (AdvanceIf(parser, TokenType::Colon)) + { + // User is specifying the class that should be construted + auto classNameAndLoc = expectIdentifier(parser); + syntaxClass = parser->astBuilder->findSyntaxClass(classNameAndLoc.name); - assert(syntaxClass.classInfo); - } - else - { - // For now we don't support the alternative approach where - // an existing piece of syntax is named to provide the parsing - // support. + assert(syntaxClass.classInfo); + } + else + { + // For now we don't support the alternative approach where + // an existing piece of syntax is named to provide the parsing + // support. - // TODO: diagnose: a syntax class must be specified. - } + // TODO: diagnose: a syntax class must be specified. + } - expect(parser, TokenType::Semicolon); + expect(parser, TokenType::Semicolon); - // TODO: skip creating the declaration if anything failed, just to not screw things - // up for downstream code? + // TODO: skip creating the declaration if anything failed, just to not screw things + // up for downstream code? - attrDecl->nameAndLoc = nameAndLoc; - attrDecl->loc = nameAndLoc.loc; - attrDecl->syntaxClass = syntaxClass; - return attrDecl; - } + attrDecl->nameAndLoc = nameAndLoc; + attrDecl->loc = nameAndLoc.loc; + attrDecl->syntaxClass = syntaxClass; + return attrDecl; +} - static void addSpecialGLSLModifiersBasedOnType( - Parser* parser, - Decl* decl, - Modifiers* modifiers) - { - auto varDeclBase = as<VarDeclBase>(decl); - if (!varDeclBase) return; - auto declRefExpr = as<DeclRefExpr>(varDeclBase->type.exp); - if (!declRefExpr) return; - auto bindingMod = modifiers->findModifier<GLSLBindingAttribute>(); - if (!bindingMod) return; +static void addSpecialGLSLModifiersBasedOnType(Parser* parser, Decl* decl, Modifiers* modifiers) +{ + auto varDeclBase = as<VarDeclBase>(decl); + if (!varDeclBase) + return; + auto declRefExpr = as<DeclRefExpr>(varDeclBase->type.exp); + if (!declRefExpr) + return; + auto bindingMod = modifiers->findModifier<GLSLBindingAttribute>(); + if (!bindingMod) + return; - // here is a problem; we link types into a literal in IR stage post parse - // but, order (top down) mattter when parsing atomic_uint offset - // more over, we can have patterns like: offset = 20, no offset [+4], offset = 16. - // Therefore we must parse all in order. The issue then is we will struggle to - // subsitute atomic_uint for storage buffers... - if (auto name = declRefExpr->name) + // here is a problem; we link types into a literal in IR stage post parse + // but, order (top down) mattter when parsing atomic_uint offset + // more over, we can have patterns like: offset = 20, no offset [+4], offset = 16. + // Therefore we must parse all in order. The issue then is we will struggle to + // subsitute atomic_uint for storage buffers... + if (auto name = declRefExpr->name) + { + if (name->text.equals("atomic_uint")) { - if (name->text.equals("atomic_uint")) + if (!modifiers->findModifier<GLSLOffsetLayoutAttribute>()) { - if (!modifiers->findModifier<GLSLOffsetLayoutAttribute>()) - { - const int64_t nextOffset = parser->getNextBindingOffset(bindingMod->binding); - GLSLOffsetLayoutAttribute* modifier = parser->astBuilder->create<GLSLOffsetLayoutAttribute>(); - modifier->keywordName = NULL; //no keyword name given - modifier->loc = bindingMod->loc; //has no location in file, set to parent binding - modifier->offset = nextOffset; - - Modifiers newModifier; - newModifier.first = modifier; - _addModifiers(decl, newModifier); - } + const int64_t nextOffset = parser->getNextBindingOffset(bindingMod->binding); + GLSLOffsetLayoutAttribute* modifier = + parser->astBuilder->create<GLSLOffsetLayoutAttribute>(); + modifier->keywordName = NULL; // no keyword name given + modifier->loc = bindingMod->loc; // has no location in file, set to parent binding + modifier->offset = nextOffset; + + Modifiers newModifier; + newModifier.first = modifier; + _addModifiers(decl, newModifier); } } } - // Finish up work on a declaration that was parsed - static void CompleteDecl( - Parser* parser, - Decl* decl, - ContainerDecl* containerDecl, - Modifiers modifiers) - { +} +// Finish up work on a declaration that was parsed +static void CompleteDecl( + Parser* parser, + Decl* decl, + ContainerDecl* containerDecl, + Modifiers modifiers) +{ - // If this is a namespace and already added, we don't want to add to the parent - // Or add any modifiers - if (as<NamespaceDecl>(decl) && decl->parentDecl) - { - return; - } + // If this is a namespace and already added, we don't want to add to the parent + // Or add any modifiers + if (as<NamespaceDecl>(decl) && decl->parentDecl) + { + return; + } - // Add any modifiers we parsed before the declaration to the list - // of modifiers on the declaration itself. - // - // We need to be careful, because if `decl` is a generic declaration, - // then we really want the modifiers to apply to the inner declaration. - // - Decl* declToModify = decl; - if(auto genericDecl = as<GenericDecl>(decl)) - declToModify = genericDecl->inner; + // Add any modifiers we parsed before the declaration to the list + // of modifiers on the declaration itself. + // + // We need to be careful, because if `decl` is a generic declaration, + // then we really want the modifiers to apply to the inner declaration. + // + Decl* declToModify = decl; + if (auto genericDecl = as<GenericDecl>(decl)) + declToModify = genericDecl->inner; - if (as<ModuleDeclarationDecl>(decl)) + if (as<ModuleDeclarationDecl>(decl)) + { + // Modifiers on module declaration should be added to the module itself. + auto moduleDecl = getModuleDecl(containerDecl); + if (moduleDecl) { - // Modifiers on module declaration should be added to the module itself. - auto moduleDecl = getModuleDecl(containerDecl); - if (moduleDecl) - { - _addModifiers(moduleDecl, modifiers); - } + _addModifiers(moduleDecl, modifiers); } - else + } + else + { + if (parser->options.allowGLSLInput) { - if (parser->options.allowGLSLInput) - { - addSpecialGLSLModifiersBasedOnType(parser, declToModify, &modifiers); - } - _addModifiers(declToModify, modifiers); + addSpecialGLSLModifiersBasedOnType(parser, declToModify, &modifiers); } + _addModifiers(declToModify, modifiers); + } - if (containerDecl) + if (containerDecl) + { + // Check that the declaration is actually allowed to be nested inside container. + if (!isDeclAllowed( + parser->options.isInLanguageServer, + containerDecl->astNodeType, + decl->astNodeType)) { - // Check that the declaration is actually allowed to be nested inside container. - if (!isDeclAllowed(parser->options.isInLanguageServer, containerDecl->astNodeType, decl->astNodeType)) - { - parser->sink->diagnose(decl->loc, Diagnostics::declNotAllowed, decl->astNodeType); - } - else + parser->sink->diagnose(decl->loc, Diagnostics::declNotAllowed, decl->astNodeType); + } + else + { + // For generic decls, we also need to check if the inner decl type is allowed to be + // nested here. + if (declToModify && declToModify != decl) { - // For generic decls, we also need to check if the inner decl type is allowed to be - // nested here. - if (declToModify && declToModify != decl) + if (!isDeclAllowed( + parser->options.isInLanguageServer, + containerDecl->astNodeType, + declToModify->astNodeType)) { - if (!isDeclAllowed(parser->options.isInLanguageServer, containerDecl->astNodeType, declToModify->astNodeType)) - { - parser->sink->diagnose(decl->loc, Diagnostics::declNotAllowed, declToModify->astNodeType); - } + parser->sink->diagnose( + decl->loc, + Diagnostics::declNotAllowed, + declToModify->astNodeType); } } + } - if (!as<GenericDecl>(containerDecl)) - { - // Make sure the decl is properly nested inside its lexical parent - AddMember(containerDecl, decl); - } + if (!as<GenericDecl>(containerDecl)) + { + // Make sure the decl is properly nested inside its lexical parent + AddMember(containerDecl, decl); } } +} + +static DeclBase* ParseDeclWithModifiers( + Parser* parser, + ContainerDecl* containerDecl, + Modifiers modifiers) +{ + DeclBase* decl = nullptr; + + struct RestorePendingModifiersRAII + { + Modifiers* oldValue; + Parser* parser; + ~RestorePendingModifiersRAII() { parser->pendingModifiers = oldValue; } + }; + RestorePendingModifiersRAII restorePendingModifiersRAII{parser->pendingModifiers, parser}; + parser->pendingModifiers = &modifiers; - static DeclBase* ParseDeclWithModifiers( - Parser* parser, - ContainerDecl* containerDecl, - Modifiers modifiers ) + auto loc = parser->tokenReader.peekLoc(); + switch (peekTokenType(parser)) { - DeclBase* decl = nullptr; - - struct RestorePendingModifiersRAII + case TokenType::Identifier: { - Modifiers* oldValue; - Parser* parser; - ~RestorePendingModifiersRAII() + // A declaration that starts with an identifier might be: + // + // - A keyword-based declaration (e.g., `cbuffer ...`) + // - The beginning of a type in a declarator-based declaration (e.g., `int ...`) + + // First we will check whether we can use the identifier token + // as a declaration keyword and parse a declaration using + // its associated callback: + Decl* parsedDecl = nullptr; + if (tryParseUsingSyntaxDecl<Decl>(parser, &parsedDecl)) { - parser->pendingModifiers = oldValue; + decl = parsedDecl; + break; } - }; - RestorePendingModifiersRAII restorePendingModifiersRAII{ parser->pendingModifiers, parser }; - parser->pendingModifiers = &modifiers; - auto loc = parser->tokenReader.peekLoc(); - switch (peekTokenType(parser)) - { - case TokenType::Identifier: + // This can also be a GLSL style buffer block declaration. + if (parser->options.allowGLSLInput) { - // A declaration that starts with an identifier might be: - // - // - A keyword-based declaration (e.g., `cbuffer ...`) - // - The beginning of a type in a declarator-based declaration (e.g., `int ...`) - - // First we will check whether we can use the identifier token - // as a declaration keyword and parse a declaration using - // its associated callback: - Decl* parsedDecl = nullptr; - if (tryParseUsingSyntaxDecl<Decl>(parser, &parsedDecl)) + auto getLayoutArg = [&](const char* defaultLayout) + { + if (auto dataLayoutMod = modifiers.findModifier<GLSLBufferDataLayoutModifier>()) + { + if (as<GLSLStd140Modifier>(dataLayoutMod)) + return "Std140DataLayout"; + else if (as<GLSLStd430Modifier>(dataLayoutMod)) + return "Std430DataLayout"; + else if (as<GLSLScalarModifier>(dataLayoutMod)) + return "ScalarDataLayout"; + } + return defaultLayout; + }; + if (AdvanceIf(parser, "buffer")) { - decl = parsedDecl; + decl = as<Decl>( + parseGLSLShaderStorageBufferDecl(parser, getLayoutArg("Std430DataLayout"))); break; } - - // This can also be a GLSL style buffer block declaration. - if (parser->options.allowGLSLInput) + else if (auto mod = findPotentialGLSLInterfaceBlockModifier(parser, modifiers)) { - auto getLayoutArg = [&](const char* defaultLayout) - { - if (auto dataLayoutMod = modifiers.findModifier<GLSLBufferDataLayoutModifier>()) - { - if (as<GLSLStd140Modifier>(dataLayoutMod)) - return "Std140DataLayout"; - else if (as<GLSLStd430Modifier>(dataLayoutMod)) - return "Std430DataLayout"; - else if (as<GLSLScalarModifier>(dataLayoutMod)) - return "ScalarDataLayout"; - } - return defaultLayout; - }; - if (AdvanceIf(parser, "buffer")) + if (!parser->LookAheadToken(TokenType::LBrace, 1)) + { + goto endOfGlslBufferBlock; + } + + if (as<HLSLUniformModifier>(mod)) { - decl = as<Decl>(parseGLSLShaderStorageBufferDecl(parser, getLayoutArg("Std430DataLayout"))); + decl = as<Decl>(parseHLSLCBufferDecl(parser, nullptr)); break; } - else if (auto mod = findPotentialGLSLInterfaceBlockModifier(parser, modifiers)) + else { - if (!parser->LookAheadToken(TokenType::LBrace, 1)) - { - goto endOfGlslBufferBlock; - } - - if (as<HLSLUniformModifier>(mod)) + bool isGLSLBuiltinRedeclaration = + parser->tokenReader.peekToken().getContent().startsWith("gl_"); + decl = ParseBufferBlockDecl(parser, "", nullptr); + if (isGLSLBuiltinRedeclaration) { - decl = as<Decl>(parseHLSLCBufferDecl(parser, nullptr)); - break; - } - else - { - bool isGLSLBuiltinRedeclaration = parser->tokenReader.peekToken().getContent().startsWith("gl_"); - decl = ParseBufferBlockDecl(parser, "", nullptr); - if (isGLSLBuiltinRedeclaration) - { - // Ignore builtin redeclaration. - decl = parser->astBuilder->create<EmptyDecl>(); - decl->loc = loc; - } - break; + // Ignore builtin redeclaration. + decl = parser->astBuilder->create<EmptyDecl>(); + decl->loc = loc; } + break; } - endOfGlslBufferBlock:; } - - // Our final fallback case is to assume that the user is - // probably writing a C-style declarator-based declaration. - decl = ParseDeclaratorDecl(parser, containerDecl, modifiers); - break; - } - break; - - // It is valid in HLSL/GLSL to have an "empty" declaration - // that consists of just a semicolon. In particular, this - // gets used a lot in GLSL to attach custom semantics to - // shader input or output. - // - case TokenType::Semicolon: - { - advanceToken(parser); - - decl = parser->astBuilder->create<EmptyDecl>(); - decl->loc = loc; + endOfGlslBufferBlock:; } - break; - case TokenType::LBrace: - case TokenType::LParent: - { - // We shouldn't be seeing an LBrace or an LParent when expecting a decl. - // However recovery logic may lead us here. In this case we just - // skip the whole `{}` block and return an empty decl. - SkipBalancedToken(&parser->tokenReader); - decl = parser->astBuilder->create<EmptyDecl>(); - decl->loc = loc; - } - break; - // If nothing else matched, we try to parse an "ordinary" declarator-based declaration - default: + // Our final fallback case is to assume that the user is + // probably writing a C-style declarator-based declaration. decl = ParseDeclaratorDecl(parser, containerDecl, modifiers); break; } + break; - if (decl) + // It is valid in HLSL/GLSL to have an "empty" declaration + // that consists of just a semicolon. In particular, this + // gets used a lot in GLSL to attach custom semantics to + // shader input or output. + // + case TokenType::Semicolon: { - if( auto dd = as<Decl>(decl) ) - { - CompleteDecl(parser, dd, containerDecl, modifiers); - } - else if(auto declGroup = as<DeclGroup>(decl)) - { - // We are going to add the same modifiers to *all* of these declarations, - // so we want to give later passes a way to detect which modifiers - // were shared, vs. which ones are specific to a single declaration. - - auto sharedModifiers = parser->astBuilder->create<SharedModifiers>(); - sharedModifiers->next = modifiers.first; - modifiers.first = sharedModifiers; + advanceToken(parser); - for( auto subDecl : declGroup->decls ) - { - CompleteDecl(parser, subDecl, containerDecl, modifiers); - } - } + decl = parser->astBuilder->create<EmptyDecl>(); + decl->loc = loc; } - return decl; - } + break; - static DeclBase* ParseDecl( - Parser* parser, - ContainerDecl* containerDecl) - { - Modifiers modifiers = ParseModifiers(parser); - return ParseDeclWithModifiers(parser, containerDecl, modifiers); + case TokenType::LBrace: + case TokenType::LParent: + { + // We shouldn't be seeing an LBrace or an LParent when expecting a decl. + // However recovery logic may lead us here. In this case we just + // skip the whole `{}` block and return an empty decl. + SkipBalancedToken(&parser->tokenReader); + decl = parser->astBuilder->create<EmptyDecl>(); + decl->loc = loc; + } + break; + // If nothing else matched, we try to parse an "ordinary" declarator-based declaration + default: decl = ParseDeclaratorDecl(parser, containerDecl, modifiers); break; } - static Decl* ParseSingleDecl( - Parser* parser, - ContainerDecl* containerDecl) + if (decl) { - auto declBase = ParseDecl(parser, containerDecl); - if(!declBase) - return nullptr; - if( auto decl = as<Decl>(declBase) ) + if (auto dd = as<Decl>(decl)) { - return decl; + CompleteDecl(parser, dd, containerDecl, modifiers); } - else if( auto declGroup = as<DeclGroup>(declBase) ) + else if (auto declGroup = as<DeclGroup>(decl)) { - if( declGroup->decls.getCount() == 1 ) + // We are going to add the same modifiers to *all* of these declarations, + // so we want to give later passes a way to detect which modifiers + // were shared, vs. which ones are specific to a single declaration. + + auto sharedModifiers = parser->astBuilder->create<SharedModifiers>(); + sharedModifiers->next = modifiers.first; + modifiers.first = sharedModifiers; + + for (auto subDecl : declGroup->decls) { - return declGroup->decls[0]; + CompleteDecl(parser, subDecl, containerDecl, modifiers); } } - - parser->sink->diagnose(declBase->loc, Diagnostics::unimplemented, "didn't expect multiple declarations here"); - return nullptr; } + return decl; +} - static bool parseGLSLGlobalDecl(Parser* parser, ContainerDecl* containerDecl) - { - SLANG_UNUSED(containerDecl); +static DeclBase* ParseDecl(Parser* parser, ContainerDecl* containerDecl) +{ + Modifiers modifiers = ParseModifiers(parser); + return ParseDeclWithModifiers(parser, containerDecl, modifiers); +} - if (AdvanceIf(parser, "precision")) - { - // skip global precision declarations. - parser->ReadToken(); - parser->ReadToken(); - parser->ReadToken(TokenType::Semicolon); - return true; - } - return false; +static Decl* ParseSingleDecl(Parser* parser, ContainerDecl* containerDecl) +{ + auto declBase = ParseDecl(parser, containerDecl); + if (!declBase) + return nullptr; + if (auto decl = as<Decl>(declBase)) + { + return decl; } - - static void parseDecls( - Parser* parser, - ContainerDecl* containerDecl, - MatchedTokenType matchType) + else if (auto declGroup = as<DeclGroup>(declBase)) { - Token closingBraceToken; - while (!AdvanceIfMatch(parser, matchType, &closingBraceToken)) + if (declGroup->decls.getCount() == 1) { - if (parser->options.allowGLSLInput) - { - if (parseGLSLGlobalDecl(parser, containerDecl)) - continue; - } - ParseDecl(parser, containerDecl); + return declGroup->decls[0]; } - containerDecl->closingSourceLoc = closingBraceToken.loc; } - static void parseDeclBody( - Parser* parser, - ContainerDecl* parent) - { - parser->PushScope(parent); + parser->sink->diagnose( + declBase->loc, + Diagnostics::unimplemented, + "didn't expect multiple declarations here"); + return nullptr; +} - parser->ReadToken(TokenType::LBrace); - parseDecls(parser, parent, MatchedTokenType::CurlyBraces); +static bool parseGLSLGlobalDecl(Parser* parser, ContainerDecl* containerDecl) +{ + SLANG_UNUSED(containerDecl); - parser->PopScope(); + if (AdvanceIf(parser, "precision")) + { + // skip global precision declarations. + parser->ReadToken(); + parser->ReadToken(); + parser->ReadToken(TokenType::Semicolon); + return true; } + return false; +} - - void Parser::parseSourceFile(ContainerDecl* program) +static void parseDecls(Parser* parser, ContainerDecl* containerDecl, MatchedTokenType matchType) +{ + Token closingBraceToken; + while (!AdvanceIfMatch(parser, matchType, &closingBraceToken)) { - SLANG_AST_BUILDER_RAII(astBuilder); - - if (outerScope) + if (parser->options.allowGLSLInput) { - currentScope = outerScope; + if (parseGLSLGlobalDecl(parser, containerDecl)) + continue; } + ParseDecl(parser, containerDecl); + } + containerDecl->closingSourceLoc = closingBraceToken.loc; +} - currentModule = getModuleDecl(program); +static void parseDeclBody(Parser* parser, ContainerDecl* parent) +{ + parser->PushScope(parent); - PushScope(program); + parser->ReadToken(TokenType::LBrace); + parseDecls(parser, parent, MatchedTokenType::CurlyBraces); - // A single `ModuleDecl` might span multiple source files, so it - // is possible that we are parsing a new source file into a module - // that has already been created and filled in for a previous - // source file. - // - // If this is the first source file for the module then we expect - // its location information to be invalid, and we will set it to - // refer to the start of the first source file. - // - // This convention is reasonable for any single-source-file module, - // and about as good as possible for multiple-file modules. - // - if(!program->loc.isValid()) - { - program->loc = tokenReader.peekLoc(); - } + parser->PopScope(); +} - if (options.allowGLSLInput) - { - auto glslName = getName(this, "glsl"); - if (program->nameAndLoc.name != glslName) - { - auto importDecl = astBuilder->create<ImportDecl>(); - importDecl->moduleNameAndLoc.name = glslName; - importDecl->scope = currentScope; - AddMember(currentScope, importDecl); - } - auto glslModuleModifier = astBuilder->create<GLSLModuleModifier>(); - addModifier(currentModule, glslModuleModifier); - } - parseDecls(this, program, MatchedTokenType::File); - PopScope(); +void Parser::parseSourceFile(ContainerDecl* program) +{ + SLANG_AST_BUILDER_RAII(astBuilder); - SLANG_RELEASE_ASSERT(currentScope == outerScope); - currentScope = nullptr; + if (outerScope) + { + currentScope = outerScope; } - Decl* Parser::ParseStruct() + currentModule = getModuleDecl(program); + + PushScope(program); + + // A single `ModuleDecl` might span multiple source files, so it + // is possible that we are parsing a new source file into a module + // that has already been created and filled in for a previous + // source file. + // + // If this is the first source file for the module then we expect + // its location information to be invalid, and we will set it to + // refer to the start of the first source file. + // + // This convention is reasonable for any single-source-file module, + // and about as good as possible for multiple-file modules. + // + if (!program->loc.isValid()) { - StructDecl* rs = astBuilder->create<StructDecl>(); - ReadToken("struct"); - FillPosition(rs); + program->loc = tokenReader.peekLoc(); + } - // The `struct` keyword may optionally be followed by - // attributes that appertain to the struct declaration - // itself, and not to any variables declared using this - // type specifier. - // - // TODO: We don't yet correctly associate attributes with - // a variable decarlation vs. a struct type when a variable - // is declared with a struct type specified. - // - if(LookAheadToken(TokenType::LBracket)) + if (options.allowGLSLInput) + { + auto glslName = getName(this, "glsl"); + if (program->nameAndLoc.name != glslName) { - Modifier** modifierLink = &rs->modifiers.first; - ParseSquareBracketAttributes(this, &modifierLink); + auto importDecl = astBuilder->create<ImportDecl>(); + importDecl->moduleNameAndLoc.name = glslName; + importDecl->scope = currentScope; + AddMember(currentScope, importDecl); } + auto glslModuleModifier = astBuilder->create<GLSLModuleModifier>(); + addModifier(currentModule, glslModuleModifier); + } - // Skip completion request token to prevent producing a type named completion request. - AdvanceIf(this, TokenType::CompletionRequest); + parseDecls(this, program, MatchedTokenType::File); + PopScope(); - if (LookAheadToken(TokenType::Identifier)) - { - rs->nameAndLoc = expectIdentifier(this); - } - else - { - rs->nameAndLoc.name = generateName(this); - rs->nameAndLoc.loc = rs->loc; - } - return parseOptGenericDecl(this, [&](GenericDecl* genericParent) + SLANG_RELEASE_ASSERT(currentScope == outerScope); + currentScope = nullptr; +} + +Decl* Parser::ParseStruct() +{ + StructDecl* rs = astBuilder->create<StructDecl>(); + ReadToken("struct"); + FillPosition(rs); + + // The `struct` keyword may optionally be followed by + // attributes that appertain to the struct declaration + // itself, and not to any variables declared using this + // type specifier. + // + // TODO: We don't yet correctly associate attributes with + // a variable decarlation vs. a struct type when a variable + // is declared with a struct type specified. + // + if (LookAheadToken(TokenType::LBracket)) + { + Modifier** modifierLink = &rs->modifiers.first; + ParseSquareBracketAttributes(this, &modifierLink); + } + + // Skip completion request token to prevent producing a type named completion request. + AdvanceIf(this, TokenType::CompletionRequest); + + if (LookAheadToken(TokenType::Identifier)) + { + rs->nameAndLoc = expectIdentifier(this); + } + else + { + rs->nameAndLoc.name = generateName(this); + rs->nameAndLoc.loc = rs->loc; + } + return parseOptGenericDecl( + this, + [&](GenericDecl* genericParent) { // We allow for an inheritance clause on a `struct` // so that it can conform to interfaces. @@ -5023,103 +4945,105 @@ namespace Slang parseDeclBody(this, rs); return rs; }); - } +} - ClassDecl* Parser::ParseClass() - { - ClassDecl* rs = astBuilder->create<ClassDecl>(); - ReadToken("class"); +ClassDecl* Parser::ParseClass() +{ + ClassDecl* rs = astBuilder->create<ClassDecl>(); + ReadToken("class"); - AdvanceIf(this, TokenType::CompletionRequest); + AdvanceIf(this, TokenType::CompletionRequest); - FillPosition(rs); - rs->nameAndLoc = expectIdentifier(this); + FillPosition(rs); + rs->nameAndLoc = expectIdentifier(this); - parseOptionalInheritanceClause(this, rs); + parseOptionalInheritanceClause(this, rs); - parseDeclBody(this, rs); - return rs; - } + parseDeclBody(this, rs); + return rs; +} - Decl* Parser::ParseGLSLInterfaceBlock() - { - // - // MyBlockName { float myData[]; } myBufferName; - // - // This returns a struct decl representing the fields +Decl* Parser::ParseGLSLInterfaceBlock() +{ + // + // MyBlockName { float myData[]; } myBufferName; + // + // This returns a struct decl representing the fields - auto* rs = astBuilder->create<GLSLInterfaceBlockDecl>(); - FillPosition(rs); + auto* rs = astBuilder->create<GLSLInterfaceBlockDecl>(); + FillPosition(rs); - // As for struct, skip completion request token to prevent producing a - // block named completion request. - AdvanceIf(this, TokenType::CompletionRequest); - rs->nameAndLoc = expectIdentifier(this); - parseDeclBody(this, rs); - return rs; - } + // As for struct, skip completion request token to prevent producing a + // block named completion request. + AdvanceIf(this, TokenType::CompletionRequest); + rs->nameAndLoc = expectIdentifier(this); + parseDeclBody(this, rs); + return rs; +} - static EnumCaseDecl* parseEnumCaseDecl(Parser* parser) - { - EnumCaseDecl* decl = parser->astBuilder->create<EnumCaseDecl>(); - parser->FillPosition(decl); - - decl->nameAndLoc = expectIdentifier(parser); +static EnumCaseDecl* parseEnumCaseDecl(Parser* parser) +{ + EnumCaseDecl* decl = parser->astBuilder->create<EnumCaseDecl>(); + parser->FillPosition(decl); - if(AdvanceIf(parser, TokenType::OpAssign)) - { - decl->tagExpr = parser->ParseArgExpr(); - } + decl->nameAndLoc = expectIdentifier(parser); - return decl; + if (AdvanceIf(parser, TokenType::OpAssign)) + { + decl->tagExpr = parser->ParseArgExpr(); } - static Decl* parseEnumDecl(Parser* parser) - { - EnumDecl* decl = parser->astBuilder->create<EnumDecl>(); + return decl; +} - parser->ReadToken("enum"); +static Decl* parseEnumDecl(Parser* parser) +{ + EnumDecl* decl = parser->astBuilder->create<EnumDecl>(); - // HACK: allow the user to write `enum class` in case - // they are trying to share a header between C++ and Slang. - // - // TODO: diagnose this with a warning some day, and move - // toward deprecating it. - // - bool isEnumClass = AdvanceIf(parser, "class"); - bool isUnscoped = false; + parser->ReadToken("enum"); - if (!isEnumClass) + // HACK: allow the user to write `enum class` in case + // they are trying to share a header between C++ and Slang. + // + // TODO: diagnose this with a warning some day, and move + // toward deprecating it. + // + bool isEnumClass = AdvanceIf(parser, "class"); + bool isUnscoped = false; + + if (!isEnumClass) + { + if (parser->options.optionSet.getBoolOption(CompilerOptionName::UnscopedEnum)) { - if (parser->options.optionSet.getBoolOption(CompilerOptionName::UnscopedEnum)) - { - isUnscoped = true; - } + isUnscoped = true; } + } - AdvanceIf(parser, TokenType::CompletionRequest); + AdvanceIf(parser, TokenType::CompletionRequest); - parser->FillPosition(decl); + parser->FillPosition(decl); - if (parser->tokenReader.peekTokenType() != TokenType::Identifier) - { - decl->nameAndLoc.name = generateName(parser); - decl->nameAndLoc.loc = decl->loc; - isUnscoped = true; - } - else - { - decl->nameAndLoc = expectIdentifier(parser); - } + if (parser->tokenReader.peekTokenType() != TokenType::Identifier) + { + decl->nameAndLoc.name = generateName(parser); + decl->nameAndLoc.loc = decl->loc; + isUnscoped = true; + } + else + { + decl->nameAndLoc = expectIdentifier(parser); + } - // If the type needs to be unscoped, insert modifiers to make it so. - if (isUnscoped) - { - addModifier(decl, parser->astBuilder->create<UnscopedEnumAttribute>()); - addModifier(decl, parser->astBuilder->create<TransparentModifier>()); - } + // If the type needs to be unscoped, insert modifiers to make it so. + if (isUnscoped) + { + addModifier(decl, parser->astBuilder->create<UnscopedEnumAttribute>()); + addModifier(decl, parser->astBuilder->create<TransparentModifier>()); + } - return parseOptGenericDecl(parser, [&](GenericDecl* genericParent) + return parseOptGenericDecl( + parser, + [&](GenericDecl* genericParent) { parseOptionalInheritanceClause(parser, decl); maybeParseGenericConstraints(parser, genericParent); @@ -5131,7 +5055,7 @@ namespace Slang EnumCaseDecl* caseDecl = parseEnumCaseDecl(parser); AddMember(decl, caseDecl); - if(AdvanceIf(parser, TokenType::RBrace)) + if (AdvanceIf(parser, TokenType::RBrace)) break; parser->ReadToken(TokenType::Comma); @@ -5140,1064 +5064,1054 @@ namespace Slang decl->closingSourceLoc = closingToken.loc; return decl; }); - } +} - static Stmt* ParseSwitchStmt(Parser* parser) - { - SwitchStmt* stmt = parser->astBuilder->create<SwitchStmt>(); - parser->FillPosition(stmt); - parser->ReadToken("switch"); - parser->ReadToken(TokenType::LParent); - stmt->condition = parser->ParseExpression(); - parser->ReadToken(TokenType::RParent); - stmt->body = parser->parseBlockStatement(); - return stmt; - } +static Stmt* ParseSwitchStmt(Parser* parser) +{ + SwitchStmt* stmt = parser->astBuilder->create<SwitchStmt>(); + parser->FillPosition(stmt); + parser->ReadToken("switch"); + parser->ReadToken(TokenType::LParent); + stmt->condition = parser->ParseExpression(); + parser->ReadToken(TokenType::RParent); + stmt->body = parser->parseBlockStatement(); + return stmt; +} - static Stmt* ParseCaseStmt(Parser* parser) - { - CaseStmt* stmt = parser->astBuilder->create<CaseStmt>(); - parser->FillPosition(stmt); - parser->ReadToken("case"); - stmt->expr = parser->ParseExpression(); - parser->ReadToken(TokenType::Colon); - return stmt; - } +static Stmt* ParseCaseStmt(Parser* parser) +{ + CaseStmt* stmt = parser->astBuilder->create<CaseStmt>(); + parser->FillPosition(stmt); + parser->ReadToken("case"); + stmt->expr = parser->ParseExpression(); + parser->ReadToken(TokenType::Colon); + return stmt; +} - static Stmt* ParseDefaultStmt(Parser* parser) +static Stmt* ParseDefaultStmt(Parser* parser) +{ + DefaultStmt* stmt = parser->astBuilder->create<DefaultStmt>(); + parser->FillPosition(stmt); + parser->ReadToken("default"); + parser->ReadToken(TokenType::Colon); + return stmt; +} + +static Stmt* parseTargetSwitchStmt(Parser* parser) +{ + TargetSwitchStmt* stmt = parser->astBuilder->create<TargetSwitchStmt>(); + parser->FillPosition(stmt); + parser->ReadToken(); + if (!beginMatch(parser, MatchedTokenType::CurlyBraces)) { - DefaultStmt* stmt = parser->astBuilder->create<DefaultStmt>(); - parser->FillPosition(stmt); - parser->ReadToken("default"); - parser->ReadToken(TokenType::Colon); return stmt; } - - static Stmt* parseTargetSwitchStmt(Parser* parser) + Token closingBraceToken; + while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingBraceToken)) { - TargetSwitchStmt* stmt = parser->astBuilder->create<TargetSwitchStmt>(); - parser->FillPosition(stmt); - parser->ReadToken(); - if (!beginMatch(parser, MatchedTokenType::CurlyBraces)) - { - return stmt; - } - Token closingBraceToken; - while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingBraceToken)) + ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); + parser->pushScopeAndSetParent(scopeDecl); + List<Token> caseNames; + for (;;) { - ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); - parser->pushScopeAndSetParent(scopeDecl); - List<Token> caseNames; - for (;;) + if (parser->LookAheadToken("case")) { - if (parser->LookAheadToken("case")) - { - parser->ReadToken(); - caseNames.add(parser->ReadToken()); - parser->ReadToken(TokenType::Colon); - } - else if (parser->LookAheadToken("default")) - { - auto token = parser->ReadToken(); - parser->ReadToken(TokenType::Colon); - token.setContent(UnownedStringSlice("")); - caseNames.add(token); - } - else - break; + parser->ReadToken(); + caseNames.add(parser->ReadToken()); + parser->ReadToken(TokenType::Colon); } - if (caseNames.getCount() == 0) + else if (parser->LookAheadToken("default")) { - parser->sink->diagnose( - parser->tokenReader.peekLoc(), - Diagnostics::unexpectedTokenExpectedTokenType, - parser->tokenReader.peekToken(), - "'case' or 'default'"); - parser->isRecovering = true; - goto recover; + auto token = parser->ReadToken(); + parser->ReadToken(TokenType::Colon); + token.setContent(UnownedStringSlice("")); + caseNames.add(token); } else + break; + } + if (caseNames.getCount() == 0) + { + parser->sink->diagnose( + parser->tokenReader.peekLoc(), + Diagnostics::unexpectedTokenExpectedTokenType, + parser->tokenReader.peekToken(), + "'case' or 'default'"); + parser->isRecovering = true; + goto recover; + } + else + { + Stmt* bodyStmt = nullptr; + for (;;) { - Stmt* bodyStmt = nullptr; - for (;;) + if (parser->LookAheadToken("case") || parser->LookAheadToken("default") || + parser->LookAheadToken(TokenType::RBrace) || + parser->LookAheadToken(TokenType::EndOfFile)) + break; + auto nextStmt = parser->ParseStatement(stmt); + if (nextStmt) { - if (parser->LookAheadToken("case") || parser->LookAheadToken("default") || parser->LookAheadToken(TokenType::RBrace) || - parser->LookAheadToken(TokenType::EndOfFile)) - break; - auto nextStmt = parser->ParseStatement(stmt); - if (nextStmt) + if (!bodyStmt) { - if (!bodyStmt) - { - bodyStmt = nextStmt; - } - else if (auto seqStmt = as<SeqStmt>(bodyStmt)) - { - seqStmt->stmts.add(nextStmt); - } - else - { - SeqStmt* newBody = parser->astBuilder->create<SeqStmt>(); - newBody->loc = bodyStmt->loc; - newBody->stmts.add(bodyStmt); - newBody->stmts.add(nextStmt); - bodyStmt = newBody; - } + bodyStmt = nextStmt; + } + else if (auto seqStmt = as<SeqStmt>(bodyStmt)) + { + seqStmt->stmts.add(nextStmt); + } + else + { + SeqStmt* newBody = parser->astBuilder->create<SeqStmt>(); + newBody->loc = bodyStmt->loc; + newBody->stmts.add(bodyStmt); + newBody->stmts.add(nextStmt); + bodyStmt = newBody; } } + } - for (auto caseName : caseNames) + for (auto caseName : caseNames) + { + TargetCaseStmt* targetCase = parser->astBuilder->create<TargetCaseStmt>(); + auto cap = findCapabilityName(caseName.getContent()); + if (caseName.getContent().getLength() && cap == CapabilityName::Invalid) { - TargetCaseStmt* targetCase = parser->astBuilder->create<TargetCaseStmt>(); - auto cap = findCapabilityName(caseName.getContent()); - if (caseName.getContent().getLength() && cap == CapabilityName::Invalid) - { - parser->sink->diagnose(caseName.loc, Diagnostics::unknownTargetName, caseName.getContent()); - } - targetCase->capability = int32_t(cap); - targetCase->capabilityToken = caseName; - targetCase->loc = caseName.loc; - targetCase->body = bodyStmt; - stmt->targetCases.add(targetCase); + parser->sink->diagnose( + caseName.loc, + Diagnostics::unknownTargetName, + caseName.getContent()); } + targetCase->capability = int32_t(cap); + targetCase->capabilityToken = caseName; + targetCase->loc = caseName.loc; + targetCase->body = bodyStmt; + stmt->targetCases.add(targetCase); } - recover:; - TryRecover(parser); - parser->PopScope(); } - return stmt; + recover:; + TryRecover(parser); + parser->PopScope(); } + return stmt; +} - static Stmt* parseIntrinsicAsmStmt(Parser* parser) - { - IntrinsicAsmStmt* stmt = parser->astBuilder->create<IntrinsicAsmStmt>(); - parser->FillPosition(stmt); - parser->ReadToken(); - - stmt->asmText = getStringLiteralTokenValue(parser->ReadToken(TokenType::StringLiteral)); +static Stmt* parseIntrinsicAsmStmt(Parser* parser) +{ + IntrinsicAsmStmt* stmt = parser->astBuilder->create<IntrinsicAsmStmt>(); + parser->FillPosition(stmt); + parser->ReadToken(); - while (AdvanceIf(parser, TokenType::Comma)) - { - stmt->args.add(parser->ParseArgExpr()); - } + stmt->asmText = getStringLiteralTokenValue(parser->ReadToken(TokenType::StringLiteral)); - parser->ReadToken(TokenType::Semicolon); - return stmt; + while (AdvanceIf(parser, TokenType::Comma)) + { + stmt->args.add(parser->ParseArgExpr()); } - GpuForeachStmt* ParseGpuForeachStmt(Parser* parser) - { - // Hard-coding parsing of the following: - // __GPU_FOREACH(renderer, gridDims, LAMBDA(uint3 dispatchThreadID) { - // kernelCall(args, ...); }); + parser->ReadToken(TokenType::Semicolon); + return stmt; +} - // Setup the scope so that dispatchThreadID is in scope for kernelCall - ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); - GpuForeachStmt* stmt = parser->astBuilder->create<GpuForeachStmt>(); - stmt->scopeDecl = scopeDecl; +GpuForeachStmt* ParseGpuForeachStmt(Parser* parser) +{ + // Hard-coding parsing of the following: + // __GPU_FOREACH(renderer, gridDims, LAMBDA(uint3 dispatchThreadID) { + // kernelCall(args, ...); }); - parser->FillPosition(stmt); - parser->ReadToken("__GPU_FOREACH"); - parser->ReadToken(TokenType::LParent); - stmt->device = parser->ParseArgExpr(); - parser->ReadToken(TokenType::Comma); - stmt->gridDims = parser->ParseArgExpr(); + // Setup the scope so that dispatchThreadID is in scope for kernelCall + ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); + GpuForeachStmt* stmt = parser->astBuilder->create<GpuForeachStmt>(); + stmt->scopeDecl = scopeDecl; - parser->ReadToken(TokenType::Comma); - parser->ReadToken("LAMBDA"); - parser->ReadToken(TokenType::LParent); + parser->FillPosition(stmt); + parser->ReadToken("__GPU_FOREACH"); + parser->ReadToken(TokenType::LParent); + stmt->device = parser->ParseArgExpr(); + parser->ReadToken(TokenType::Comma); + stmt->gridDims = parser->ParseArgExpr(); - auto idType = parser->ParseTypeExp(); - NameLoc varNameAndLoc = expectIdentifier(parser); - VarDecl* varDecl = parser->astBuilder->create<VarDecl>(); - varDecl->nameAndLoc = varNameAndLoc; - varDecl->loc = varNameAndLoc.loc; - varDecl->type = idType; - stmt->dispatchThreadID = varDecl; + parser->ReadToken(TokenType::Comma); + parser->ReadToken("LAMBDA"); + parser->ReadToken(TokenType::LParent); - parser->ReadToken(TokenType::RParent); - parser->ReadToken(TokenType::LBrace); + auto idType = parser->ParseTypeExp(); + NameLoc varNameAndLoc = expectIdentifier(parser); + VarDecl* varDecl = parser->astBuilder->create<VarDecl>(); + varDecl->nameAndLoc = varNameAndLoc; + varDecl->loc = varNameAndLoc.loc; + varDecl->type = idType; + stmt->dispatchThreadID = varDecl; - parser->pushScopeAndSetParent(scopeDecl); - AddMember(parser->currentScope, varDecl); + parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::LBrace); - stmt->kernelCall = parser->ParseExpression(); + parser->pushScopeAndSetParent(scopeDecl); + AddMember(parser->currentScope, varDecl); - parser->PopScope(); + stmt->kernelCall = parser->ParseExpression(); - parser->ReadToken(TokenType::Semicolon); - parser->ReadToken(TokenType::RBrace); + parser->PopScope(); - parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::Semicolon); + parser->ReadToken(TokenType::RBrace); - parser->ReadToken(TokenType::Semicolon); + parser->ReadToken(TokenType::RParent); - return stmt; - } + parser->ReadToken(TokenType::Semicolon); - static bool _isType(Decl* decl) - { - return decl && (as<AggTypeDecl>(decl) || as<SimpleTypeDecl>(decl)); - } + return stmt; +} - // TODO(JS): - // This only handles StaticMemberExpr, and VarExpr lookup scenarios! - static Decl* _tryResolveDecl(Parser* parser, Expr* expr) +static bool _isType(Decl* decl) +{ + return decl && (as<AggTypeDecl>(decl) || as<SimpleTypeDecl>(decl)); +} + +// TODO(JS): +// This only handles StaticMemberExpr, and VarExpr lookup scenarios! +static Decl* _tryResolveDecl(Parser* parser, Expr* expr) +{ + if (auto staticMemberExpr = as<StaticMemberExpr>(expr)) { - if (auto staticMemberExpr = as<StaticMemberExpr>(expr)) + Decl* baseTypeDecl = _tryResolveDecl(parser, staticMemberExpr->baseExpression); + if (!baseTypeDecl) { - Decl* baseTypeDecl = _tryResolveDecl(parser, staticMemberExpr->baseExpression); - if (!baseTypeDecl) - { - return nullptr; - } - if (AggTypeDecl* aggTypeDecl = as<AggTypeDecl>(baseTypeDecl)) - { - // TODO(JS): - // Is it valid to always have empty substitution set here? - DeclRef<ContainerDecl> declRef(aggTypeDecl); - - auto lookupResult = lookUpDirectAndTransparentMembers( - parser->astBuilder, - nullptr, // no semantics visitor available yet - staticMemberExpr->name, - aggTypeDecl, - declRef); - - if (!lookupResult.isValid() || lookupResult.isOverloaded()) - return nullptr; - - return lookupResult.item.declRef.getDecl(); - } - - // Didn't find it return nullptr; } - - if (auto varExpr = as<VarExpr>(expr)) + if (AggTypeDecl* aggTypeDecl = as<AggTypeDecl>(baseTypeDecl)) { - // Do the lookup in the current scope - auto lookupResult = lookUp( + // TODO(JS): + // Is it valid to always have empty substitution set here? + DeclRef<ContainerDecl> declRef(aggTypeDecl); + + auto lookupResult = lookUpDirectAndTransparentMembers( parser->astBuilder, nullptr, // no semantics visitor available yet - varExpr->name, - parser->currentScope); + staticMemberExpr->name, + aggTypeDecl, + declRef); + if (!lookupResult.isValid() || lookupResult.isOverloaded()) return nullptr; return lookupResult.item.declRef.getDecl(); } + // Didn't find it return nullptr; } - static bool isTypeName(Parser* parser, Name* name) + if (auto varExpr = as<VarExpr>(expr)) { + // Do the lookup in the current scope auto lookupResult = lookUp( parser->astBuilder, nullptr, // no semantics visitor available yet - name, + varExpr->name, parser->currentScope); if (!lookupResult.isValid() || lookupResult.isOverloaded()) - return false; + return nullptr; - return _isType(lookupResult.item.declRef.getDecl()); + return lookupResult.item.declRef.getDecl(); } - static bool peekTypeName(Parser* parser) - { - if (!parser->LookAheadToken(TokenType::Identifier)) - return false; + return nullptr; +} - auto name = parser->tokenReader.peekToken().getName(); - return isTypeName(parser, name); - } +static bool isTypeName(Parser* parser, Name* name) +{ + auto lookupResult = lookUp( + parser->astBuilder, + nullptr, // no semantics visitor available yet + name, + parser->currentScope); + if (!lookupResult.isValid() || lookupResult.isOverloaded()) + return false; - Stmt* parseCompileTimeForStmt( - Parser* parser) - { - ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); - CompileTimeForStmt* stmt = parser->astBuilder->create<CompileTimeForStmt>(); - stmt->scopeDecl = scopeDecl; + return _isType(lookupResult.item.declRef.getDecl()); +} +static bool peekTypeName(Parser* parser) +{ + if (!parser->LookAheadToken(TokenType::Identifier)) + return false; - parser->ReadToken("for"); - parser->ReadToken(TokenType::LParent); + auto name = parser->tokenReader.peekToken().getName(); + return isTypeName(parser, name); +} - NameLoc varNameAndLoc = expectIdentifier(parser); - VarDecl* varDecl = parser->astBuilder->create<VarDecl>(); - varDecl->nameAndLoc = varNameAndLoc; - varDecl->loc = varNameAndLoc.loc; +Stmt* parseCompileTimeForStmt(Parser* parser) +{ + ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>(); + CompileTimeForStmt* stmt = parser->astBuilder->create<CompileTimeForStmt>(); + stmt->scopeDecl = scopeDecl; - stmt->varDecl = varDecl; - parser->ReadToken("in"); - parser->ReadToken("Range"); - parser->ReadToken(TokenType::LParent); + parser->ReadToken("for"); + parser->ReadToken(TokenType::LParent); - Expr* rangeBeginExpr = nullptr; - Expr* rangeEndExpr = parser->ParseArgExpr(); - if (AdvanceIf(parser, TokenType::Comma)) - { - rangeBeginExpr = rangeEndExpr; - rangeEndExpr = parser->ParseArgExpr(); - } + NameLoc varNameAndLoc = expectIdentifier(parser); + VarDecl* varDecl = parser->astBuilder->create<VarDecl>(); + varDecl->nameAndLoc = varNameAndLoc; + varDecl->loc = varNameAndLoc.loc; - stmt->rangeBeginExpr = rangeBeginExpr; - stmt->rangeEndExpr = rangeEndExpr; + stmt->varDecl = varDecl; - parser->ReadToken(TokenType::RParent); - parser->ReadToken(TokenType::RParent); + parser->ReadToken("in"); + parser->ReadToken("Range"); + parser->ReadToken(TokenType::LParent); - parser->pushScopeAndSetParent(scopeDecl); - AddMember(parser->currentScope, varDecl); + Expr* rangeBeginExpr = nullptr; + Expr* rangeEndExpr = parser->ParseArgExpr(); + if (AdvanceIf(parser, TokenType::Comma)) + { + rangeBeginExpr = rangeEndExpr; + rangeEndExpr = parser->ParseArgExpr(); + } - stmt->body = parser->ParseStatement(); + stmt->rangeBeginExpr = rangeBeginExpr; + stmt->rangeEndExpr = rangeEndExpr; - parser->PopScope(); + parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::RParent); - return stmt; + parser->pushScopeAndSetParent(scopeDecl); + AddMember(parser->currentScope, varDecl); + + stmt->body = parser->ParseStatement(); + + parser->PopScope(); + + return stmt; +} + +Stmt* parseCompileTimeStmt(Parser* parser) +{ + parser->ReadToken(TokenType::Dollar); + if (parser->LookAheadToken("for")) + { + return parseCompileTimeForStmt(parser); + } + else + { + Unexpected(parser); + return nullptr; } +} - Stmt* parseCompileTimeStmt( - Parser* parser) +Stmt* Parser::ParseStatement(Stmt* parentStmt) +{ + auto modifiers = ParseModifiers(this); + + Stmt* statement = nullptr; + if (LookAheadToken(TokenType::LBrace)) + statement = parseBlockStatement(); + else if (LookAheadToken("if")) { - parser->ReadToken(TokenType::Dollar); - if (parser->LookAheadToken("for")) + if (LookAheadToken("let", 2)) { - return parseCompileTimeForStmt(parser); + statement = parseIfLetStatement(); } else { - Unexpected(parser); - return nullptr; + statement = parseIfStatement(); } } - - Stmt* Parser::ParseStatement(Stmt* parentStmt) + else if (LookAheadToken("for")) + statement = ParseForStatement(); + else if (LookAheadToken("while")) + statement = ParseWhileStatement(); + else if (LookAheadToken("do")) + statement = ParseDoWhileStatement(); + else if (LookAheadToken("break")) + statement = ParseBreakStatement(); + else if (LookAheadToken("continue")) + statement = ParseContinueStatement(); + else if (LookAheadToken("return")) + statement = ParseReturnStatement(); + else if (LookAheadToken("discard")) { - auto modifiers = ParseModifiers(this); - - Stmt* statement = nullptr; - if (LookAheadToken(TokenType::LBrace)) - statement = parseBlockStatement(); - else if (LookAheadToken("if")) - { - if(LookAheadToken("let", 2)) - { - statement = parseIfLetStatement(); - } - else - { - statement = parseIfStatement(); - } - } - else if (LookAheadToken("for")) - statement = ParseForStatement(); - else if (LookAheadToken("while")) - statement = ParseWhileStatement(); - else if (LookAheadToken("do")) - statement = ParseDoWhileStatement(); - else if (LookAheadToken("break")) - statement = ParseBreakStatement(); - else if (LookAheadToken("continue")) - statement = ParseContinueStatement(); - else if (LookAheadToken("return")) - statement = ParseReturnStatement(); - else if (LookAheadToken("discard")) - { - statement = astBuilder->create<DiscardStmt>(); - FillPosition(statement); - ReadToken("discard"); - ReadToken(TokenType::Semicolon); - } - else if (LookAheadToken("switch")) - statement = ParseSwitchStmt(this); - else if (LookAheadToken("__target_switch")) - statement = parseTargetSwitchStmt(this); - else if (LookAheadToken("__intrinsic_asm")) - statement = parseIntrinsicAsmStmt(this); - else if (LookAheadToken("case")) - statement = ParseCaseStmt(this); - else if (LookAheadToken("default")) - statement = ParseDefaultStmt(this); - else if (LookAheadToken("__GPU_FOREACH")) - statement = ParseGpuForeachStmt(this); - else if (LookAheadToken(TokenType::Dollar)) - { - statement = parseCompileTimeStmt(this); - } - else if (LookAheadToken("try")) + statement = astBuilder->create<DiscardStmt>(); + FillPosition(statement); + ReadToken("discard"); + ReadToken(TokenType::Semicolon); + } + else if (LookAheadToken("switch")) + statement = ParseSwitchStmt(this); + else if (LookAheadToken("__target_switch")) + statement = parseTargetSwitchStmt(this); + else if (LookAheadToken("__intrinsic_asm")) + statement = parseIntrinsicAsmStmt(this); + else if (LookAheadToken("case")) + statement = ParseCaseStmt(this); + else if (LookAheadToken("default")) + statement = ParseDefaultStmt(this); + else if (LookAheadToken("__GPU_FOREACH")) + statement = ParseGpuForeachStmt(this); + else if (LookAheadToken(TokenType::Dollar)) + { + statement = parseCompileTimeStmt(this); + } + else if (LookAheadToken("try")) + { + statement = ParseExpressionStatement(); + } + else if (LookAheadToken(TokenType::Identifier) || LookAheadToken(TokenType::Scope)) + { + if (LookAheadToken(TokenType::Identifier) && LookAheadToken(TokenType::Colon, 1)) { - statement = ParseExpressionStatement(); + // An identifier followed by an ":" is a label. + return parseLabelStatement(); } - else if (LookAheadToken(TokenType::Identifier) || LookAheadToken(TokenType::Scope)) - { - if (LookAheadToken(TokenType::Identifier) && LookAheadToken(TokenType::Colon, 1)) - { - // An identifier followed by an ":" is a label. - return parseLabelStatement(); - } - // We might be looking at a local declaration, or an - // expression statement, and we need to figure out which. - // - // We'll solve this with backtracking for now. - // - // TODO: This should not require backtracking at all. - - TokenReader::ParsingCursor startPos = tokenReader.getCursor(); + // We might be looking at a local declaration, or an + // expression statement, and we need to figure out which. + // + // We'll solve this with backtracking for now. + // + // TODO: This should not require backtracking at all. - // Try to parse a type (knowing that the type grammar is - // a subset of the expression grammar, and so this should - // always succeed). - // - // HACK: The type grammar that `ParseType` supports is *not* - // a subset of the expression grammar because it includes - // type specififers like `struct` and `enum` declarations - // which should always be the start of a declaration. - // - // TODO: Before launching into this attempt to parse a type, - // this logic should really be looking up the `SyntaxDecl`, - // if any, assocaited with the identifier. If a piece of - // syntax is discovered, then it should dictate the next - // steps of parsing, and only in the case where the lookahead - // isn't a keyword should we fall back to the approach - // here. - // - bool prevHasSeenCompletionToken = hasSeenCompletionToken; - Expr* type = ParseType(); + TokenReader::ParsingCursor startPos = tokenReader.getCursor(); - // We don't actually care about the type, though, so - // don't retain it - // - // TODO: There is no reason to throw away the work we - // did parsing the `type` expression. Once we disambiguate - // what to do, we should be able to use the expression - // we already parsed as a starting point for whatever we - // parse next. E.g., if we have `A.b` and the lookahead is `+` - // then we can use `A.b` as the left-hand-side expression - // when starting to parse an infix expression. - // - type = nullptr; - SLANG_UNUSED(type); - - // TODO: If we decide to intermix parsing of statement bodies - // with semantic checking (by delaying the parsing of bodies - // until semantic context is available), then we could look - // at the *type* of `type` to disambiguate what to do next, - // which might result in a nice simplification (at the cost - // of definitely making the grammar context-dependent). - - // If the next token after we parsed a type looks like - // we are going to declare a variable, then lets guess - // that this is a declaration. - // - // TODO(tfoley): this wouldn't be robust for more - // general kinds of declarators (notably pointer declarators), - // so we'll need to be careful about this. - // - // If the line being parsed token is `Something* ...`, then the `*` - // is already consumed by `ParseType` above and the current logic - // will continue to parse as var declaration instead of a mul expr. - // In this context it makes sense to disambiguate - // in favor of a pointer over a multiply, since a multiply - // expression can't appear at the start of a statement - // with any side effects. - // - // - if (!hasSeenCompletionToken && (LookAheadToken(TokenType::Identifier) || - LookAheadToken(TokenType::CompletionRequest))) - { - // Reset the cursor and try to parse a declaration now. - // Note: the declaration will consume any modifiers - // that had been in place on the statement. - tokenReader.setCursor(startPos); - statement = parseVarDeclrStatement(modifiers); - return statement; - } + // Try to parse a type (knowing that the type grammar is + // a subset of the expression grammar, and so this should + // always succeed). + // + // HACK: The type grammar that `ParseType` supports is *not* + // a subset of the expression grammar because it includes + // type specififers like `struct` and `enum` declarations + // which should always be the start of a declaration. + // + // TODO: Before launching into this attempt to parse a type, + // this logic should really be looking up the `SyntaxDecl`, + // if any, assocaited with the identifier. If a piece of + // syntax is discovered, then it should dictate the next + // steps of parsing, and only in the case where the lookahead + // isn't a keyword should we fall back to the approach + // here. + // + bool prevHasSeenCompletionToken = hasSeenCompletionToken; + Expr* type = ParseType(); - // Fallback: reset and parse an expression - hasSeenCompletionToken = prevHasSeenCompletionToken; - tokenReader.setCursor(startPos); - statement = ParseExpressionStatement(); - } - else if (LookAheadToken(TokenType::Semicolon)) - { - if (as<IfStmt>(parentStmt)) - { - // An empty statement after an `if` is probably a mistake, - // so we will diagnose it as such. - // - sink->diagnose(tokenReader.peekLoc(), Diagnostics::unintendedEmptyStatement); - } - statement = astBuilder->create<EmptyStmt>(); - FillPosition(statement); - ReadToken(TokenType::Semicolon); - } - else + // We don't actually care about the type, though, so + // don't retain it + // + // TODO: There is no reason to throw away the work we + // did parsing the `type` expression. Once we disambiguate + // what to do, we should be able to use the expression + // we already parsed as a starting point for whatever we + // parse next. E.g., if we have `A.b` and the lookahead is `+` + // then we can use `A.b` as the left-hand-side expression + // when starting to parse an infix expression. + // + type = nullptr; + SLANG_UNUSED(type); + + // TODO: If we decide to intermix parsing of statement bodies + // with semantic checking (by delaying the parsing of bodies + // until semantic context is available), then we could look + // at the *type* of `type` to disambiguate what to do next, + // which might result in a nice simplification (at the cost + // of definitely making the grammar context-dependent). + + // If the next token after we parsed a type looks like + // we are going to declare a variable, then lets guess + // that this is a declaration. + // + // TODO(tfoley): this wouldn't be robust for more + // general kinds of declarators (notably pointer declarators), + // so we'll need to be careful about this. + // + // If the line being parsed token is `Something* ...`, then the `*` + // is already consumed by `ParseType` above and the current logic + // will continue to parse as var declaration instead of a mul expr. + // In this context it makes sense to disambiguate + // in favor of a pointer over a multiply, since a multiply + // expression can't appear at the start of a statement + // with any side effects. + // + // + if (!hasSeenCompletionToken && + (LookAheadToken(TokenType::Identifier) || LookAheadToken(TokenType::CompletionRequest))) { - // Default case should always fall back to parsing an expression, - // and then let that detect any errors - statement = ParseExpressionStatement(); + // Reset the cursor and try to parse a declaration now. + // Note: the declaration will consume any modifiers + // that had been in place on the statement. + tokenReader.setCursor(startPos); + statement = parseVarDeclrStatement(modifiers); + return statement; } - if (statement && !as<DeclStmt>(statement)) + // Fallback: reset and parse an expression + hasSeenCompletionToken = prevHasSeenCompletionToken; + tokenReader.setCursor(startPos); + statement = ParseExpressionStatement(); + } + else if (LookAheadToken(TokenType::Semicolon)) + { + if (as<IfStmt>(parentStmt)) { - // Install any modifiers onto the statement. - // Note: this path is bypassed in the case of a - // declaration statement, so we don't end up - // doubling up the modifiers. - statement->modifiers = modifiers; + // An empty statement after an `if` is probably a mistake, + // so we will diagnose it as such. + // + sink->diagnose(tokenReader.peekLoc(), Diagnostics::unintendedEmptyStatement); } + statement = astBuilder->create<EmptyStmt>(); + FillPosition(statement); + ReadToken(TokenType::Semicolon); + } + else + { + // Default case should always fall back to parsing an expression, + // and then let that detect any errors + statement = ParseExpressionStatement(); + } - return statement; + if (statement && !as<DeclStmt>(statement)) + { + // Install any modifiers onto the statement. + // Note: this path is bypassed in the case of a + // declaration statement, so we don't end up + // doubling up the modifiers. + statement->modifiers = modifiers; } - // Look ahead token, skipping all modifiers. - bool lookAheadTokenAfterModifiers(Parser* parser, const char* token) + return statement; +} + +// Look ahead token, skipping all modifiers. +bool lookAheadTokenAfterModifiers(Parser* parser, const char* token) +{ + TokenReader tokenPreview = parser->tokenReader; + for (;;) { - TokenReader tokenPreview = parser->tokenReader; - for (;;) + if (tokenPreview.peekToken().getContent() == token) + return true; + else if (auto syntaxDecl = tryLookUpSyntaxDecl(parser, tokenPreview.peekToken().getName())) { - if (tokenPreview.peekToken().getContent() == token) - return true; - else if (auto syntaxDecl = tryLookUpSyntaxDecl(parser, tokenPreview.peekToken().getName())) + if (syntaxDecl->syntaxClass.isSubClassOf<Modifier>()) { - if (syntaxDecl->syntaxClass.isSubClassOf<Modifier>()) - { - tokenPreview.advanceToken(); - continue; - } + tokenPreview.advanceToken(); + continue; } - break; } - return false; + break; } + return false; +} - Stmt* Parser::parseBlockStatement() +Stmt* Parser::parseBlockStatement() +{ + if (!beginMatch(this, MatchedTokenType::CurlyBraces)) { - if(!beginMatch(this, MatchedTokenType::CurlyBraces)) - { - auto emptyStmt = astBuilder->create<EmptyStmt>(); - FillPosition(emptyStmt); - return emptyStmt; - } + auto emptyStmt = astBuilder->create<EmptyStmt>(); + FillPosition(emptyStmt); + return emptyStmt; + } - ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); - BlockStmt* blockStatement = astBuilder->create<BlockStmt>(); - blockStatement->scopeDecl = scopeDecl; - pushScopeAndSetParent(scopeDecl); + ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); + BlockStmt* blockStatement = astBuilder->create<BlockStmt>(); + blockStatement->scopeDecl = scopeDecl; + pushScopeAndSetParent(scopeDecl); - Stmt* body = nullptr; + Stmt* body = nullptr; - if(!tokenReader.isAtEnd()) + if (!tokenReader.isAtEnd()) + { + FillPosition(blockStatement); + } + + Token closingBraceToken; + auto addStmt = [&](Stmt* stmt) + { + if (!body) { - FillPosition(blockStatement); + body = stmt; } - - Token closingBraceToken; - auto addStmt = [&](Stmt* stmt) + else if (auto seqStmt = as<SeqStmt>(body)) { - if (!body) - { - body = stmt; - } - else if (auto seqStmt = as<SeqStmt>(body)) - { - seqStmt->stmts.add(stmt); - } - else - { - SeqStmt* newBody = astBuilder->create<SeqStmt>(); - newBody->loc = blockStatement->loc; - newBody->stmts.add(body); - newBody->stmts.add(stmt); + seqStmt->stmts.add(stmt); + } + else + { + SeqStmt* newBody = astBuilder->create<SeqStmt>(); + newBody->loc = blockStatement->loc; + newBody->stmts.add(body); + newBody->stmts.add(stmt); - body = newBody; - } - }; - while (!AdvanceIfMatch(this, MatchedTokenType::CurlyBraces, &closingBraceToken)) + body = newBody; + } + }; + while (!AdvanceIfMatch(this, MatchedTokenType::CurlyBraces, &closingBraceToken)) + { + if (lookAheadTokenAfterModifiers(this, "struct")) { - if (lookAheadTokenAfterModifiers(this, "struct")) + auto declBase = ParseDecl(this, scopeDecl); + if (auto declGroup = as<DeclGroup>(declBase)) { - auto declBase = ParseDecl(this, scopeDecl); - if (auto declGroup = as<DeclGroup>(declBase)) + for (auto subDecl : declGroup->decls) { - for (auto subDecl : declGroup->decls) + if (auto varDecl = as<VarDeclBase>(subDecl)) { - if (auto varDecl = as<VarDeclBase>(subDecl)) - { - auto declStmt = astBuilder->create<DeclStmt>(); - declStmt->loc = varDecl->loc; - declStmt->decl = varDecl; - addStmt(declStmt); - } + auto declStmt = astBuilder->create<DeclStmt>(); + declStmt->loc = varDecl->loc; + declStmt->decl = varDecl; + addStmt(declStmt); } } - continue; - } - else if (AdvanceIf(this, "typedef")) - { - auto typeDefDecl = parseTypeDef(this, nullptr); - AddMember(scopeDecl, (Decl*)typeDefDecl); - continue; } - else if (AdvanceIf(this, "typealias")) - { - auto typeDefDecl = parseTypeAliasDecl(this, nullptr); - AddMember(scopeDecl, (Decl*)typeDefDecl); - continue; - } - - auto stmt = ParseStatement(); - - if (stmt) - addStmt(stmt); - - TryRecover(this); + continue; } - PopScope(); - - // Save the closing braces source loc - blockStatement->closingSourceLoc = closingBraceToken.loc; - - if(!body) + else if (AdvanceIf(this, "typedef")) + { + auto typeDefDecl = parseTypeDef(this, nullptr); + AddMember(scopeDecl, (Decl*)typeDefDecl); + continue; + } + else if (AdvanceIf(this, "typealias")) { - body = astBuilder->create<EmptyStmt>(); - body->loc = blockStatement->loc; + auto typeDefDecl = parseTypeAliasDecl(this, nullptr); + AddMember(scopeDecl, (Decl*)typeDefDecl); + continue; } - blockStatement->body = body; - return blockStatement; + auto stmt = ParseStatement(); + + if (stmt) + addStmt(stmt); + + TryRecover(this); } + PopScope(); - Stmt* Parser::parseLabelStatement() + // Save the closing braces source loc + blockStatement->closingSourceLoc = closingBraceToken.loc; + + if (!body) { - LabelStmt* stmt = astBuilder->create<LabelStmt>(); - FillPosition(stmt); - stmt->label = ReadToken(TokenType::Identifier); - ReadToken(TokenType::Colon); - stmt->innerStmt = ParseStatement(); - return stmt; + body = astBuilder->create<EmptyStmt>(); + body->loc = blockStatement->loc; } - DeclStmt* Parser::parseVarDeclrStatement( - Modifiers modifiers) - { - DeclStmt*varDeclrStatement = astBuilder->create<DeclStmt>(); + blockStatement->body = body; + return blockStatement; +} - FillPosition(varDeclrStatement); - auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers); - varDeclrStatement->decl = decl; - return varDeclrStatement; - } +Stmt* Parser::parseLabelStatement() +{ + LabelStmt* stmt = astBuilder->create<LabelStmt>(); + FillPosition(stmt); + stmt->label = ReadToken(TokenType::Identifier); + ReadToken(TokenType::Colon); + stmt->innerStmt = ParseStatement(); + return stmt; +} - static Expr* constructIfLetPredicate(Parser* parser, VarExpr* varExpr) - { - // create a "var.hasValue" expression - MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>(); - memberExpr->baseExpression = varExpr; - parser->FillPosition(memberExpr); - memberExpr->name = getName(parser, "hasValue"); +DeclStmt* Parser::parseVarDeclrStatement(Modifiers modifiers) +{ + DeclStmt* varDeclrStatement = astBuilder->create<DeclStmt>(); - return memberExpr; - } + FillPosition(varDeclrStatement); + auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers); + varDeclrStatement->decl = decl; + return varDeclrStatement; +} - // Parse the syntax 'if (let var = X as Y)' - Stmt* Parser::parseIfLetStatement() - { - ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); - pushScopeAndSetParent(scopeDecl); +static Expr* constructIfLetPredicate(Parser* parser, VarExpr* varExpr) +{ + // create a "var.hasValue" expression + MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>(); + memberExpr->baseExpression = varExpr; + parser->FillPosition(memberExpr); + memberExpr->name = getName(parser, "hasValue"); - SeqStmt* newBody = astBuilder->create<SeqStmt>(); - - IfStmt* ifStatement = astBuilder->create<IfStmt>(); - FillPosition(ifStatement); - ReadToken("if"); - ReadToken(TokenType::LParent); - - // parse 'let var = X as Y' - ReadToken("let"); - auto identifierToken = ReadToken(TokenType::Identifier); - ReadToken(TokenType::OpAssign); - auto initExpr = ParseInitExpr(); - - // insert 'let tempVarDecl = X as Y;' - auto tempVarDecl = astBuilder->create<LetDecl>(); - tempVarDecl->nameAndLoc = NameLoc(getName(this, "$OptVar"), identifierToken.loc); - tempVarDecl->initExpr = initExpr; - AddMember(currentScope->containerDecl, tempVarDecl); - - DeclStmt* tmpVarDeclStmt = astBuilder->create<DeclStmt>(); - FillPosition(tmpVarDeclStmt); - tmpVarDeclStmt->decl = tempVarDecl; - newBody->stmts.add(tmpVarDeclStmt); - - // construct 'if (tempVarDecl.hasValue == true)' - VarExpr* tempVarExpr = astBuilder->create<VarExpr>(); - tempVarExpr->scope = currentScope; - FillPosition(tempVarExpr); - tempVarExpr->name = tempVarDecl->getName(); - ifStatement->predicate = constructIfLetPredicate(this, tempVarExpr); - - ReadToken(TokenType::RParent); - - // Create a new scope surrounding the positive statement, will be used for - // the variable declared in the if_let syntax - ScopeDecl* positiveScopeDecl = astBuilder->create<ScopeDecl>(); - pushScopeAndSetParent(positiveScopeDecl); - ifStatement->positiveStatement = ParseStatement(ifStatement); - PopScope(); + return memberExpr; +} - if (LookAheadToken("else")) - { - ReadToken("else"); - ifStatement->negativeStatement = ParseStatement(ifStatement); - } +// Parse the syntax 'if (let var = X as Y)' +Stmt* Parser::parseIfLetStatement() +{ + ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); + pushScopeAndSetParent(scopeDecl); - if (ifStatement->positiveStatement) - { - auto seqPositiveStmt = as<SeqStmt>(ifStatement->positiveStatement); - if (!seqPositiveStmt) - { - seqPositiveStmt = astBuilder->create<SeqStmt>(); - } + SeqStmt* newBody = astBuilder->create<SeqStmt>(); - MemberExpr* memberExpr = astBuilder->create<MemberExpr>(); - memberExpr->baseExpression = tempVarExpr; - memberExpr->name = getName(this, "value"); + IfStmt* ifStatement = astBuilder->create<IfStmt>(); + FillPosition(ifStatement); + ReadToken("if"); + ReadToken(TokenType::LParent); - auto varDecl = astBuilder->create<LetDecl>(); - varDecl->nameAndLoc = NameLoc(identifierToken.getName(), identifierToken.loc); - varDecl->initExpr = memberExpr; + // parse 'let var = X as Y' + ReadToken("let"); + auto identifierToken = ReadToken(TokenType::Identifier); + ReadToken(TokenType::OpAssign); + auto initExpr = ParseInitExpr(); - DeclStmt* varDeclrStatement = astBuilder->create<DeclStmt>(); - varDeclrStatement->decl = varDecl; + // insert 'let tempVarDecl = X as Y;' + auto tempVarDecl = astBuilder->create<LetDecl>(); + tempVarDecl->nameAndLoc = NameLoc(getName(this, "$OptVar"), identifierToken.loc); + tempVarDecl->initExpr = initExpr; + AddMember(currentScope->containerDecl, tempVarDecl); - // Add scope to the variable declared in the if_let syntax such - // that this variable cannot be used outside the positive statement - AddMember(positiveScopeDecl, varDecl); + DeclStmt* tmpVarDeclStmt = astBuilder->create<DeclStmt>(); + FillPosition(tmpVarDeclStmt); + tmpVarDeclStmt->decl = tempVarDecl; + newBody->stmts.add(tmpVarDeclStmt); - seqPositiveStmt->stmts.add(varDeclrStatement); - seqPositiveStmt->stmts.add(ifStatement->positiveStatement); - ifStatement->positiveStatement = seqPositiveStmt; - } + // construct 'if (tempVarDecl.hasValue == true)' + VarExpr* tempVarExpr = astBuilder->create<VarExpr>(); + tempVarExpr->scope = currentScope; + FillPosition(tempVarExpr); + tempVarExpr->name = tempVarDecl->getName(); + ifStatement->predicate = constructIfLetPredicate(this, tempVarExpr); - newBody->stmts.add(ifStatement); - PopScope(); + ReadToken(TokenType::RParent); + + // Create a new scope surrounding the positive statement, will be used for + // the variable declared in the if_let syntax + ScopeDecl* positiveScopeDecl = astBuilder->create<ScopeDecl>(); + pushScopeAndSetParent(positiveScopeDecl); + ifStatement->positiveStatement = ParseStatement(ifStatement); + PopScope(); - return newBody; + if (LookAheadToken("else")) + { + ReadToken("else"); + ifStatement->negativeStatement = ParseStatement(ifStatement); } - IfStmt* Parser::parseIfStatement() + if (ifStatement->positiveStatement) { - IfStmt* ifStatement = astBuilder->create<IfStmt>(); - FillPosition(ifStatement); - ReadToken("if"); - ReadToken(TokenType::LParent); - ifStatement->predicate = ParseExpression(); - ReadToken(TokenType::RParent); - ifStatement->positiveStatement = ParseStatement(ifStatement); - if (LookAheadToken("else")) + auto seqPositiveStmt = as<SeqStmt>(ifStatement->positiveStatement); + if (!seqPositiveStmt) { - ReadToken("else"); - ifStatement->negativeStatement = ParseStatement(ifStatement); + seqPositiveStmt = astBuilder->create<SeqStmt>(); } - return ifStatement; - } - ForStmt* Parser::ParseForStatement() - { - ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); + MemberExpr* memberExpr = astBuilder->create<MemberExpr>(); + memberExpr->baseExpression = tempVarExpr; + memberExpr->name = getName(this, "value"); - // HLSL implements the bad approach to scoping a `for` loop - // variable, and we want to respect that, but *only* when - // parsing HLSL code. - // + auto varDecl = astBuilder->create<LetDecl>(); + varDecl->nameAndLoc = NameLoc(identifierToken.getName(), identifierToken.loc); + varDecl->initExpr = memberExpr; - bool brokenScoping = getSourceLanguage() == SourceLanguage::HLSL; + DeclStmt* varDeclrStatement = astBuilder->create<DeclStmt>(); + varDeclrStatement->decl = varDecl; - // We will create a distinct syntax node class for the unscoped - // case, just so that we can correctly handle it in downstream - // logic. - // - ForStmt* stmt = nullptr; - if (brokenScoping) - { - stmt = astBuilder->create<UnscopedForStmt>(); - } - else - { - stmt = astBuilder->create<ForStmt>(); - } + // Add scope to the variable declared in the if_let syntax such + // that this variable cannot be used outside the positive statement + AddMember(positiveScopeDecl, varDecl); - stmt->scopeDecl = scopeDecl; + seqPositiveStmt->stmts.add(varDeclrStatement); + seqPositiveStmt->stmts.add(ifStatement->positiveStatement); + ifStatement->positiveStatement = seqPositiveStmt; + } - if(!brokenScoping) - pushScopeAndSetParent(scopeDecl); - FillPosition(stmt); - ReadToken("for"); - ReadToken(TokenType::LParent); - auto modifiers = ParseModifiers(this); - if (peekTypeName(this) || !modifiers.isEmpty()) - { - stmt->initialStatement = parseVarDeclrStatement(modifiers); - } - else - { - if (!LookAheadToken(TokenType::Semicolon)) - { - stmt->initialStatement = ParseExpressionStatement(); - } - else - { - ReadToken(TokenType::Semicolon); - } - } - if (!LookAheadToken(TokenType::Semicolon)) - stmt->predicateExpression = ParseExpression(); - ReadToken(TokenType::Semicolon); - if (!LookAheadToken(TokenType::RParent)) - stmt->sideEffectExpression = ParseExpression(); - ReadToken(TokenType::RParent); - stmt->statement = ParseStatement(); + newBody->stmts.add(ifStatement); + PopScope(); - if (!brokenScoping) - PopScope(); + return newBody; +} - return stmt; - } +IfStmt* Parser::parseIfStatement() +{ + IfStmt* ifStatement = astBuilder->create<IfStmt>(); + FillPosition(ifStatement); + ReadToken("if"); + ReadToken(TokenType::LParent); + ifStatement->predicate = ParseExpression(); + ReadToken(TokenType::RParent); + ifStatement->positiveStatement = ParseStatement(ifStatement); + if (LookAheadToken("else")) + { + ReadToken("else"); + ifStatement->negativeStatement = ParseStatement(ifStatement); + } + return ifStatement; +} - WhileStmt* Parser::ParseWhileStatement() - { - WhileStmt* whileStatement = astBuilder->create<WhileStmt>(); - FillPosition(whileStatement); - ReadToken("while"); - ReadToken(TokenType::LParent); - whileStatement->predicate = ParseExpression(); - ReadToken(TokenType::RParent); - whileStatement->statement = ParseStatement(); - return whileStatement; - } +ForStmt* Parser::ParseForStatement() +{ + ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>(); + + // HLSL implements the bad approach to scoping a `for` loop + // variable, and we want to respect that, but *only* when + // parsing HLSL code. + // + + bool brokenScoping = getSourceLanguage() == SourceLanguage::HLSL; - DoWhileStmt* Parser::ParseDoWhileStatement() + // We will create a distinct syntax node class for the unscoped + // case, just so that we can correctly handle it in downstream + // logic. + // + ForStmt* stmt = nullptr; + if (brokenScoping) { - DoWhileStmt* doWhileStatement = astBuilder->create<DoWhileStmt>(); - FillPosition(doWhileStatement); - ReadToken("do"); - doWhileStatement->statement = ParseStatement(); - ReadToken("while"); - ReadToken(TokenType::LParent); - doWhileStatement->predicate = ParseExpression(); - ReadToken(TokenType::RParent); - ReadToken(TokenType::Semicolon); - return doWhileStatement; + stmt = astBuilder->create<UnscopedForStmt>(); } - - BreakStmt* Parser::ParseBreakStatement() + else { - BreakStmt* breakStatement = astBuilder->create<BreakStmt>(); - FillPosition(breakStatement); - ReadToken("break"); - if (LookAheadToken(TokenType::Identifier)) - { - breakStatement->targetLabel = ReadToken(); - } - ReadToken(TokenType::Semicolon); - return breakStatement; + stmt = astBuilder->create<ForStmt>(); } - ContinueStmt* Parser::ParseContinueStatement() + stmt->scopeDecl = scopeDecl; + + if (!brokenScoping) + pushScopeAndSetParent(scopeDecl); + FillPosition(stmt); + ReadToken("for"); + ReadToken(TokenType::LParent); + auto modifiers = ParseModifiers(this); + if (peekTypeName(this) || !modifiers.isEmpty()) { - ContinueStmt* continueStatement = astBuilder->create<ContinueStmt>(); - FillPosition(continueStatement); - ReadToken("continue"); - ReadToken(TokenType::Semicolon); - return continueStatement; + stmt->initialStatement = parseVarDeclrStatement(modifiers); } - - ReturnStmt* Parser::ParseReturnStatement() + else { - ReturnStmt* returnStatement = astBuilder->create<ReturnStmt>(); - FillPosition(returnStatement); - ReadToken("return"); if (!LookAheadToken(TokenType::Semicolon)) - returnStatement->expression = ParseExpression(); - ReadToken(TokenType::Semicolon); - return returnStatement; + { + stmt->initialStatement = ParseExpressionStatement(); + } + else + { + ReadToken(TokenType::Semicolon); + } } + if (!LookAheadToken(TokenType::Semicolon)) + stmt->predicateExpression = ParseExpression(); + ReadToken(TokenType::Semicolon); + if (!LookAheadToken(TokenType::RParent)) + stmt->sideEffectExpression = ParseExpression(); + ReadToken(TokenType::RParent); + stmt->statement = ParseStatement(); - ExpressionStmt* Parser::ParseExpressionStatement() - { - ExpressionStmt* statement = astBuilder->create<ExpressionStmt>(); + if (!brokenScoping) + PopScope(); - FillPosition(statement); - statement->expression = ParseExpression(); + return stmt; +} - ReadToken(TokenType::Semicolon); - return statement; - } +WhileStmt* Parser::ParseWhileStatement() +{ + WhileStmt* whileStatement = astBuilder->create<WhileStmt>(); + FillPosition(whileStatement); + ReadToken("while"); + ReadToken(TokenType::LParent); + whileStatement->predicate = ParseExpression(); + ReadToken(TokenType::RParent); + whileStatement->statement = ParseStatement(); + return whileStatement; +} - ParamDecl* Parser::ParseParameter() - { - ParamDecl* parameter = astBuilder->create<ParamDecl>(); - parameter->modifiers = ParseModifiers(this); - currentLookupScope = currentScope->parent; - _parseTraditionalParamDeclCommonBase(this, parameter, kDeclaratorParseOption_AllowEmpty); - resetLookupScope(); - return parameter; - } +DoWhileStmt* Parser::ParseDoWhileStatement() +{ + DoWhileStmt* doWhileStatement = astBuilder->create<DoWhileStmt>(); + FillPosition(doWhileStatement); + ReadToken("do"); + doWhileStatement->statement = ParseStatement(); + ReadToken("while"); + ReadToken(TokenType::LParent); + doWhileStatement->predicate = ParseExpression(); + ReadToken(TokenType::RParent); + ReadToken(TokenType::Semicolon); + return doWhileStatement; +} - /// Parse an "atomic" type expression. - /// - /// An atomic type expression is a type specifier followed by an optional - /// body in the case of a `struct`, `enum`, etc. - /// - static Expr* _parseAtomicTypeExpr(Parser* parser) +BreakStmt* Parser::ParseBreakStatement() +{ + BreakStmt* breakStatement = astBuilder->create<BreakStmt>(); + FillPosition(breakStatement); + ReadToken("break"); + if (LookAheadToken(TokenType::Identifier)) { - auto typeSpec = _parseTypeSpec(parser); - if( typeSpec.decl ) - { - AddMember(parser->currentScope, typeSpec.decl); - } - return typeSpec.expr; + breakStatement->targetLabel = ReadToken(); } + ReadToken(TokenType::Semicolon); + return breakStatement; +} - /// Parse a postfix type expression. - /// - /// A postfix type expression is an atomic type expression followed - /// by zero or more postifx suffixes like array brackets. - /// - static Expr* _parsePostfixTypeExpr(Parser* parser) - { - auto typeExpr = _parseAtomicTypeExpr(parser); - return parsePostfixTypeSuffix(parser, typeExpr); - } +ContinueStmt* Parser::ParseContinueStatement() +{ + ContinueStmt* continueStatement = astBuilder->create<ContinueStmt>(); + FillPosition(continueStatement); + ReadToken("continue"); + ReadToken(TokenType::Semicolon); + return continueStatement; +} - static Expr* _parseInfixTypeExpr(Parser* parser); +ReturnStmt* Parser::ParseReturnStatement() +{ + ReturnStmt* returnStatement = astBuilder->create<ReturnStmt>(); + FillPosition(returnStatement); + ReadToken("return"); + if (!LookAheadToken(TokenType::Semicolon)) + returnStatement->expression = ParseExpression(); + ReadToken(TokenType::Semicolon); + return returnStatement; +} - static Expr* _parseInfixTypeExprSuffix(Parser* parser, Expr* leftExpr) - { - for(;;) - { - // As long as the next token is an `&`, we will try - // to gobble up another type expression and form - // a conjunction type expression. +ExpressionStmt* Parser::ParseExpressionStatement() +{ + ExpressionStmt* statement = astBuilder->create<ExpressionStmt>(); - auto loc = peekToken(parser).loc; - if(AdvanceIf(parser, TokenType::OpBitAnd)) - { - auto rightExpr = _parsePostfixTypeExpr(parser); + FillPosition(statement); + statement->expression = ParseExpression(); - auto andExpr = parser->astBuilder->create<AndTypeExpr>(); - andExpr->loc = loc; - andExpr->left = TypeExp(leftExpr); - andExpr->right = TypeExp(rightExpr); - leftExpr = andExpr; - } - else - { - break; - } - } + ReadToken(TokenType::Semicolon); + return statement; +} - return leftExpr; - } +ParamDecl* Parser::ParseParameter() +{ + ParamDecl* parameter = astBuilder->create<ParamDecl>(); + parameter->modifiers = ParseModifiers(this); + currentLookupScope = currentScope->parent; + _parseTraditionalParamDeclCommonBase(this, parameter, kDeclaratorParseOption_AllowEmpty); + resetLookupScope(); + return parameter; +} - /// Parse an infix type expression. - /// - /// Currently, the only infix type expressions we support are the `&` - /// operator for forming interface conjunctions and the `->` operator - /// for functions. - /// - static Expr* _parseInfixTypeExpr(Parser* parser) +/// Parse an "atomic" type expression. +/// +/// An atomic type expression is a type specifier followed by an optional +/// body in the case of a `struct`, `enum`, etc. +/// +static Expr* _parseAtomicTypeExpr(Parser* parser) +{ + auto typeSpec = _parseTypeSpec(parser); + if (typeSpec.decl) { - auto leftExpr = _parsePostfixTypeExpr(parser); - return _parseInfixTypeExprSuffix(parser, leftExpr); + AddMember(parser->currentScope, typeSpec.decl); } + return typeSpec.expr; +} - Expr* Parser::ParseType() - { - return _parseInfixTypeExpr(this); - } +/// Parse a postfix type expression. +/// +/// A postfix type expression is an atomic type expression followed +/// by zero or more postifx suffixes like array brackets. +/// +static Expr* _parsePostfixTypeExpr(Parser* parser) +{ + auto typeExpr = _parseAtomicTypeExpr(parser); + return parsePostfixTypeSuffix(parser, typeExpr); +} - TypeExp Parser::ParseTypeExp() - { - return TypeExp(ParseType()); - } +static Expr* _parseInfixTypeExpr(Parser* parser); - enum class Associativity +static Expr* _parseInfixTypeExprSuffix(Parser* parser, Expr* leftExpr) +{ + for (;;) { - Left, Right - }; - + // As long as the next token is an `&`, we will try + // to gobble up another type expression and form + // a conjunction type expression. + auto loc = peekToken(parser).loc; + if (AdvanceIf(parser, TokenType::OpBitAnd)) + { + auto rightExpr = _parsePostfixTypeExpr(parser); - Associativity GetAssociativityFromLevel(Precedence level) - { - if (level == Precedence::Assignment) - return Associativity::Right; + auto andExpr = parser->astBuilder->create<AndTypeExpr>(); + andExpr->loc = loc; + andExpr->left = TypeExp(leftExpr); + andExpr->right = TypeExp(rightExpr); + leftExpr = andExpr; + } else - return Associativity::Left; + { + break; + } } - Precedence GetOpLevel(Parser* parser, const Token& token) - { - switch(token.type) - { - case TokenType::QuestionMark: - return Precedence::TernaryConditional; - case TokenType::Comma: - return Precedence::Comma; - case TokenType::OpAssign: - case TokenType::OpMulAssign: - case TokenType::OpDivAssign: - case TokenType::OpAddAssign: - case TokenType::OpSubAssign: - case TokenType::OpModAssign: - case TokenType::OpShlAssign: - case TokenType::OpShrAssign: - case TokenType::OpOrAssign: - case TokenType::OpAndAssign: - case TokenType::OpXorAssign: - return Precedence::Assignment; - case TokenType::OpOr: - return Precedence::LogicalOr; - case TokenType::OpAnd: - return Precedence::LogicalAnd; - case TokenType::OpBitOr: - return Precedence::BitOr; - case TokenType::OpBitXor: - return Precedence::BitXor; - case TokenType::OpBitAnd: - return Precedence::BitAnd; - case TokenType::OpEql: - case TokenType::OpNeq: - return Precedence::EqualityComparison; - case TokenType::OpGreater: - case TokenType::OpGeq: - // Don't allow these ops inside a generic argument - if (parser->genericDepth > 0) return Precedence::Invalid; - ; // fall-thru - case TokenType::OpLeq: - case TokenType::OpLess: - return Precedence::RelationalComparison; - case TokenType::OpRsh: - // Don't allow this op inside a generic argument - if (parser->genericDepth > 0) return Precedence::Invalid; - ; // fall-thru - case TokenType::OpLsh: - return Precedence::BitShift; - case TokenType::OpAdd: - case TokenType::OpSub: - return Precedence::Additive; - case TokenType::OpMul: - case TokenType::OpDiv: - case TokenType::OpMod: - return Precedence::Multiplicative; - default: + return leftExpr; +} + +/// Parse an infix type expression. +/// +/// Currently, the only infix type expressions we support are the `&` +/// operator for forming interface conjunctions and the `->` operator +/// for functions. +/// +static Expr* _parseInfixTypeExpr(Parser* parser) +{ + auto leftExpr = _parsePostfixTypeExpr(parser); + return _parseInfixTypeExprSuffix(parser, leftExpr); +} + +Expr* Parser::ParseType() +{ + return _parseInfixTypeExpr(this); +} + +TypeExp Parser::ParseTypeExp() +{ + return TypeExp(ParseType()); +} + +enum class Associativity +{ + Left, + Right +}; + + +Associativity GetAssociativityFromLevel(Precedence level) +{ + if (level == Precedence::Assignment) + return Associativity::Right; + else + return Associativity::Left; +} + +Precedence GetOpLevel(Parser* parser, const Token& token) +{ + switch (token.type) + { + case TokenType::QuestionMark: return Precedence::TernaryConditional; + case TokenType::Comma: return Precedence::Comma; + case TokenType::OpAssign: + case TokenType::OpMulAssign: + case TokenType::OpDivAssign: + case TokenType::OpAddAssign: + case TokenType::OpSubAssign: + case TokenType::OpModAssign: + case TokenType::OpShlAssign: + case TokenType::OpShrAssign: + case TokenType::OpOrAssign: + case TokenType::OpAndAssign: + case TokenType::OpXorAssign: return Precedence::Assignment; + case TokenType::OpOr: return Precedence::LogicalOr; + case TokenType::OpAnd: return Precedence::LogicalAnd; + case TokenType::OpBitOr: return Precedence::BitOr; + case TokenType::OpBitXor: return Precedence::BitXor; + case TokenType::OpBitAnd: return Precedence::BitAnd; + case TokenType::OpEql: + case TokenType::OpNeq: return Precedence::EqualityComparison; + case TokenType::OpGreater: + case TokenType::OpGeq: + // Don't allow these ops inside a generic argument + if (parser->genericDepth > 0) + return Precedence::Invalid; + ; // fall-thru + case TokenType::OpLeq: + case TokenType::OpLess: return Precedence::RelationalComparison; + case TokenType::OpRsh: + // Don't allow this op inside a generic argument + if (parser->genericDepth > 0) + return Precedence::Invalid; + ; // fall-thru + case TokenType::OpLsh: return Precedence::BitShift; + case TokenType::OpAdd: + case TokenType::OpSub: return Precedence::Additive; + case TokenType::OpMul: + case TokenType::OpDiv: + case TokenType::OpMod: return Precedence::Multiplicative; + default: { const auto content = token.getContent(); if (content == "is" || content == "as") @@ -6206,628 +6120,668 @@ namespace Slang } return Precedence::Invalid; } - } } +} - static Expr* parseOperator(Parser* parser) +static Expr* parseOperator(Parser* parser) +{ + Token opToken; + switch (parser->tokenReader.peekTokenType()) { - Token opToken; - switch(parser->tokenReader.peekTokenType()) - { - case TokenType::QuestionMark: - opToken = parser->ReadToken(); - opToken.setContent(UnownedStringSlice::fromLiteral("?:")); - break; + case TokenType::QuestionMark: + opToken = parser->ReadToken(); + opToken.setContent(UnownedStringSlice::fromLiteral("?:")); + break; - default: - opToken = parser->ReadToken(); - break; - } + default: opToken = parser->ReadToken(); break; + } - auto opExpr = parser->astBuilder->create<VarExpr>(); - opExpr->name = getName(parser, opToken.getContent()); - opExpr->scope = parser->currentScope; - opExpr->loc = opToken.loc; + auto opExpr = parser->astBuilder->create<VarExpr>(); + opExpr->name = getName(parser, opToken.getContent()); + opExpr->scope = parser->currentScope; + opExpr->loc = opToken.loc; - return opExpr; + return opExpr; +} - } +static Expr* createInfixExpr(Parser* parser, Expr* left, Expr* op, Expr* right) +{ + InfixExpr* expr = parser->astBuilder->create<InfixExpr>(); + expr->loc = op->loc; + expr->functionExpr = op; + expr->arguments.add(left); + expr->arguments.add(right); + return expr; +} - static Expr* createInfixExpr( - Parser* parser, - Expr* left, - Expr* op, - Expr* right) +static Expr* parseInfixExprWithPrecedence(Parser* parser, Expr* inExpr, Precedence prec) +{ + auto expr = inExpr; + for (;;) { - InfixExpr* expr = parser->astBuilder->create<InfixExpr>(); - expr->loc = op->loc; - expr->functionExpr = op; - expr->arguments.add(left); - expr->arguments.add(right); - return expr; - } + auto opToken = parser->tokenReader.peekToken(); + auto opPrec = GetOpLevel(parser, opToken); + if (opPrec < prec) + break; - static Expr* parseInfixExprWithPrecedence( - Parser* parser, - Expr* inExpr, - Precedence prec) - { - auto expr = inExpr; - for(;;) + // Special case the "is" and "as" operators. + if (opToken.type == TokenType::Identifier) { - auto opToken = parser->tokenReader.peekToken(); - auto opPrec = GetOpLevel(parser, opToken); - if(opPrec < prec) - break; + const auto content = opToken.getContent(); - // Special case the "is" and "as" operators. - if (opToken.type == TokenType::Identifier) + if (content == "is") { - const auto content = opToken.getContent(); - - if (content == "is") - { - auto isExpr = parser->astBuilder->create<IsTypeExpr>(); - isExpr->value = expr; - parser->ReadToken(); - isExpr->typeExpr = parser->ParseTypeExp(); - isExpr->loc = opToken.loc; - expr = isExpr; - continue; - } - else if (content == "as") - { - auto asExpr = parser->astBuilder->create<AsTypeExpr>(); - asExpr->value = expr; - parser->ReadToken(); - asExpr->typeExpr = parser->ParseType(); - asExpr->loc = opToken.loc; - expr = asExpr; - continue; - } + auto isExpr = parser->astBuilder->create<IsTypeExpr>(); + isExpr->value = expr; + parser->ReadToken(); + isExpr->typeExpr = parser->ParseTypeExp(); + isExpr->loc = opToken.loc; + expr = isExpr; + continue; + } + else if (content == "as") + { + auto asExpr = parser->astBuilder->create<AsTypeExpr>(); + asExpr->value = expr; + parser->ReadToken(); + asExpr->typeExpr = parser->ParseType(); + asExpr->loc = opToken.loc; + expr = asExpr; + continue; } + } - auto op = parseOperator(parser); + auto op = parseOperator(parser); - // Special case the `?:` operator since it is the - // one non-binary case we need to deal with. - if(opToken.type == TokenType::QuestionMark) - { - SelectExpr* select = parser->astBuilder->create<SelectExpr>(); - select->loc = op->loc; - select->functionExpr = op; + // Special case the `?:` operator since it is the + // one non-binary case we need to deal with. + if (opToken.type == TokenType::QuestionMark) + { + SelectExpr* select = parser->astBuilder->create<SelectExpr>(); + select->loc = op->loc; + select->functionExpr = op; - select->arguments.add(expr); + select->arguments.add(expr); - select->arguments.add(parser->ParseExpression(opPrec)); - parser->ReadToken(TokenType::Colon); - select->arguments.add(parser->ParseExpression(opPrec)); + select->arguments.add(parser->ParseExpression(opPrec)); + parser->ReadToken(TokenType::Colon); + select->arguments.add(parser->ParseExpression(opPrec)); - expr = select; - continue; - } + expr = select; + continue; + } - auto right = parser->ParseLeafExpression(); + auto right = parser->ParseLeafExpression(); - for(;;) - { - auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.peekToken()); + for (;;) + { + auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.peekToken()); - if((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) ? (nextOpPrec < opPrec) : (nextOpPrec <= opPrec)) - break; + if ((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) + ? (nextOpPrec < opPrec) + : (nextOpPrec <= opPrec)) + break; - right = parseInfixExprWithPrecedence(parser, right, nextOpPrec); - } + right = parseInfixExprWithPrecedence(parser, right, nextOpPrec); + } - if (opToken.type == TokenType::OpAssign) - { - AssignExpr* assignExpr = parser->astBuilder->create<AssignExpr>(); - assignExpr->loc = op->loc; - assignExpr->left = expr; - assignExpr->right = right; + if (opToken.type == TokenType::OpAssign) + { + AssignExpr* assignExpr = parser->astBuilder->create<AssignExpr>(); + assignExpr->loc = op->loc; + assignExpr->left = expr; + assignExpr->right = right; - expr = assignExpr; - } - else - { - expr = createInfixExpr(parser, expr, op, right); - } + expr = assignExpr; + } + else + { + expr = createInfixExpr(parser, expr, op, right); } - return expr; } + return expr; +} - Expr* Parser::ParseExpression(Precedence level) - { - auto expr = ParseLeafExpression(); - return parseInfixExprWithPrecedence(this, expr, level); - } +Expr* Parser::ParseExpression(Precedence level) +{ + auto expr = ParseLeafExpression(); + return parseInfixExprWithPrecedence(this, expr, level); +} - // We *might* be looking at an application of a generic to arguments, - // but we need to disambiguate to make sure. - static Expr* maybeParseGenericApp( - Parser* parser, +// We *might* be looking at an application of a generic to arguments, +// but we need to disambiguate to make sure. +static Expr* maybeParseGenericApp( + Parser* parser, - // TODO: need to support more general expressions here - Expr* base) - { - if(peekTokenType(parser) != TokenType::OpLess) - return base; - return tryParseGenericApp(parser, base); - } + // TODO: need to support more general expressions here + Expr* base) +{ + if (peekTokenType(parser) != TokenType::OpLess) + return base; + return tryParseGenericApp(parser, base); +} - static Expr* parsePrefixExpr(Parser* parser); +static Expr* parsePrefixExpr(Parser* parser); - // Parse OOP `this` expression syntax - static NodeBase* parseThisExpr(Parser* parser, void* /*userData*/) - { - ThisExpr* expr = parser->astBuilder->create<ThisExpr>(); - expr->scope = parser->currentScope; - return expr; - } +// Parse OOP `this` expression syntax +static NodeBase* parseThisExpr(Parser* parser, void* /*userData*/) +{ + ThisExpr* expr = parser->astBuilder->create<ThisExpr>(); + expr->scope = parser->currentScope; + return expr; +} - static NodeBase* parseReturnValExpr(Parser* parser, void* /*userData*/) - { - ReturnValExpr* expr = parser->astBuilder->create<ReturnValExpr>(); - expr->scope = parser->currentScope; - return expr; - } +static NodeBase* parseReturnValExpr(Parser* parser, void* /*userData*/) +{ + ReturnValExpr* expr = parser->astBuilder->create<ReturnValExpr>(); + expr->scope = parser->currentScope; + return expr; +} - static Expr* parseBoolLitExpr(Parser* parser, bool value) - { - BoolLiteralExpr* expr = parser->astBuilder->create<BoolLiteralExpr>(); - expr->value = value; - return expr; - } +static Expr* parseBoolLitExpr(Parser* parser, bool value) +{ + BoolLiteralExpr* expr = parser->astBuilder->create<BoolLiteralExpr>(); + expr->value = value; + return expr; +} - static NodeBase* parseTrueExpr(Parser* parser, void* /*userData*/) - { - return parseBoolLitExpr(parser, true); - } +static NodeBase* parseTrueExpr(Parser* parser, void* /*userData*/) +{ + return parseBoolLitExpr(parser, true); +} - static NodeBase* parseFalseExpr(Parser* parser, void* /*userData*/) - { - return parseBoolLitExpr(parser, false); - } +static NodeBase* parseFalseExpr(Parser* parser, void* /*userData*/) +{ + return parseBoolLitExpr(parser, false); +} - static NodeBase* parseNullPtrExpr(Parser* parser, void* /*userData*/) - { - return parser->astBuilder->create<NullPtrLiteralExpr>(); - } +static NodeBase* parseNullPtrExpr(Parser* parser, void* /*userData*/) +{ + return parser->astBuilder->create<NullPtrLiteralExpr>(); +} - static NodeBase* parseNoneExpr(Parser* parser, void* /*userData*/) - { - return parser->astBuilder->create<NoneLiteralExpr>(); - } +static NodeBase* parseNoneExpr(Parser* parser, void* /*userData*/) +{ + return parser->astBuilder->create<NoneLiteralExpr>(); +} - static NodeBase* parseSizeOfExpr(Parser* parser, void* /*userData*/) - { - // We could have a type or a variable or an expression - SizeOfExpr* sizeOfExpr = parser->astBuilder->create<SizeOfExpr>(); +static NodeBase* parseSizeOfExpr(Parser* parser, void* /*userData*/) +{ + // We could have a type or a variable or an expression + SizeOfExpr* sizeOfExpr = parser->astBuilder->create<SizeOfExpr>(); - parser->ReadMatchingToken(TokenType::LParent); + parser->ReadMatchingToken(TokenType::LParent); - // The return type is always a Int - sizeOfExpr->type = parser->astBuilder->getIntType(); + // The return type is always a Int + sizeOfExpr->type = parser->astBuilder->getIntType(); - sizeOfExpr->value = parser->ParseExpression(); + sizeOfExpr->value = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); + parser->ReadMatchingToken(TokenType::RParent); - return sizeOfExpr; - } + return sizeOfExpr; +} - static NodeBase* parseAlignOfExpr(Parser* parser, void* /*userData*/) - { - // We could have a type or a variable or an expression - AlignOfExpr* alignOfExpr = parser->astBuilder->create<AlignOfExpr>(); +static NodeBase* parseAlignOfExpr(Parser* parser, void* /*userData*/) +{ + // We could have a type or a variable or an expression + AlignOfExpr* alignOfExpr = parser->astBuilder->create<AlignOfExpr>(); - parser->ReadMatchingToken(TokenType::LParent); + parser->ReadMatchingToken(TokenType::LParent); - // The return type is always a Int - alignOfExpr->type = parser->astBuilder->getIntType(); + // The return type is always a Int + alignOfExpr->type = parser->astBuilder->getIntType(); - alignOfExpr->value = parser->ParseExpression(); + alignOfExpr->value = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); + parser->ReadMatchingToken(TokenType::RParent); - return alignOfExpr; - } + return alignOfExpr; +} - static NodeBase* parseCountOfExpr(Parser* parser, void* /*userData*/) - { - // We could have a type or a variable or an expression - CountOfExpr* countOfExpr = parser->astBuilder->create<CountOfExpr>(); +static NodeBase* parseCountOfExpr(Parser* parser, void* /*userData*/) +{ + // We could have a type or a variable or an expression + CountOfExpr* countOfExpr = parser->astBuilder->create<CountOfExpr>(); - parser->ReadMatchingToken(TokenType::LParent); + parser->ReadMatchingToken(TokenType::LParent); - // The return type is always an Int - countOfExpr->type = parser->astBuilder->getIntType(); + // The return type is always an Int + countOfExpr->type = parser->astBuilder->getIntType(); - countOfExpr->value = parser->ParseExpression(); + countOfExpr->value = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); + parser->ReadMatchingToken(TokenType::RParent); - return countOfExpr; - } + return countOfExpr; +} - static NodeBase* parseTryExpr(Parser* parser, void* /*userData*/) - { - auto tryExpr = parser->astBuilder->create<TryExpr>(); - tryExpr->tryClauseType = TryClauseType::Standard; - tryExpr->base = parser->ParseLeafExpression(); - tryExpr->scope = parser->currentScope; - return tryExpr; - } +static NodeBase* parseTryExpr(Parser* parser, void* /*userData*/) +{ + auto tryExpr = parser->astBuilder->create<TryExpr>(); + tryExpr->tryClauseType = TryClauseType::Standard; + tryExpr->base = parser->ParseLeafExpression(); + tryExpr->scope = parser->currentScope; + return tryExpr; +} - static NodeBase* parseTreatAsDifferentiableExpr(Parser* parser, void* /*userData*/) - { - auto noDiffExpr = parser->astBuilder->create<TreatAsDifferentiableExpr>(); - noDiffExpr->innerExpr = parser->ParseLeafExpression(); - noDiffExpr->scope = parser->currentScope; - noDiffExpr->flavor = TreatAsDifferentiableExpr::Flavor::NoDiff; - return noDiffExpr; - } +static NodeBase* parseTreatAsDifferentiableExpr(Parser* parser, void* /*userData*/) +{ + auto noDiffExpr = parser->astBuilder->create<TreatAsDifferentiableExpr>(); + noDiffExpr->innerExpr = parser->ParseLeafExpression(); + noDiffExpr->scope = parser->currentScope; + noDiffExpr->flavor = TreatAsDifferentiableExpr::Flavor::NoDiff; + return noDiffExpr; +} - static bool _isFinite(double value) - { - // Lets type pun double to uint64_t, so we can detect special double values - union - { - double d; - uint64_t i; - } u = { value }; - // Detects nan and +-inf - const uint64_t i = u.i; - int e = int(i >> 52) & 0x7ff; - return (e != 0x7ff); - } +static bool _isFinite(double value) +{ + // Lets type pun double to uint64_t, so we can detect special double values + union + { + double d; + uint64_t i; + } u = {value}; + // Detects nan and +-inf + const uint64_t i = u.i; + int e = int(i >> 52) & 0x7ff; + return (e != 0x7ff); +} - enum class FloatFixKind - { - None, ///< No modification was made - Unrepresentable, ///< Unrepresentable - Zeroed, ///< Too close to 0 - Truncated, ///< Truncated to a non zero value - }; +enum class FloatFixKind +{ + None, ///< No modification was made + Unrepresentable, ///< Unrepresentable + Zeroed, ///< Too close to 0 + Truncated, ///< Truncated to a non zero value +}; + +static FloatFixKind _fixFloatLiteralValue( + BaseType type, + IRFloatingPointValue value, + IRFloatingPointValue& outValue) +{ + IRFloatingPointValue epsilon = 1e-10f; - static FloatFixKind _fixFloatLiteralValue(BaseType type, IRFloatingPointValue value, IRFloatingPointValue& outValue) + // Check the value is finite for checking narrowing to literal type losing information + if (_isFinite(value)) { - IRFloatingPointValue epsilon = 1e-10f; - - // Check the value is finite for checking narrowing to literal type losing information - if (_isFinite(value)) + switch (type) { - switch (type) + case BaseType::Float: { - case BaseType::Float: + // Fix out of range + if (value > FLT_MAX) { - // Fix out of range - if (value > FLT_MAX) + if (Math::AreNearlyEqual(value, FLT_MAX, epsilon)) { - if (Math::AreNearlyEqual(value, FLT_MAX, epsilon)) - { - outValue = FLT_MAX; - return FloatFixKind::Truncated; - } - else - { - outValue = float(INFINITY); - return FloatFixKind::Unrepresentable; - } + outValue = FLT_MAX; + return FloatFixKind::Truncated; } - else if (value < -FLT_MAX) + else { - if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon)) - { - outValue = -FLT_MAX; - return FloatFixKind::Truncated; - } - else - { - outValue = -float(INFINITY); - return FloatFixKind::Unrepresentable; - } + outValue = float(INFINITY); + return FloatFixKind::Unrepresentable; } - else if (value && float(value) == 0.0f) + } + else if (value < -FLT_MAX) + { + if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon)) { - outValue = 0.0f; - return FloatFixKind::Zeroed; + outValue = -FLT_MAX; + return FloatFixKind::Truncated; + } + else + { + outValue = -float(INFINITY); + return FloatFixKind::Unrepresentable; } - break; } - case BaseType::Double: + else if (value && float(value) == 0.0f) { - // All representable - break; + outValue = 0.0f; + return FloatFixKind::Zeroed; } - case BaseType::Half: + break; + } + case BaseType::Double: + { + // All representable + break; + } + case BaseType::Half: + { + // Fix out of range + if (value > SLANG_HALF_MAX) { - // Fix out of range - if (value > SLANG_HALF_MAX) + if (Math::AreNearlyEqual(value, FLT_MAX, epsilon)) { - if (Math::AreNearlyEqual(value, FLT_MAX, epsilon)) - { - outValue = SLANG_HALF_MAX; - return FloatFixKind::Truncated; - } - else - { - outValue = float(INFINITY); - return FloatFixKind::Unrepresentable; - } + outValue = SLANG_HALF_MAX; + return FloatFixKind::Truncated; } - else if (value < -SLANG_HALF_MAX) + else { - if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon)) - { - outValue = -SLANG_HALF_MAX; - return FloatFixKind::Truncated; - } - else - { - outValue = -float(INFINITY); - return FloatFixKind::Unrepresentable; - } + outValue = float(INFINITY); + return FloatFixKind::Unrepresentable; } - else if (value && Math::Abs(value) < SLANG_HALF_SUB_NORMAL_MIN) + } + else if (value < -SLANG_HALF_MAX) + { + if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon)) { - outValue = 0.0f; - return FloatFixKind::Zeroed; + outValue = -SLANG_HALF_MAX; + return FloatFixKind::Truncated; } - break; + else + { + outValue = -float(INFINITY); + return FloatFixKind::Unrepresentable; + } + } + else if (value && Math::Abs(value) < SLANG_HALF_SUB_NORMAL_MIN) + { + outValue = 0.0f; + return FloatFixKind::Zeroed; } - default: break; + break; } + default: break; } - - outValue = value; - return FloatFixKind::None; } - static IntegerLiteralValue _fixIntegerLiteral(BaseType baseType, IntegerLiteralValue value, Token* token, DiagnosticSink* sink) - { - // TODO(JS): - // It is worth noting here that because of the way that the lexer works, that literals - // are always handled as if they are positive (a preceding - is taken as a negate on a - // positive value). - // The code here is designed to work with positive and negative values, as this behavior - // might change in the future, and is arguably more 'correct'. + outValue = value; + return FloatFixKind::None; +} - const BaseTypeInfo& info = BaseTypeInfo::getInfo(baseType); - SLANG_ASSERT(info.flags & BaseTypeInfo::Flag::Integer); - SLANG_COMPILE_TIME_ASSERT(sizeof(value) == sizeof(uint64_t)); +static IntegerLiteralValue _fixIntegerLiteral( + BaseType baseType, + IntegerLiteralValue value, + Token* token, + DiagnosticSink* sink) +{ + // TODO(JS): + // It is worth noting here that because of the way that the lexer works, that literals + // are always handled as if they are positive (a preceding - is taken as a negate on a + // positive value). + // The code here is designed to work with positive and negative values, as this behavior + // might change in the future, and is arguably more 'correct'. - // If the type is 64 bits, do nothing, we'll assume all is good - if (baseType != BaseType::Void && info.sizeInBytes != sizeof(value)) - { - const IntegerLiteralValue signBit = IntegerLiteralValue(1) << (8 * info.sizeInBytes - 1); - // Same as (~IntegerLiteralValue(0)) << (8 * info.sizeInBytes);, without the need for variable shift - const IntegerLiteralValue mask = -(signBit + signBit); + const BaseTypeInfo& info = BaseTypeInfo::getInfo(baseType); + SLANG_ASSERT(info.flags & BaseTypeInfo::Flag::Integer); + SLANG_COMPILE_TIME_ASSERT(sizeof(value) == sizeof(uint64_t)); - IntegerLiteralValue truncatedValue = value; - // If it's signed, and top bit is set, sign extend it negative - if (info.flags & BaseTypeInfo::Flag::Signed) - { - // Sign extend - truncatedValue = (value & signBit) ? (value | mask) : (value & ~mask); - } - else - { - // 0 top bits - truncatedValue = value & ~mask; - } + // If the type is 64 bits, do nothing, we'll assume all is good + if (baseType != BaseType::Void && info.sizeInBytes != sizeof(value)) + { + const IntegerLiteralValue signBit = IntegerLiteralValue(1) << (8 * info.sizeInBytes - 1); + // Same as (~IntegerLiteralValue(0)) << (8 * info.sizeInBytes);, without the need for + // variable shift + const IntegerLiteralValue mask = -(signBit + signBit); - const IntegerLiteralValue maskedValue = value & mask; + IntegerLiteralValue truncatedValue = value; + // If it's signed, and top bit is set, sign extend it negative + if (info.flags & BaseTypeInfo::Flag::Signed) + { + // Sign extend + truncatedValue = (value & signBit) ? (value | mask) : (value & ~mask); + } + else + { + // 0 top bits + truncatedValue = value & ~mask; + } - // If the masked value is 0 or equal to the mask, we 'assume' no information is - // lost - // This allows for example -1u, to give 0xffffffff - // It also means 0xfffffffffffffffffu will give 0xffffffff, without a warning. - if ((!(maskedValue == 0 || maskedValue == mask)) && sink && token) - { - // Output a warning that number has been altered - sink->diagnose(*token, Diagnostics::integerLiteralTruncated, token->getContent(), BaseTypeInfo::asText(baseType), truncatedValue); - } + const IntegerLiteralValue maskedValue = value & mask; - value = truncatedValue; + // If the masked value is 0 or equal to the mask, we 'assume' no information is + // lost + // This allows for example -1u, to give 0xffffffff + // It also means 0xfffffffffffffffffu will give 0xffffffff, without a warning. + if ((!(maskedValue == 0 || maskedValue == mask)) && sink && token) + { + // Output a warning that number has been altered + sink->diagnose( + *token, + Diagnostics::integerLiteralTruncated, + token->getContent(), + BaseTypeInfo::asText(baseType), + truncatedValue); } - return value; + value = truncatedValue; } - static bool _isCast(Parser* parser, Expr* expr) + return value; +} + +static bool _isCast(Parser* parser, Expr* expr) +{ + if (as<PointerTypeExpr>(expr)) { - if (as<PointerTypeExpr>(expr)) - { - return true; - } + return true; + } - // We can't just look at expr and look up if it's a type, because we allow - // out-of-order declarations. So to a first approximation we'll try and - // determine if it is a cast via a heuristic based on what comes next + // We can't just look at expr and look up if it's a type, because we allow + // out-of-order declarations. So to a first approximation we'll try and + // determine if it is a cast via a heuristic based on what comes next - TokenType tokenType = peekTokenType(parser); + TokenType tokenType = peekTokenType(parser); - // Expression - // ========== - // - // Misc: ; ) [ ] , . = ? (ternary) { } ++ -- -> - // Binary ops: * / | & ^ % << >> - // Logical ops: || && - // Comparisons: != == < > <= => - // - // Any assign op - // - // If we don't have pointers then - // & : (Thing::Another) &v - // * : (Thing::Another)*ptr is a cast. - // - // Cast - // ==== - // - // Misc: ( - // Identifier, Literal - // Unary ops: !, ~ - // - // Ambiguous - // ========= - // - // - : Can be unary and therefore a cast or a binary subtract, and therefore an expression - // + : Can be unary and therefore could be a cast, or a binary add and therefore an expression - // - // Arbitrary - // ========= - // - // End of file, End of directive, Invalid, :, :: + // Expression + // ========== + // + // Misc: ; ) [ ] , . = ? (ternary) { } ++ -- -> + // Binary ops: * / | & ^ % << >> + // Logical ops: || && + // Comparisons: != == < > <= => + // + // Any assign op + // + // If we don't have pointers then + // & : (Thing::Another) &v + // * : (Thing::Another)*ptr is a cast. + // + // Cast + // ==== + // + // Misc: ( + // Identifier, Literal + // Unary ops: !, ~ + // + // Ambiguous + // ========= + // + // - : Can be unary and therefore a cast or a binary subtract, and therefore an expression + // + : Can be unary and therefore could be a cast, or a binary add and therefore an expression + // + // Arbitrary + // ========= + // + // End of file, End of directive, Invalid, :, :: - switch (tokenType) + switch (tokenType) + { + case TokenType::FloatingPointLiteral: + case TokenType::CharLiteral: + case TokenType::IntegerLiteral: + case TokenType::Identifier: + case TokenType::OpNot: + case TokenType::OpBitNot: { - case TokenType::FloatingPointLiteral: - case TokenType::CharLiteral: - case TokenType::IntegerLiteral: - case TokenType::Identifier: - case TokenType::OpNot: - case TokenType::OpBitNot: - { - // If followed by one of these, must be a cast - return true; - } - case TokenType::LParent: + // If followed by one of these, must be a cast + return true; + } + case TokenType::LParent: + { + // If we are followed by ( it might not be a cast - it could be a call invocation. + // BUT we can always *assume* it is a call, because such a 'call' will be correctly + // handled as a cast if necessary later. + return false; + } + case TokenType::OpAdd: + case TokenType::OpSub: + case TokenType::OpMul: + case TokenType::OpBitAnd: + { + // + - are ambiguous, it could be a binary + or - so -> expression, or unary -> cast + // + // (Some::Stuff) + 3 + // (Some::Stuff) - 3 + // Strictly I can only tell if this is an expression or a cast if I know Some::Stuff is + // a type or not but we can't know here in general because we allow out-of-order + // declarations. + + // If we can determine it's a type, then it must be a cast, and we are done. + // + // NOTE! This test can only determine if it's a type *iff* it has already been defined. + // A future out of order declaration, will not be correctly found here. + // + // This means the semantics change depending on the order of definition (!) + Decl* decl = _tryResolveDecl(parser, expr); + // If we can find the decl-> we can resolve unambiguously + if (decl) { - // If we are followed by ( it might not be a cast - it could be a call invocation. - // BUT we can always *assume* it is a call, because such a 'call' will be correctly - // handled as a cast if necessary later. - return false; + return _isType(decl); } - case TokenType::OpAdd: - case TokenType::OpSub: - case TokenType::OpMul: - case TokenType::OpBitAnd: - { - // + - are ambiguous, it could be a binary + or - so -> expression, or unary -> cast - // - // (Some::Stuff) + 3 - // (Some::Stuff) - 3 - // Strictly I can only tell if this is an expression or a cast if I know Some::Stuff is a type or not - // but we can't know here in general because we allow out-of-order declarations. - - // If we can determine it's a type, then it must be a cast, and we are done. - // - // NOTE! This test can only determine if it's a type *iff* it has already been defined. A future out - // of order declaration, will not be correctly found here. - // - // This means the semantics change depending on the order of definition (!) - Decl* decl = _tryResolveDecl(parser, expr); - // If we can find the decl-> we can resolve unambiguously - if (decl) - { - return _isType(decl); - } - // Now we use a heuristic. - // - // Whitespace before, whitespace after->binary - // No whitespace before, no whitespace after->binary - // Otherwise->unary - // - // Unary -> cast, binary -> expression. - // - // Ie: - // (Some::Stuff) +3 - must be a cast - // (Some::Stuff)+ 3 - must be a cast (?) This is a bit odd. - // (Some::Stuff) + 3 - must be an expression. - // (Some::Stuff)+3 - must be an expression. - - // TODO(JS): This covers the (SomeScope::Identifier) case - // - // But perhaps there other ways of referring to types, that this now misses? With associated types/generics perhaps. - // - // For now we'll assume it's not a cast if it's not a StaticMemberExpr - // The reason for the restriction (which perhaps can be broadened), is we don't - // want the interpretation of something in parentheses to be determined by something as common as + or - whitespace. + // Now we use a heuristic. + // + // Whitespace before, whitespace after->binary + // No whitespace before, no whitespace after->binary + // Otherwise->unary + // + // Unary -> cast, binary -> expression. + // + // Ie: + // (Some::Stuff) +3 - must be a cast + // (Some::Stuff)+ 3 - must be a cast (?) This is a bit odd. + // (Some::Stuff) + 3 - must be an expression. + // (Some::Stuff)+3 - must be an expression. - if (const auto staticMemberExpr = dynamicCast<StaticMemberExpr>(expr)) - { - // Apply the heuristic: - TokenReader::ParsingCursor cursor = parser->tokenReader.getCursor(); - // Skip the + or - - const Token opToken = advanceToken(parser); - // Peek the next token to see if it was preceded by white space - const Token nextToken = peekToken(parser); + // TODO(JS): This covers the (SomeScope::Identifier) case + // + // But perhaps there other ways of referring to types, that this now misses? With + // associated types/generics perhaps. + // + // For now we'll assume it's not a cast if it's not a StaticMemberExpr + // The reason for the restriction (which perhaps can be broadened), is we don't + // want the interpretation of something in parentheses to be determined by something as + // common as + or - whitespace. - // Rewind - parser->tokenReader.setCursor(cursor); + if (const auto staticMemberExpr = dynamicCast<StaticMemberExpr>(expr)) + { + // Apply the heuristic: + TokenReader::ParsingCursor cursor = parser->tokenReader.getCursor(); + // Skip the + or - + const Token opToken = advanceToken(parser); + // Peek the next token to see if it was preceded by white space + const Token nextToken = peekToken(parser); - const bool isBinary = (nextToken.flags & TokenFlag::AfterWhitespace) == (opToken.flags & TokenFlag::AfterWhitespace); + // Rewind + parser->tokenReader.setCursor(cursor); - // If it's binary it's not a cast - return !isBinary; - } - break; + const bool isBinary = (nextToken.flags & TokenFlag::AfterWhitespace) == + (opToken.flags & TokenFlag::AfterWhitespace); + + // If it's binary it's not a cast + return !isBinary; } - default: break; + break; } - - // We'll assume it's not a cast - return false; + default: break; } - static bool tryParseExpression(Parser* parser, Expr* &outExpr, TokenType tokenTypeAfter) - { - auto cursor = parser->tokenReader.getCursor(); - auto isRecovering = parser->isRecovering; - auto oldSink = parser->sink; - DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr); - parser->sink = &newSink; - outExpr = parser->ParseExpression(); - parser->sink = oldSink; - parser->isRecovering = isRecovering; - if (outExpr && newSink.getErrorCount() == 0 && parser->LookAheadToken(tokenTypeAfter)) - { - return true; - } - parser->tokenReader.setCursor(cursor); - return false; - } + // We'll assume it's not a cast + return false; +} + +static bool tryParseExpression(Parser* parser, Expr*& outExpr, TokenType tokenTypeAfter) +{ + auto cursor = parser->tokenReader.getCursor(); + auto isRecovering = parser->isRecovering; + auto oldSink = parser->sink; + DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr); + parser->sink = &newSink; + outExpr = parser->ParseExpression(); + parser->sink = oldSink; + parser->isRecovering = isRecovering; + if (outExpr && newSink.getErrorCount() == 0 && parser->LookAheadToken(tokenTypeAfter)) + { + return true; + } + parser->tokenReader.setCursor(cursor); + return false; +} - static Expr* parseAtomicExpr(Parser* parser) +static Expr* parseAtomicExpr(Parser* parser) +{ + switch (peekTokenType(parser)) { - switch( peekTokenType(parser) ) + default: + // TODO: should this return an error expression instead of NULL? + parser->diagnose(parser->tokenReader.peekLoc(), Diagnostics::syntaxError); + return parser->astBuilder->create<IncompleteExpr>(); + + // Either: + // - parenthesized expression `(exp)` + // - cast `(type) exp` + // + // Proper disambiguation requires mixing up parsing + // and semantic checking (which we should do eventually) + // but for now we will follow some heuristics. + case TokenType::LParent: { - default: - // TODO: should this return an error expression instead of NULL? - parser->diagnose(parser->tokenReader.peekLoc(), Diagnostics::syntaxError); - return parser->astBuilder->create<IncompleteExpr>(); + Token openParen = parser->ReadToken(TokenType::LParent); - // Either: - // - parenthesized expression `(exp)` - // - cast `(type) exp` - // - // Proper disambiguation requires mixing up parsing - // and semantic checking (which we should do eventually) - // but for now we will follow some heuristics. - case TokenType::LParent: + // Only handles cases of `(type)`, where type is a single identifier, + // and at this point the type is known + if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1)) + { + // Get the identifier for the type + const Token typeToken = advanceToken(parser); + // Consume the closing `)` + parser->ReadToken(TokenType::RParent); + + auto varExpr = parser->astBuilder->create<VarExpr>(); + varExpr->scope = parser->currentScope; + varExpr->loc = typeToken.loc; + varExpr->name = typeToken.getName(); + + TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>(); + tcexpr->loc = openParen.loc; + + tcexpr->functionExpr = varExpr; + + auto arg = parsePrefixExpr(parser); + tcexpr->arguments.add(arg); + + return tcexpr; + } + else { - Token openParen = parser->ReadToken(TokenType::LParent); + // The above branch catches the case where we have a cast like (Thing), but with + // the scoping operator it will not handle (SomeScope::Thing). In that case this + // branch will be taken. This is okay in so far as SomeScope::Thing will parse + // as an expression. - // Only handles cases of `(type)`, where type is a single identifier, - // and at this point the type is known - if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1)) + Expr* base = nullptr; + if (!tryParseExpression(parser, base, TokenType::RParent)) { - // Get the identifier for the type - const Token typeToken = advanceToken(parser); - // Consume the closing `)` - parser->ReadToken(TokenType::RParent); + base = parser->ParseType(); + } - auto varExpr = parser->astBuilder->create<VarExpr>(); - varExpr->scope = parser->currentScope; - varExpr->loc = typeToken.loc; - varExpr->name = typeToken.getName(); + parser->ReadToken(TokenType::RParent); + + // We now try and determine by what base is, if this is actually a cast or an + // expression in parentheses + if (_isCast(parser, base)) + { + // Parse as a cast TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>(); tcexpr->loc = openParen.loc; - tcexpr->functionExpr = varExpr; + tcexpr->functionExpr = base; auto arg = parsePrefixExpr(parser); tcexpr->arguments.add(arg); @@ -6836,466 +6790,443 @@ namespace Slang } else { - // The above branch catches the case where we have a cast like (Thing), but with - // the scoping operator it will not handle (SomeScope::Thing). In that case this - // branch will be taken. This is okay in so far as SomeScope::Thing will parse - // as an expression. - - Expr* base = nullptr; - if (!tryParseExpression(parser, base, TokenType::RParent)) - { - base = parser->ParseType(); - } - - parser->ReadToken(TokenType::RParent); - - // We now try and determine by what base is, if this is actually a cast or an expression in parentheses - if (_isCast(parser, base)) - { - // Parse as a cast + // Pass as an expression in parentheses - TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>(); - tcexpr->loc = openParen.loc; - - tcexpr->functionExpr = base; - - auto arg = parsePrefixExpr(parser); - tcexpr->arguments.add(arg); - - return tcexpr; - } - else - { - // Pass as an expression in parentheses - - ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>(); - parenExpr->loc = openParen.loc; - parenExpr->base = base; - return parenExpr; - } + ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>(); + parenExpr->loc = openParen.loc; + parenExpr->base = base; + return parenExpr; } } + } - // An initializer list `{ expr, ... }` - case TokenType::LBrace: - { - InitializerListExpr* initExpr = parser->astBuilder->create<InitializerListExpr>(); - parser->FillPosition(initExpr); + // An initializer list `{ expr, ... }` + case TokenType::LBrace: + { + InitializerListExpr* initExpr = parser->astBuilder->create<InitializerListExpr>(); + parser->FillPosition(initExpr); - // Initializer list - parser->ReadToken(TokenType::LBrace); + // Initializer list + parser->ReadToken(TokenType::LBrace); - List<Expr*> exprs; + List<Expr*> exprs; + + for (;;) + { + if (AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces)) + break; - for(;;) + auto expr = parser->ParseArgExpr(); + if (expr) { - if(AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces)) - break; + initExpr->args.add(expr); + } - auto expr = parser->ParseArgExpr(); - if( expr ) - { - initExpr->args.add(expr); - } + if (AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces)) + break; - if(AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces)) - break; + parser->ReadToken(TokenType::Comma); + } - parser->ReadToken(TokenType::Comma); - } + return initExpr; + } - return initExpr; - } + case TokenType::IntegerLiteral: + { + IntegerLiteralExpr* constExpr = parser->astBuilder->create<IntegerLiteralExpr>(); + parser->FillPosition(constExpr); - case TokenType::IntegerLiteral: - { - IntegerLiteralExpr* constExpr = parser->astBuilder->create<IntegerLiteralExpr>(); - parser->FillPosition(constExpr); + auto token = parser->tokenReader.advanceToken(); + constExpr->token = token; - auto token = parser->tokenReader.advanceToken(); - constExpr->token = token; + UnownedStringSlice suffix; + IntegerLiteralValue value = getIntegerLiteralValue(token, &suffix); - UnownedStringSlice suffix; - IntegerLiteralValue value = getIntegerLiteralValue(token, &suffix); + // Look at any suffix on the value + char const* suffixCursor = suffix.begin(); + const char* const suffixEnd = suffix.end(); - // Look at any suffix on the value - char const* suffixCursor = suffix.begin(); - const char*const suffixEnd = suffix.end(); + // If no suffix is defined go with the default + BaseType suffixBaseType = BaseType::Int; - // If no suffix is defined go with the default - BaseType suffixBaseType = BaseType::Int; + if (suffixCursor < suffixEnd) + { + // Mark as void, taken as an error + suffixBaseType = BaseType::Void; - if( suffixCursor < suffixEnd ) + int lCount = 0; + int uCount = 0; + int zCount = 0; + int unknownCount = 0; + while (suffixCursor < suffixEnd) { - // Mark as void, taken as an error - suffixBaseType = BaseType::Void; - - int lCount = 0; - int uCount = 0; - int zCount = 0; - int unknownCount = 0; - while(suffixCursor < suffixEnd) + switch (*suffixCursor++) { - switch( *suffixCursor++ ) - { - case 'l': case 'L': - lCount++; - break; + case 'l': + case 'L': lCount++; break; - case 'u': case 'U': - uCount++; - break; + case 'u': + case 'U': uCount++; break; - case 'z': case 'Z': - zCount++; - break; + case 'z': + case 'Z': zCount++; break; - default: - unknownCount++; - break; - } + default: unknownCount++; break; } + } - if(unknownCount) - { - parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); - suffixBaseType = BaseType::Int; - } - // `u` or `ul` suffix -> `uint` - else if(uCount == 1 && (lCount <= 1) && zCount == 0) - { - suffixBaseType = BaseType::UInt; - } - // `l` suffix on integer -> `int` (== `long`) - else if(lCount == 1 && !uCount && zCount == 0) - { - suffixBaseType = BaseType::Int; - } - // `ull` suffix -> `uint64_t` - else if(uCount == 1 && lCount == 2 && zCount == 0) - { - suffixBaseType = BaseType::UInt64; - } - // `ll` suffix -> `int64_t` - else if(uCount == 0 && lCount == 2 && zCount == 0) - { - suffixBaseType = BaseType::Int64; - } - else if (uCount == 0 && zCount == 1) - { - suffixBaseType = BaseType::IntPtr; - } - else if (uCount == 1 && zCount == 1) - { - suffixBaseType = BaseType::UIntPtr; - } - // TODO: do we need suffixes for smaller integer types? - else - { - parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); - suffixBaseType = BaseType::Int; - } + if (unknownCount) + { + parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); + suffixBaseType = BaseType::Int; + } + // `u` or `ul` suffix -> `uint` + else if (uCount == 1 && (lCount <= 1) && zCount == 0) + { + suffixBaseType = BaseType::UInt; + } + // `l` suffix on integer -> `int` (== `long`) + else if (lCount == 1 && !uCount && zCount == 0) + { + suffixBaseType = BaseType::Int; + } + // `ull` suffix -> `uint64_t` + else if (uCount == 1 && lCount == 2 && zCount == 0) + { + suffixBaseType = BaseType::UInt64; + } + // `ll` suffix -> `int64_t` + else if (uCount == 0 && lCount == 2 && zCount == 0) + { + suffixBaseType = BaseType::Int64; + } + else if (uCount == 0 && zCount == 1) + { + suffixBaseType = BaseType::IntPtr; } + else if (uCount == 1 && zCount == 1) + { + suffixBaseType = BaseType::UIntPtr; + } + // TODO: do we need suffixes for smaller integer types? + else + { + parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); + suffixBaseType = BaseType::Int; + } + } - value = _fixIntegerLiteral(suffixBaseType, value, &token, parser->sink); + value = _fixIntegerLiteral(suffixBaseType, value, &token, parser->sink); - constExpr->value = value; - constExpr->suffixType = suffixBaseType; + constExpr->value = value; + constExpr->suffixType = suffixBaseType; - return constExpr; - } + return constExpr; + } - case TokenType::FloatingPointLiteral: - { - FloatingPointLiteralExpr* constExpr = parser->astBuilder->create<FloatingPointLiteralExpr>(); - parser->FillPosition(constExpr); + case TokenType::FloatingPointLiteral: + { + FloatingPointLiteralExpr* constExpr = + parser->astBuilder->create<FloatingPointLiteralExpr>(); + parser->FillPosition(constExpr); - auto token = parser->tokenReader.advanceToken(); - constExpr->token = token; + auto token = parser->tokenReader.advanceToken(); + constExpr->token = token; - UnownedStringSlice suffix; - FloatingPointLiteralValue value = getFloatingPointLiteralValue(token, &suffix); + UnownedStringSlice suffix; + FloatingPointLiteralValue value = getFloatingPointLiteralValue(token, &suffix); - // Look at any suffix on the value - char const* suffixCursor = suffix.begin(); - const char*const suffixEnd = suffix.end(); + // Look at any suffix on the value + char const* suffixCursor = suffix.begin(); + const char* const suffixEnd = suffix.end(); - // Default is Float - BaseType suffixBaseType = BaseType::Float; - if( suffixCursor < suffixEnd ) + // Default is Float + BaseType suffixBaseType = BaseType::Float; + if (suffixCursor < suffixEnd) + { + int fCount = 0; + int lCount = 0; + int hCount = 0; + int unknownCount = 0; + while (suffixCursor < suffixEnd) { - int fCount = 0; - int lCount = 0; - int hCount = 0; - int unknownCount = 0; - while(suffixCursor < suffixEnd) + switch (*suffixCursor++) { - switch( *suffixCursor++ ) - { - case 'f': case 'F': - fCount++; - break; + case 'f': + case 'F': fCount++; break; - case 'l': case 'L': - lCount++; - break; + case 'l': + case 'L': lCount++; break; - case 'h': case 'H': - hCount++; - break; + case 'h': + case 'H': hCount++; break; - default: - unknownCount++; - break; - } + default: unknownCount++; break; } + } - if (unknownCount) - { - parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix); - suffixBaseType = BaseType::Float; - } - // `f` suffix -> `float` - if(fCount == 1 && !lCount && !hCount) - { - suffixBaseType = BaseType::Float; - } - // `l` or `lf` suffix on floating-point literal -> `double` - else if(lCount == 1 && (fCount <= 1)) - { - suffixBaseType = BaseType::Double; - } - // `h` or `hf` suffix on floating-point literal -> `half` - else if(hCount == 1 && (fCount <= 1)) - { - suffixBaseType = BaseType::Half; - } - // TODO: are there other suffixes we need to handle? - else - { - parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix); - suffixBaseType = BaseType::Float; - } + if (unknownCount) + { + parser->sink->diagnose( + token, + Diagnostics::invalidFloatingPointLiteralSuffix, + suffix); + suffixBaseType = BaseType::Float; + } + // `f` suffix -> `float` + if (fCount == 1 && !lCount && !hCount) + { + suffixBaseType = BaseType::Float; + } + // `l` or `lf` suffix on floating-point literal -> `double` + else if (lCount == 1 && (fCount <= 1)) + { + suffixBaseType = BaseType::Double; } + // `h` or `hf` suffix on floating-point literal -> `half` + else if (hCount == 1 && (fCount <= 1)) + { + suffixBaseType = BaseType::Half; + } + // TODO: are there other suffixes we need to handle? + else + { + parser->sink->diagnose( + token, + Diagnostics::invalidFloatingPointLiteralSuffix, + suffix); + suffixBaseType = BaseType::Float; + } + } - // TODO(JS): - // It is worth noting here that because of the way that the lexer works, that literals - // are always handled as if they are positive (a preceding - is taken as a negate on a - // positive value). - // The code here is designed to work with positive and negative values, as this behavior - // might change in the future, and is arguably more 'correct'. + // TODO(JS): + // It is worth noting here that because of the way that the lexer works, that literals + // are always handled as if they are positive (a preceding - is taken as a negate on a + // positive value). + // The code here is designed to work with positive and negative values, as this behavior + // might change in the future, and is arguably more 'correct'. - FloatingPointLiteralValue fixedValue = value; - auto fixType = _fixFloatLiteralValue(suffixBaseType, value, fixedValue); + FloatingPointLiteralValue fixedValue = value; + auto fixType = _fixFloatLiteralValue(suffixBaseType, value, fixedValue); - switch (fixType) + switch (fixType) + { + case FloatFixKind::Truncated: + case FloatFixKind::None: { - case FloatFixKind::Truncated: - case FloatFixKind::None: - { - // No warning. - // The truncation allowed must be very small. When Truncated the value *is* changed though. - break; - } - case FloatFixKind::Zeroed: - { - parser->sink->diagnose(token, Diagnostics::floatLiteralTooSmall, BaseTypeInfo::asText(suffixBaseType), token.getContent(), fixedValue); - break; - } - case FloatFixKind::Unrepresentable: - { - parser->sink->diagnose(token, Diagnostics::floatLiteralUnrepresentable, BaseTypeInfo::asText(suffixBaseType), token.getContent(), fixedValue); - break; - } + // No warning. + // The truncation allowed must be very small. When Truncated the value *is* + // changed though. + break; + } + case FloatFixKind::Zeroed: + { + parser->sink->diagnose( + token, + Diagnostics::floatLiteralTooSmall, + BaseTypeInfo::asText(suffixBaseType), + token.getContent(), + fixedValue); + break; + } + case FloatFixKind::Unrepresentable: + { + parser->sink->diagnose( + token, + Diagnostics::floatLiteralUnrepresentable, + BaseTypeInfo::asText(suffixBaseType), + token.getContent(), + fixedValue); + break; } + } - constExpr->value = fixedValue; - constExpr->suffixType = suffixBaseType; + constExpr->value = fixedValue; + constExpr->suffixType = suffixBaseType; - return constExpr; - } + return constExpr; + } - case TokenType::StringLiteral: - { - StringLiteralExpr* constExpr = parser->astBuilder->create<StringLiteralExpr>(); - auto token = parser->tokenReader.advanceToken(); - constExpr->token = token; - parser->FillPosition(constExpr); + case TokenType::StringLiteral: + { + StringLiteralExpr* constExpr = parser->astBuilder->create<StringLiteralExpr>(); + auto token = parser->tokenReader.advanceToken(); + constExpr->token = token; + parser->FillPosition(constExpr); - if (!parser->LookAheadToken(TokenType::StringLiteral)) - { - // Easy/common case: a single string - constExpr->value = getStringLiteralTokenValue(token); - } - else + if (!parser->LookAheadToken(TokenType::StringLiteral)) + { + // Easy/common case: a single string + constExpr->value = getStringLiteralTokenValue(token); + } + else + { + StringBuilder sb; + sb << getStringLiteralTokenValue(token); + while (parser->LookAheadToken(TokenType::StringLiteral)) { - StringBuilder sb; + token = parser->tokenReader.advanceToken(); sb << getStringLiteralTokenValue(token); - while (parser->LookAheadToken(TokenType::StringLiteral)) - { - token = parser->tokenReader.advanceToken(); - sb << getStringLiteralTokenValue(token); - } - constExpr->value = sb.produceString(); } - - return constExpr; + constExpr->value = sb.produceString(); } - case TokenType::CompletionRequest: + + return constExpr; + } + case TokenType::CompletionRequest: + { + VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); + varExpr->scope = parser->currentScope; + parser->FillPosition(varExpr); + auto nameAndLoc = NameLoc(peekToken(parser)); + varExpr->name = nameAndLoc.name; + parser->hasSeenCompletionToken = true; + // Don't consume the token, instead we skip directly. + parser->ReadToken(TokenType::Identifier); + return varExpr; + } + case TokenType::Scope: + { + parser->ReadToken(TokenType::Scope); + VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); + varExpr->scope = parser->currentScope; + while (varExpr->scope && !as<ModuleDecl>(varExpr->scope->containerDecl)) + varExpr->scope = varExpr->scope->parent; + parser->FillPosition(varExpr); + + auto nameToken = peekToken(parser); + auto nameAndLoc = NameLoc(nameToken); + varExpr->name = nameAndLoc.name; + if (nameToken.type == TokenType::CompletionRequest) { - VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); - varExpr->scope = parser->currentScope; - parser->FillPosition(varExpr); - auto nameAndLoc = NameLoc(peekToken(parser)); - varExpr->name = nameAndLoc.name; parser->hasSeenCompletionToken = true; - // Don't consume the token, instead we skip directly. - parser->ReadToken(TokenType::Identifier); - return varExpr; } - case TokenType::Scope: + else { - parser->ReadToken(TokenType::Scope); - VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); - varExpr->scope = parser->currentScope; - while (varExpr->scope && !as<ModuleDecl>(varExpr->scope->containerDecl)) - varExpr->scope = varExpr->scope->parent; - parser->FillPosition(varExpr); - - auto nameToken = peekToken(parser); - auto nameAndLoc = NameLoc(nameToken); - varExpr->name = nameAndLoc.name; - if (nameToken.type == TokenType::CompletionRequest) - { - parser->hasSeenCompletionToken = true; - } - else + parser->ReadToken(TokenType::Identifier); + if (peekTokenType(parser) == TokenType::OpLess) { - parser->ReadToken(TokenType::Identifier); - if (peekTokenType(parser) == TokenType::OpLess) - { - return maybeParseGenericApp(parser, varExpr); - } + return maybeParseGenericApp(parser, varExpr); } - return varExpr; } - case TokenType::Identifier: - { - // We will perform name lookup here so that we can find syntax - // keywords registered for use as expressions. - Token nameToken = peekToken(parser); + return varExpr; + } + case TokenType::Identifier: + { + // We will perform name lookup here so that we can find syntax + // keywords registered for use as expressions. + Token nameToken = peekToken(parser); - Expr* parsedExpr = nullptr; - if (tryParseUsingSyntaxDecl<Expr>(parser, &parsedExpr)) + Expr* parsedExpr = nullptr; + if (tryParseUsingSyntaxDecl<Expr>(parser, &parsedExpr)) + { + if (!parsedExpr->loc.isValid()) { - if (!parsedExpr->loc.isValid()) - { - parsedExpr->loc = nameToken.loc; - } - return parsedExpr; + parsedExpr->loc = nameToken.loc; } + return parsedExpr; + } - // Default behavior is just to create a name expression - VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); - varExpr->scope = parser->currentScope; - parser->FillPosition(varExpr); + // Default behavior is just to create a name expression + VarExpr* varExpr = parser->astBuilder->create<VarExpr>(); + varExpr->scope = parser->currentScope; + parser->FillPosition(varExpr); - auto nameAndLoc = ParseDeclName(parser); - varExpr->name = nameAndLoc.name; + auto nameAndLoc = ParseDeclName(parser); + varExpr->name = nameAndLoc.name; - if(peekTokenType(parser) == TokenType::OpLess) - { - return maybeParseGenericApp(parser, varExpr); - } - - return varExpr; + if (peekTokenType(parser) == TokenType::OpLess) + { + return maybeParseGenericApp(parser, varExpr); } + + return varExpr; } } +} - static Expr* parsePostfixExpr(Parser* parser) +static Expr* parsePostfixExpr(Parser* parser) +{ + auto expr = parseAtomicExpr(parser); + for (;;) { - auto expr = parseAtomicExpr(parser); - for(;;) + auto nextTokenType = peekTokenType(parser); + switch (nextTokenType) { - auto nextTokenType = peekTokenType(parser); - switch (nextTokenType) - { - default: - return expr; + default: return expr; - // Postfix increment/decrement - case TokenType::OpInc: - case TokenType::OpDec: - { - OperatorExpr* postfixExpr = parser->astBuilder->create<PostfixExpr>(); - parser->FillPosition(postfixExpr); - postfixExpr->functionExpr = parseOperator(parser); - postfixExpr->arguments.add(expr); + // Postfix increment/decrement + case TokenType::OpInc: + case TokenType::OpDec: + { + OperatorExpr* postfixExpr = parser->astBuilder->create<PostfixExpr>(); + parser->FillPosition(postfixExpr); + postfixExpr->functionExpr = parseOperator(parser); + postfixExpr->arguments.add(expr); - expr = postfixExpr; - } - break; + expr = postfixExpr; + } + break; - // Subscript operation `a[i]` - case TokenType::LBracket: + // Subscript operation `a[i]` + case TokenType::LBracket: + { + IndexExpr* indexExpr = parser->astBuilder->create<IndexExpr>(); + indexExpr->baseExpression = expr; + parser->FillPosition(indexExpr); + auto lBracket = parser->ReadToken(TokenType::LBracket); + indexExpr->argumentDelimeterLocs.add(lBracket.loc); + while (!parser->tokenReader.isAtEnd()) { - IndexExpr* indexExpr = parser->astBuilder->create<IndexExpr>(); - indexExpr->baseExpression = expr; - parser->FillPosition(indexExpr); - auto lBracket = parser->ReadToken(TokenType::LBracket); - indexExpr->argumentDelimeterLocs.add(lBracket.loc); - while (!parser->tokenReader.isAtEnd()) + if (!parser->LookAheadToken(TokenType::RBracket)) + indexExpr->indexExprs.add(parser->ParseArgExpr()); + else { - if (!parser->LookAheadToken(TokenType::RBracket)) - indexExpr->indexExprs.add(parser->ParseArgExpr()); - else - { - break; - } - if (!parser->LookAheadToken(TokenType::Comma)) - break; - auto comma = parser->ReadToken(TokenType::Comma); - indexExpr->argumentDelimeterLocs.add(comma.loc); + break; } - auto rBracket = parser->ReadToken(TokenType::RBracket); - indexExpr->argumentDelimeterLocs.add(rBracket.loc); - expr = indexExpr; + if (!parser->LookAheadToken(TokenType::Comma)) + break; + auto comma = parser->ReadToken(TokenType::Comma); + indexExpr->argumentDelimeterLocs.add(comma.loc); } - break; + auto rBracket = parser->ReadToken(TokenType::RBracket); + indexExpr->argumentDelimeterLocs.add(rBracket.loc); + expr = indexExpr; + } + break; - // Call operation `f(x)` - case TokenType::LParent: + // Call operation `f(x)` + case TokenType::LParent: + { + InvokeExpr* invokeExpr = parser->astBuilder->create<InvokeExpr>(); + invokeExpr->functionExpr = expr; + parser->FillPosition(invokeExpr); + auto lParen = parser->ReadToken(TokenType::LParent); + invokeExpr->argumentDelimeterLocs.add(lParen.loc); + while (!parser->tokenReader.isAtEnd()) { - InvokeExpr* invokeExpr = parser->astBuilder->create<InvokeExpr>(); - invokeExpr->functionExpr = expr; - parser->FillPosition(invokeExpr); - auto lParen = parser->ReadToken(TokenType::LParent); - invokeExpr->argumentDelimeterLocs.add(lParen.loc); - while (!parser->tokenReader.isAtEnd()) + if (!parser->LookAheadToken(TokenType::RParent)) + invokeExpr->arguments.add(parser->ParseArgExpr()); + else { - if (!parser->LookAheadToken(TokenType::RParent)) - invokeExpr->arguments.add(parser->ParseArgExpr()); - else - { - break; - } - if (!parser->LookAheadToken(TokenType::Comma)) - break; - auto comma = parser->ReadToken(TokenType::Comma); - invokeExpr->argumentDelimeterLocs.add(comma.loc); + break; } - auto rParen = parser->ReadToken(TokenType::RParent); - invokeExpr->argumentDelimeterLocs.add(rParen.loc); - expr = invokeExpr; + if (!parser->LookAheadToken(TokenType::Comma)) + break; + auto comma = parser->ReadToken(TokenType::Comma); + invokeExpr->argumentDelimeterLocs.add(comma.loc); } - break; + auto rParen = parser->ReadToken(TokenType::RParent); + invokeExpr->argumentDelimeterLocs.add(rParen.loc); + expr = invokeExpr; + } + break; - // Scope access `x::m` - case TokenType::Scope: + // Scope access `x::m` + case TokenType::Scope: { StaticMemberExpr* staticMemberExpr = parser->astBuilder->create<StaticMemberExpr>(); @@ -7314,428 +7245,438 @@ namespace Slang break; } - // Member access `x.m` or `x->m` - case TokenType::Dot: - case TokenType::RightArrow: - { - - MemberExpr* memberExpr = nextTokenType == TokenType::Dot - ? parser->astBuilder->create<MemberExpr>() - : parser->astBuilder->create<DerefMemberExpr>(); - - // TODO(tfoley): why would a member expression need this? - memberExpr->scope = parser->currentScope; - memberExpr->memberOperatorLoc = parser->tokenReader.peekLoc(); - memberExpr->baseExpression = expr; - parser->ReadToken(nextTokenType); - parser->FillPosition(memberExpr); - memberExpr->name = ParseDeclName(parser).name; - - if (peekTokenType(parser) == TokenType::OpLess) - expr = maybeParseGenericApp(parser, memberExpr); - else - expr = memberExpr; - } - break; + // Member access `x.m` or `x->m` + case TokenType::Dot: + case TokenType::RightArrow: + { + + MemberExpr* memberExpr = nextTokenType == TokenType::Dot + ? parser->astBuilder->create<MemberExpr>() + : parser->astBuilder->create<DerefMemberExpr>(); + + // TODO(tfoley): why would a member expression need this? + memberExpr->scope = parser->currentScope; + memberExpr->memberOperatorLoc = parser->tokenReader.peekLoc(); + memberExpr->baseExpression = expr; + parser->ReadToken(nextTokenType); + parser->FillPosition(memberExpr); + memberExpr->name = ParseDeclName(parser).name; + + if (peekTokenType(parser) == TokenType::OpLess) + expr = maybeParseGenericApp(parser, memberExpr); + else + expr = memberExpr; } + break; } } +} - static IRIntegerValue _foldIntegerPrefixOp(TokenType tokenType, IRIntegerValue value) +static IRIntegerValue _foldIntegerPrefixOp(TokenType tokenType, IRIntegerValue value) +{ + switch (tokenType) { - switch (tokenType) + case TokenType::OpBitNot: return ~value; + case TokenType::OpAdd: return value; + case TokenType::OpSub: return -value; + default: { - case TokenType::OpBitNot: return ~value; - case TokenType::OpAdd: return value; - case TokenType::OpSub: return -value; - default: - { - SLANG_ASSERT(!"Unexpected op"); - return value; - } + SLANG_ASSERT(!"Unexpected op"); + return value; } } +} - static IRFloatingPointValue _foldFloatPrefixOp(TokenType tokenType, IRFloatingPointValue value) +static IRFloatingPointValue _foldFloatPrefixOp(TokenType tokenType, IRFloatingPointValue value) +{ + switch (tokenType) { - switch (tokenType) + case TokenType::OpAdd: return value; + case TokenType::OpSub: return -value; + default: { - case TokenType::OpAdd: return value; - case TokenType::OpSub: return -value; - default: - { - SLANG_ASSERT(!"Unexpected op"); - return value; - } + SLANG_ASSERT(!"Unexpected op"); + return value; } } +} - static std::optional<SPIRVAsmOperand> parseSPIRVAsmOperand(Parser* parser) +static std::optional<SPIRVAsmOperand> parseSPIRVAsmOperand(Parser* parser) +{ + const auto slangIdentOperand = [&](auto flavor) { - const auto slangIdentOperand = [&](auto flavor){ - auto token = parser->tokenReader.peekToken(); - return SPIRVAsmOperand{flavor, token, parseAtomicExpr(parser)}; - }; + auto token = parser->tokenReader.peekToken(); + return SPIRVAsmOperand{flavor, token, parseAtomicExpr(parser)}; + }; - const auto slangTypeExprOperand = [&](auto flavor) { - auto tok = parser->tokenReader.peekToken(); - const auto typeExpr = parser->ParseType(); - return SPIRVAsmOperand{ flavor, tok, typeExpr }; - }; + const auto slangTypeExprOperand = [&](auto flavor) + { + auto tok = parser->tokenReader.peekToken(); + const auto typeExpr = parser->ParseType(); + return SPIRVAsmOperand{flavor, tok, typeExpr}; + }; - // The result marker - if(parser->LookAheadToken("result")) - { - return SPIRVAsmOperand{SPIRVAsmOperand::ResultMarker, parser->ReadToken()}; - } - // The handy __sampledType function - if(AdvanceIf(parser, "__sampledType")) - { - parser->ReadToken(TokenType::LParent); - const auto typeExpr = parser->ParseType(); - parser->ReadMatchingToken(TokenType::RParent); - return SPIRVAsmOperand{SPIRVAsmOperand::SampledType, Token{}, typeExpr}; - } - // The __imageType function - if (AdvanceIf(parser, "__imageType")) - { - parser->ReadToken(TokenType::LParent); - const auto typeExpr = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); - return SPIRVAsmOperand{ SPIRVAsmOperand::ImageType, Token{}, typeExpr }; - } - if (AdvanceIf(parser, "__sampledImageType")) - { - parser->ReadToken(TokenType::LParent); - const auto typeExpr = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); - return SPIRVAsmOperand{ SPIRVAsmOperand::SampledImageType, Token{}, typeExpr }; - } - else if (AdvanceIf(parser, "__convertTexel")) - { - parser->ReadToken(TokenType::LParent); - const auto texelExpr = parser->ParseExpression(); - parser->ReadMatchingToken(TokenType::RParent); - return SPIRVAsmOperand{ SPIRVAsmOperand::ConvertTexel, Token{}, texelExpr }; - } - // The pseudo-operand for component truncation - else if(parser->LookAheadToken("__truncate")) - { - return SPIRVAsmOperand{SPIRVAsmOperand::TruncateMarker, parser->ReadToken()}; - } - // The pseudo-operand for referencing entryPoint id. - else if (parser->LookAheadToken("__entryPoint")) - { - return SPIRVAsmOperand{ SPIRVAsmOperand::EntryPoint, parser->ReadToken() }; - } - else if (AdvanceIf(parser, "builtin")) - { - // reference to a builtin var. - parser->ReadToken(TokenType::LParent); - auto operand = SPIRVAsmOperand{ SPIRVAsmOperand::BuiltinVar, parser->ReadToken() }; - parser->ReadToken(TokenType::Colon); - AdvanceIf(parser, TokenType::DollarDollar); - operand.type = parser->ParseTypeExp(); - parser->ReadToken(TokenType::RParent); - return operand; - } - else if (parser->LookAheadToken("glsl450")) - { - return SPIRVAsmOperand{ SPIRVAsmOperand::GLSL450Set, parser->ReadToken() }; - } - else if (parser->LookAheadToken("debugPrintf")) - { - return SPIRVAsmOperand{ SPIRVAsmOperand::NonSemanticDebugPrintfExtSet, parser->ReadToken() }; - } - else if (AdvanceIf(parser, "__rayPayloadFromLocation")) - { - // reference a magic number to a layout(location) for late compiler resolution of rayPayload objects - parser->ReadToken(TokenType::LParent); - auto operand = SPIRVAsmOperand{ SPIRVAsmOperand::RayPayloadFromLocation, Token{}, parseAtomicExpr(parser) }; - parser->ReadToken(TokenType::RParent); - return operand; - } - else if (AdvanceIf(parser, "__rayAttributeFromLocation")) - { - // works similar to __rayPayloadFromLocation - parser->ReadToken(TokenType::LParent); - auto operand = SPIRVAsmOperand{ SPIRVAsmOperand::RayAttributeFromLocation, Token{}, parseAtomicExpr(parser) }; - parser->ReadToken(TokenType::RParent); - return operand; - } - else if (AdvanceIf(parser, "__rayCallableFromLocation")) - { - // works similar to __rayPayloadFromLocation - parser->ReadToken(TokenType::LParent); - auto operand = SPIRVAsmOperand{ SPIRVAsmOperand::RayCallableFromLocation, Token{}, parseAtomicExpr(parser) }; - parser->ReadToken(TokenType::RParent); - return operand; - } - // A regular identifier - else if(parser->LookAheadToken(TokenType::Identifier)) - { - return SPIRVAsmOperand{SPIRVAsmOperand::NamedValue, parser->ReadToken()}; - } - // A literal integer - else if(parser->LookAheadToken(TokenType::IntegerLiteral)) - { - const auto tok = parser->ReadToken(); - const auto v = getIntegerLiteralValue(tok); - if(v < 0 || v > 0xffffffff) - parser->diagnose(tok, Diagnostics::spirvOperandRange); - return SPIRVAsmOperand{SPIRVAsmOperand::Literal, tok, nullptr, {}, SpvWord(v)}; - } - // A literal string - else if(parser->LookAheadToken(TokenType::StringLiteral)) - { - return SPIRVAsmOperand{SPIRVAsmOperand::Literal, parser->ReadToken()}; - } - // A %foo id - else if(AdvanceIf(parser, TokenType::OpMod)) - { - if(parser->LookAheadToken(TokenType::IntegerLiteral) - || parser->LookAheadToken(TokenType::Identifier)) - { - return SPIRVAsmOperand{SPIRVAsmOperand::Id, parser->ReadToken()}; - } - } - // A &foo variable reference (for the address of foo) - else if(AdvanceIf(parser, TokenType::OpBitAnd)) - { - return slangIdentOperand(SPIRVAsmOperand::SlangValueAddr); - } - // A $foo variable - else if(AdvanceIf(parser, TokenType::Dollar)) - { - Expr* expr = parseAtomicExpr(parser); - return SPIRVAsmOperand{SPIRVAsmOperand::SlangValue, Token{}, expr}; - } - // A $$foo type - else if(AdvanceIf(parser, TokenType::DollarDollar)) - { - return slangTypeExprOperand(SPIRVAsmOperand::SlangType); - } - // A !immediateValue - else if (AdvanceIf(parser, TokenType::OpNot)) + // The result marker + if (parser->LookAheadToken("result")) + { + return SPIRVAsmOperand{SPIRVAsmOperand::ResultMarker, parser->ReadToken()}; + } + // The handy __sampledType function + if (AdvanceIf(parser, "__sampledType")) + { + parser->ReadToken(TokenType::LParent); + const auto typeExpr = parser->ParseType(); + parser->ReadMatchingToken(TokenType::RParent); + return SPIRVAsmOperand{SPIRVAsmOperand::SampledType, Token{}, typeExpr}; + } + // The __imageType function + if (AdvanceIf(parser, "__imageType")) + { + parser->ReadToken(TokenType::LParent); + const auto typeExpr = parser->ParseExpression(); + parser->ReadMatchingToken(TokenType::RParent); + return SPIRVAsmOperand{SPIRVAsmOperand::ImageType, Token{}, typeExpr}; + } + if (AdvanceIf(parser, "__sampledImageType")) + { + parser->ReadToken(TokenType::LParent); + const auto typeExpr = parser->ParseExpression(); + parser->ReadMatchingToken(TokenType::RParent); + return SPIRVAsmOperand{SPIRVAsmOperand::SampledImageType, Token{}, typeExpr}; + } + else if (AdvanceIf(parser, "__convertTexel")) + { + parser->ReadToken(TokenType::LParent); + const auto texelExpr = parser->ParseExpression(); + parser->ReadMatchingToken(TokenType::RParent); + return SPIRVAsmOperand{SPIRVAsmOperand::ConvertTexel, Token{}, texelExpr}; + } + // The pseudo-operand for component truncation + else if (parser->LookAheadToken("__truncate")) + { + return SPIRVAsmOperand{SPIRVAsmOperand::TruncateMarker, parser->ReadToken()}; + } + // The pseudo-operand for referencing entryPoint id. + else if (parser->LookAheadToken("__entryPoint")) + { + return SPIRVAsmOperand{SPIRVAsmOperand::EntryPoint, parser->ReadToken()}; + } + else if (AdvanceIf(parser, "builtin")) + { + // reference to a builtin var. + parser->ReadToken(TokenType::LParent); + auto operand = SPIRVAsmOperand{SPIRVAsmOperand::BuiltinVar, parser->ReadToken()}; + parser->ReadToken(TokenType::Colon); + AdvanceIf(parser, TokenType::DollarDollar); + operand.type = parser->ParseTypeExp(); + parser->ReadToken(TokenType::RParent); + return operand; + } + else if (parser->LookAheadToken("glsl450")) + { + return SPIRVAsmOperand{SPIRVAsmOperand::GLSL450Set, parser->ReadToken()}; + } + else if (parser->LookAheadToken("debugPrintf")) + { + return SPIRVAsmOperand{SPIRVAsmOperand::NonSemanticDebugPrintfExtSet, parser->ReadToken()}; + } + else if (AdvanceIf(parser, "__rayPayloadFromLocation")) + { + // reference a magic number to a layout(location) for late compiler resolution of rayPayload + // objects + parser->ReadToken(TokenType::LParent); + auto operand = SPIRVAsmOperand{ + SPIRVAsmOperand::RayPayloadFromLocation, + Token{}, + parseAtomicExpr(parser)}; + parser->ReadToken(TokenType::RParent); + return operand; + } + else if (AdvanceIf(parser, "__rayAttributeFromLocation")) + { + // works similar to __rayPayloadFromLocation + parser->ReadToken(TokenType::LParent); + auto operand = SPIRVAsmOperand{ + SPIRVAsmOperand::RayAttributeFromLocation, + Token{}, + parseAtomicExpr(parser)}; + parser->ReadToken(TokenType::RParent); + return operand; + } + else if (AdvanceIf(parser, "__rayCallableFromLocation")) + { + // works similar to __rayPayloadFromLocation + parser->ReadToken(TokenType::LParent); + auto operand = SPIRVAsmOperand{ + SPIRVAsmOperand::RayCallableFromLocation, + Token{}, + parseAtomicExpr(parser)}; + parser->ReadToken(TokenType::RParent); + return operand; + } + // A regular identifier + else if (parser->LookAheadToken(TokenType::Identifier)) + { + return SPIRVAsmOperand{SPIRVAsmOperand::NamedValue, parser->ReadToken()}; + } + // A literal integer + else if (parser->LookAheadToken(TokenType::IntegerLiteral)) + { + const auto tok = parser->ReadToken(); + const auto v = getIntegerLiteralValue(tok); + if (v < 0 || v > 0xffffffff) + parser->diagnose(tok, Diagnostics::spirvOperandRange); + return SPIRVAsmOperand{SPIRVAsmOperand::Literal, tok, nullptr, {}, SpvWord(v)}; + } + // A literal string + else if (parser->LookAheadToken(TokenType::StringLiteral)) + { + return SPIRVAsmOperand{SPIRVAsmOperand::Literal, parser->ReadToken()}; + } + // A %foo id + else if (AdvanceIf(parser, TokenType::OpMod)) + { + if (parser->LookAheadToken(TokenType::IntegerLiteral) || + parser->LookAheadToken(TokenType::Identifier)) { - Expr* expr = parseAtomicExpr(parser); - return SPIRVAsmOperand{ SPIRVAsmOperand::SlangImmediateValue, Token{}, expr }; + return SPIRVAsmOperand{SPIRVAsmOperand::Id, parser->ReadToken()}; } - Unexpected(parser); - return std::nullopt; } - - static std::optional<SPIRVAsmInst> parseSPIRVAsmInst(Parser* parser) + // A &foo variable reference (for the address of foo) + else if (AdvanceIf(parser, TokenType::OpBitAnd)) + { + return slangIdentOperand(SPIRVAsmOperand::SlangValueAddr); + } + // A $foo variable + else if (AdvanceIf(parser, TokenType::Dollar)) { - const auto& spirvInfo = parser->astBuilder->getGlobalSession()->spirvCoreGrammarInfo; + Expr* expr = parseAtomicExpr(parser); + return SPIRVAsmOperand{SPIRVAsmOperand::SlangValue, Token{}, expr}; + } + // A $$foo type + else if (AdvanceIf(parser, TokenType::DollarDollar)) + { + return slangTypeExprOperand(SPIRVAsmOperand::SlangType); + } + // A !immediateValue + else if (AdvanceIf(parser, TokenType::OpNot)) + { + Expr* expr = parseAtomicExpr(parser); + return SPIRVAsmOperand{SPIRVAsmOperand::SlangImmediateValue, Token{}, expr}; + } + Unexpected(parser); + return std::nullopt; +} + +static std::optional<SPIRVAsmInst> parseSPIRVAsmInst(Parser* parser) +{ + const auto& spirvInfo = parser->astBuilder->getGlobalSession()->spirvCoreGrammarInfo; - SPIRVAsmInst ret; + SPIRVAsmInst ret; - // We don't yet know if this is "OpFoo a b c" or "a = OpFoo b c" - const auto resultOrOpcode = parseSPIRVAsmOperand(parser); - if(!resultOrOpcode) + // We don't yet know if this is "OpFoo a b c" or "a = OpFoo b c" + const auto resultOrOpcode = parseSPIRVAsmOperand(parser); + if (!resultOrOpcode) + return std::nullopt; + + // If this is the latter, "assignment", syntax then we'll fill these in + std::optional<SPIRVAsmOperand> resultTypeOperand; + std::optional<SPIRVAsmOperand> resultOperand; + + // If we see a colon, then this `%foo : %type = OpFoo`? + if (AdvanceIf(parser, TokenType::Colon)) + { + resultTypeOperand = parseSPIRVAsmOperand(parser); + if (!resultTypeOperand) return std::nullopt; + parser->ReadToken(TokenType::OpAssign); + } - // If this is the latter, "assignment", syntax then we'll fill these in - std::optional<SPIRVAsmOperand> resultTypeOperand; - std::optional<SPIRVAsmOperand> resultOperand; + // If we have seen a type, then insist on this syntax, otherwise allow + // skipping this if + if (resultTypeOperand || AdvanceIf(parser, TokenType::OpAssign)) + { + const auto opcode = parseSPIRVAsmOperand(parser); + if (!opcode) + return std::nullopt; + ret.opcode = *opcode; + resultOperand = *resultOrOpcode; + } + else + { + ret.opcode = *resultOrOpcode; + } - // If we see a colon, then this `%foo : %type = OpFoo`? - if(AdvanceIf(parser, TokenType::Colon)) - { - resultTypeOperand = parseSPIRVAsmOperand(parser); - if(!resultTypeOperand) - return std::nullopt; - parser->ReadToken(TokenType::OpAssign); - } + const auto& opcodeWord = spirvInfo->opcodes.lookup(ret.opcode.token.getContent()); + const auto& opInfo = opcodeWord ? spirvInfo->opInfos.lookup(*opcodeWord) : std::nullopt; + ret.opcode.knownValue = opcodeWord.value_or(SpvOp(0xffffffff)); - // If we have seen a type, then insist on this syntax, otherwise allow - // skipping this if - if(resultTypeOperand || AdvanceIf(parser, TokenType::OpAssign)) - { - const auto opcode = parseSPIRVAsmOperand(parser); - if(!opcode) - return std::nullopt; - ret.opcode = *opcode; - resultOperand = *resultOrOpcode; - } - else - { - ret.opcode = *resultOrOpcode; - } + // If we couldn't find any info, but used this assignment syntax, raise + // an error + if (!opInfo && resultOperand) + { + parser->diagnose( + resultOperand->token, + Diagnostics::unrecognizedSPIRVOpcode, + ret.opcode.token); + return std::nullopt; + } - const auto& opcodeWord = spirvInfo->opcodes.lookup(ret.opcode.token.getContent()); - const auto& opInfo = opcodeWord - ? spirvInfo->opInfos.lookup(*opcodeWord) - : std::nullopt; - ret.opcode.knownValue = opcodeWord.value_or(SpvOp(0xffffffff)); + // If we have an explicit result operand (because this was a `x = + // OpFoo` instruction) then diagnose if we don't know where to put it + if (resultOperand && opInfo && opInfo->resultIdIndex == -1) + { + parser->diagnose( + resultOperand->token, + Diagnostics::spirvInstructionWithoutResultId, + ret.opcode.token); + return std::nullopt; + } - // If we couldn't find any info, but used this assignment syntax, raise - // an error - if(!opInfo && resultOperand) - { - parser->diagnose( - resultOperand->token, - Diagnostics::unrecognizedSPIRVOpcode, - ret.opcode.token - ); - return std::nullopt; - } + // Likewise for the type + if (resultTypeOperand && opInfo && opInfo->resultTypeIndex == -1) + { + parser->diagnose( + resultTypeOperand->token, + Diagnostics::spirvInstructionWithoutResultTypeId, + ret.opcode.token); + } - // If we have an explicit result operand (because this was a `x = - // OpFoo` instruction) then diagnose if we don't know where to put it - if(resultOperand && opInfo && opInfo->resultIdIndex == -1) + // + // Now we've parsed the tricky preamble, grab the rest of the operands + // At this point we can also parse bitwise or expressions + // + while (!(parser->LookAheadToken(TokenType::RBrace) || + parser->LookAheadToken(TokenType::Semicolon) || + parser->LookAheadToken(TokenType::EndOfFile)) || + resultTypeOperand || resultOperand) + { + // Insert the LHS result-type operand + if (opInfo && ret.operands.getCount() == opInfo->resultTypeIndex && resultTypeOperand) { - parser->diagnose( - resultOperand->token, - Diagnostics::spirvInstructionWithoutResultId, - ret.opcode.token - ); - return std::nullopt; + ret.operands.add(*resultTypeOperand); + resultTypeOperand.reset(); + continue; } - // Likewise for the type - if(resultTypeOperand && opInfo && opInfo->resultTypeIndex == -1) + // Insert the LHS result operand + if (opInfo && ret.operands.getCount() == opInfo->resultIdIndex && resultOperand) { - parser->diagnose( - resultTypeOperand->token, - Diagnostics::spirvInstructionWithoutResultTypeId, - ret.opcode.token - ); + ret.operands.add(*resultOperand); + resultOperand.reset(); + continue; } - // - // Now we've parsed the tricky preamble, grab the rest of the operands - // At this point we can also parse bitwise or expressions - // - while(!(parser->LookAheadToken(TokenType::RBrace) - || parser->LookAheadToken(TokenType::Semicolon) - || parser->LookAheadToken(TokenType::EndOfFile)) - || resultTypeOperand - || resultOperand) - { - // Insert the LHS result-type operand - if(opInfo && ret.operands.getCount() == opInfo->resultTypeIndex && resultTypeOperand) - { - ret.operands.add(*resultTypeOperand); - resultTypeOperand.reset(); - continue; - } - - // Insert the LHS result operand - if(opInfo && ret.operands.getCount() == opInfo->resultIdIndex && resultOperand) - { - ret.operands.add(*resultOperand); - resultOperand.reset(); - continue; - } - - if(opInfo && ret.operands.getCount() == opInfo->maxOperandCount) - { - // The SPIRV grammar says we are providing more arguments than expected operand count. - // We will issue a warning if it is likely that the user missed a semicolon. - // This is likely the case when the next operand starts with "Op" or is an assignment - // in the form of %something = .... - // - auto token = parser->tokenReader.peekToken(); - if (token.getContent().startsWith("Op") || - token.type == TokenType::OpMod && (parser->LookAheadToken(TokenType::OpAssign, 2) || parser->LookAheadToken(TokenType::Colon, 2))) - { - parser->diagnose( - parser->tokenReader.peekLoc(), - Diagnostics::spirvInstructionWithTooManyOperands, - ret.opcode.token, - opInfo->maxOperandCount - ); - } - } - - if(auto operand = parseSPIRVAsmOperand(parser)) - { - while(AdvanceIf(parser, TokenType::OpBitOr)) - { - if(const auto next = parseSPIRVAsmOperand(parser)) - operand->bitwiseOrWith.add(*next); - else - return std::nullopt; - } - ret.operands.add(*operand); - } - else + if (opInfo && ret.operands.getCount() == opInfo->maxOperandCount) + { + // The SPIRV grammar says we are providing more arguments than expected operand count. + // We will issue a warning if it is likely that the user missed a semicolon. + // This is likely the case when the next operand starts with "Op" or is an assignment + // in the form of %something = .... + // + auto token = parser->tokenReader.peekToken(); + if (token.getContent().startsWith("Op") || + token.type == TokenType::OpMod && (parser->LookAheadToken(TokenType::OpAssign, 2) || + parser->LookAheadToken(TokenType::Colon, 2))) { - break; + parser->diagnose( + parser->tokenReader.peekLoc(), + Diagnostics::spirvInstructionWithTooManyOperands, + ret.opcode.token, + opInfo->maxOperandCount); } } - if (ret.opcode.flavor == SPIRVAsmOperand::Flavor::NamedValue - && ret.opcode.knownValue == SpvOp(0xffffffff)) + if (auto operand = parseSPIRVAsmOperand(parser)) { - if (ret.opcode.token.type == TokenType::IntegerLiteral) + while (AdvanceIf(parser, TokenType::OpBitOr)) { - Int intVal = -1; - StringUtil::parseInt(ret.opcode.token.getContent(), intVal); - ret.opcode.knownValue = (SpvWord)intVal; - } - else - { - parser->diagnose(ret.opcode.token, Diagnostics::unrecognizedSPIRVOpcode, ret.opcode.token); - return std::nullopt; + if (const auto next = parseSPIRVAsmOperand(parser)) + operand->bitwiseOrWith.add(*next); + else + return std::nullopt; } + ret.operands.add(*operand); + } + else + { + break; } - - return ret; } - static Expr* parseSPIRVAsmExpr(Parser* parser, SourceLoc loc) + if (ret.opcode.flavor == SPIRVAsmOperand::Flavor::NamedValue && + ret.opcode.knownValue == SpvOp(0xffffffff)) { - SPIRVAsmExpr* asmExpr = parser->astBuilder->create<SPIRVAsmExpr>(); - asmExpr->loc = loc; - parser->ReadToken(TokenType::LBrace); - while(!parser->tokenReader.isAtEnd()) + if (ret.opcode.token.type == TokenType::IntegerLiteral) { - if(parser->LookAheadToken(TokenType::RBrace)) - break; - if(const auto inst = parseSPIRVAsmInst(parser)) - asmExpr->insts.add(*inst); - else - { - // Recover to the semi or brace - while(!(parser->LookAheadToken(TokenType::Semicolon) - || parser->LookAheadToken(TokenType::RBrace) - || parser->LookAheadToken(TokenType::EndOfFile))) - parser->ReadToken(); - } - if(parser->LookAheadToken(TokenType::RBrace)) - break; - parser->ReadToken(TokenType::Semicolon); + Int intVal = -1; + StringUtil::parseInt(ret.opcode.token.getContent(), intVal); + ret.opcode.knownValue = (SpvWord)intVal; + } + else + { + parser->diagnose( + ret.opcode.token, + Diagnostics::unrecognizedSPIRVOpcode, + ret.opcode.token); + return std::nullopt; } - parser->ReadMatchingToken(TokenType::RBrace); - - return asmExpr; } - static Expr* parseExpandExpr(Parser* parser, SourceLoc loc) - { - ExpandExpr* expandExpr = parser->astBuilder->create<ExpandExpr>(); - expandExpr->loc = loc; - expandExpr->baseExpr = parser->ParseArgExpr(); - return expandExpr; - } + return ret; +} - static Expr* parseEachExpr(Parser* parser, SourceLoc loc) +static Expr* parseSPIRVAsmExpr(Parser* parser, SourceLoc loc) +{ + SPIRVAsmExpr* asmExpr = parser->astBuilder->create<SPIRVAsmExpr>(); + asmExpr->loc = loc; + parser->ReadToken(TokenType::LBrace); + while (!parser->tokenReader.isAtEnd()) { - EachExpr* eachExpr = parser->astBuilder->create<EachExpr>(); - eachExpr->loc = loc; - eachExpr->baseExpr = parsePostfixExpr(parser); - return eachExpr; + if (parser->LookAheadToken(TokenType::RBrace)) + break; + if (const auto inst = parseSPIRVAsmInst(parser)) + asmExpr->insts.add(*inst); + else + { + // Recover to the semi or brace + while ( + !(parser->LookAheadToken(TokenType::Semicolon) || + parser->LookAheadToken(TokenType::RBrace) || + parser->LookAheadToken(TokenType::EndOfFile))) + parser->ReadToken(); + } + if (parser->LookAheadToken(TokenType::RBrace)) + break; + parser->ReadToken(TokenType::Semicolon); } + parser->ReadMatchingToken(TokenType::RBrace); - static Expr* parsePrefixExpr(Parser* parser) + return asmExpr; +} + +static Expr* parseExpandExpr(Parser* parser, SourceLoc loc) +{ + ExpandExpr* expandExpr = parser->astBuilder->create<ExpandExpr>(); + expandExpr->loc = loc; + expandExpr->baseExpr = parser->ParseArgExpr(); + return expandExpr; +} + +static Expr* parseEachExpr(Parser* parser, SourceLoc loc) +{ + EachExpr* eachExpr = parser->astBuilder->create<EachExpr>(); + eachExpr->loc = loc; + eachExpr->baseExpr = parsePostfixExpr(parser); + return eachExpr; +} + +static Expr* parsePrefixExpr(Parser* parser) +{ + auto tokenType = peekTokenType(parser); + switch (tokenType) { - auto tokenType = peekTokenType(parser); - switch( tokenType ) - { - case TokenType::Identifier: + case TokenType::Identifier: { auto tokenLoc = peekToken(parser).getLoc(); if (AdvanceIf(parser, "new")) @@ -7779,15 +7720,15 @@ namespace Slang } return parsePostfixExpr(parser); } - default: + default: { return parsePostfixExpr(parser); } - case TokenType::OpNot: - case TokenType::OpInc: - case TokenType::OpDec: - case TokenType::OpMul: - case TokenType::OpBitAnd: + case TokenType::OpNot: + case TokenType::OpInc: + case TokenType::OpDec: + case TokenType::OpMul: + case TokenType::OpBitAnd: { PrefixExpr* prefixExpr = parser->astBuilder->create<PrefixExpr>(); parser->FillPosition(prefixExpr); @@ -7798,598 +7739,627 @@ namespace Slang prefixExpr->arguments.add(arg); return prefixExpr; } - case TokenType::OpBitNot: - case TokenType::OpAdd: - case TokenType::OpSub: - { - PrefixExpr* prefixExpr = parser->astBuilder->create<PrefixExpr>(); - parser->FillPosition(prefixExpr); - prefixExpr->functionExpr = parseOperator(parser); - - auto arg = parsePrefixExpr(parser); + case TokenType::OpBitNot: + case TokenType::OpAdd: + case TokenType::OpSub: + { + PrefixExpr* prefixExpr = parser->astBuilder->create<PrefixExpr>(); + parser->FillPosition(prefixExpr); + prefixExpr->functionExpr = parseOperator(parser); - if (auto intLit = as<IntegerLiteralExpr>(arg)) - { - IntegerLiteralExpr* newLiteral = parser->astBuilder->create<IntegerLiteralExpr>(*intLit); + auto arg = parsePrefixExpr(parser); - IRIntegerValue value = _foldIntegerPrefixOp(tokenType, newLiteral->value); + if (auto intLit = as<IntegerLiteralExpr>(arg)) + { + IntegerLiteralExpr* newLiteral = + parser->astBuilder->create<IntegerLiteralExpr>(*intLit); - // Need to get the basic type, so we can fit to underlying type - if (auto basicExprType = as<BasicExpressionType>(intLit->type.type)) - { - value = _fixIntegerLiteral(basicExprType->getBaseType(), value, nullptr, nullptr); - } + IRIntegerValue value = _foldIntegerPrefixOp(tokenType, newLiteral->value); - newLiteral->value = value; - return newLiteral; - } - else if (auto floatLit = as<FloatingPointLiteralExpr>(arg)) + // Need to get the basic type, so we can fit to underlying type + if (auto basicExprType = as<BasicExpressionType>(intLit->type.type)) { - FloatingPointLiteralExpr* newLiteral = parser->astBuilder->create<FloatingPointLiteralExpr>(*floatLit); - newLiteral->value = _foldFloatPrefixOp(tokenType, floatLit->value); - return newLiteral; + value = + _fixIntegerLiteral(basicExprType->getBaseType(), value, nullptr, nullptr); } - prefixExpr->arguments.add(arg); - return prefixExpr; + newLiteral->value = value; + return newLiteral; } - - break; + else if (auto floatLit = as<FloatingPointLiteralExpr>(arg)) + { + FloatingPointLiteralExpr* newLiteral = + parser->astBuilder->create<FloatingPointLiteralExpr>(*floatLit); + newLiteral->value = _foldFloatPrefixOp(tokenType, floatLit->value); + return newLiteral; + } + + prefixExpr->arguments.add(arg); + return prefixExpr; } - } - Expr* Parser::ParseLeafExpression() - { - return parsePrefixExpr(this); + break; } +} - /// Parse an argument to an application of a generic - static Expr* _parseGenericArg(Parser* parser) +Expr* Parser::ParseLeafExpression() +{ + return parsePrefixExpr(this); +} + +/// Parse an argument to an application of a generic +static Expr* _parseGenericArg(Parser* parser) +{ + // The grammar for generic arguments needs to be a super-set of the + // grammar for types and for expressions, because we do not know + // which to expect at each argument position during parsing. + // + // For the most part the expression grammar is more permissive than + // the type grammar, but types support modifiers that are not + // (currently) allowed in pure expression contexts. + // + // We could in theory allow modifiers to appear in expression contexts + // and deal with the cases where this should not be allowed downstream, + // but doing so runs a high risk of changing the meaning of existing code + // (notably in cases where a user might have used a variable name that + // overlaps with a language modifier keyword). + // + // Instead, we will simply detect the case where modifiers appear on + // a generic argument here, as a special case. + // + Modifiers modifiers = ParseModifiers(parser); + if (modifiers.first) { - // The grammar for generic arguments needs to be a super-set of the - // grammar for types and for expressions, because we do not know - // which to expect at each argument position during parsing. + // If there are any modifiers, then we know that we are actually + // in the type case. // - // For the most part the expression grammar is more permissive than - // the type grammar, but types support modifiers that are not - // (currently) allowed in pure expression contexts. - // - // We could in theory allow modifiers to appear in expression contexts - // and deal with the cases where this should not be allowed downstream, - // but doing so runs a high risk of changing the meaning of existing code - // (notably in cases where a user might have used a variable name that - // overlaps with a language modifier keyword). - // - // Instead, we will simply detect the case where modifiers appear on - // a generic argument here, as a special case. - // - Modifiers modifiers = ParseModifiers(parser); - if(modifiers.first) - { - // If there are any modifiers, then we know that we are actually - // in the type case. - // - auto typeSpec = _parseSimpleTypeSpec(parser); - typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers); - - auto typeExpr = typeSpec.expr; + auto typeSpec = _parseSimpleTypeSpec(parser); + typeSpec = _applyModifiersToTypeSpec(parser, typeSpec, modifiers); - typeExpr = parsePostfixTypeSuffix(parser, typeExpr); - typeExpr = _parseInfixTypeExprSuffix(parser, typeExpr); + auto typeExpr = typeSpec.expr; - return typeExpr; - } + typeExpr = parsePostfixTypeSuffix(parser, typeExpr); + typeExpr = _parseInfixTypeExprSuffix(parser, typeExpr); - return parser->ParseArgExpr(); + return typeExpr; } - Expr* parseTermFromSourceFile( - ASTBuilder* astBuilder, - TokenSpan const& tokens, - DiagnosticSink* sink, - Scope* outerScope, - NamePool* namePool, - SourceLanguage sourceLanguage) - { - ParserOptions options; - options.allowGLSLInput = sourceLanguage == SourceLanguage::GLSL; - Parser parser(astBuilder, tokens, sink, outerScope, options); - parser.currentScope = outerScope; - parser.namePool = namePool; - parser.sourceLanguage = sourceLanguage; - return parser.ParseExpression(); - } + return parser->ParseArgExpr(); +} - // Parse a source file into an existing translation unit - void parseSourceFile( - ASTBuilder* astBuilder, - TranslationUnitRequest* translationUnit, - SourceLanguage sourceLanguage, - TokenSpan const& tokens, - DiagnosticSink* sink, - Scope* outerScope, - ContainerDecl* parentDecl) - { - ParserOptions options = {}; - options.enableEffectAnnotations = translationUnit->compileRequest->optionSet.getBoolOption(CompilerOptionName::EnableEffectAnnotations); - options.allowGLSLInput = - translationUnit->compileRequest->optionSet.getBoolOption(CompilerOptionName::AllowGLSL) || - sourceLanguage == SourceLanguage::GLSL; - options.isInLanguageServer = translationUnit->compileRequest->getLinkage()->isInLanguageServer(); - options.optionSet = translationUnit->compileRequest->optionSet; +Expr* parseTermFromSourceFile( + ASTBuilder* astBuilder, + TokenSpan const& tokens, + DiagnosticSink* sink, + Scope* outerScope, + NamePool* namePool, + SourceLanguage sourceLanguage) +{ + ParserOptions options; + options.allowGLSLInput = sourceLanguage == SourceLanguage::GLSL; + Parser parser(astBuilder, tokens, sink, outerScope, options); + parser.currentScope = outerScope; + parser.namePool = namePool; + parser.sourceLanguage = sourceLanguage; + return parser.ParseExpression(); +} - Parser parser(astBuilder, tokens, sink, outerScope, options); - parser.namePool = translationUnit->getNamePool(); - parser.sourceLanguage = translationUnit->sourceLanguage; +// Parse a source file into an existing translation unit +void parseSourceFile( + ASTBuilder* astBuilder, + TranslationUnitRequest* translationUnit, + SourceLanguage sourceLanguage, + TokenSpan const& tokens, + DiagnosticSink* sink, + Scope* outerScope, + ContainerDecl* parentDecl) +{ + ParserOptions options = {}; + options.enableEffectAnnotations = translationUnit->compileRequest->optionSet.getBoolOption( + CompilerOptionName::EnableEffectAnnotations); + options.allowGLSLInput = + translationUnit->compileRequest->optionSet.getBoolOption(CompilerOptionName::AllowGLSL) || + sourceLanguage == SourceLanguage::GLSL; + options.isInLanguageServer = + translationUnit->compileRequest->getLinkage()->isInLanguageServer(); + options.optionSet = translationUnit->compileRequest->optionSet; + + Parser parser(astBuilder, tokens, sink, outerScope, options); + parser.namePool = translationUnit->getNamePool(); + parser.sourceLanguage = translationUnit->sourceLanguage; + + return parser.parseSourceFile(parentDecl); +} - return parser.parseSourceFile(parentDecl); - } +static void addBuiltinSyntaxImpl( + Session* session, + Scope* scope, + char const* nameText, + SyntaxParseCallback callback, + void* userData, + SyntaxClass<NodeBase> syntaxClass) +{ + Name* name = session->getNamePool()->getName(nameText); - static void addBuiltinSyntaxImpl( - Session* session, - Scope* scope, - char const* nameText, - SyntaxParseCallback callback, - void* userData, - SyntaxClass<NodeBase> syntaxClass) - { - Name* name = session->getNamePool()->getName(nameText); + ASTBuilder* globalASTBuilder = session->getGlobalASTBuilder(); - ASTBuilder* globalASTBuilder = session->getGlobalASTBuilder(); + SyntaxDecl* syntaxDecl = globalASTBuilder->create<SyntaxDecl>(); + syntaxDecl->nameAndLoc = NameLoc(name); + syntaxDecl->syntaxClass = syntaxClass; + syntaxDecl->parseCallback = callback; + syntaxDecl->parseUserData = userData; + addModifier(syntaxDecl, globalASTBuilder->create<PublicModifier>()); + AddMember(scope, syntaxDecl); +} - SyntaxDecl* syntaxDecl = globalASTBuilder->create<SyntaxDecl>(); - syntaxDecl->nameAndLoc = NameLoc(name); - syntaxDecl->syntaxClass = syntaxClass; - syntaxDecl->parseCallback = callback; - syntaxDecl->parseUserData = userData; - addModifier(syntaxDecl, globalASTBuilder->create<PublicModifier>()); - AddMember(scope, syntaxDecl); - } +template<typename T> +static void addBuiltinSyntax( + Session* session, + Scope* scope, + char const* name, + SyntaxParseCallback callback, + void* userData = nullptr) +{ + addBuiltinSyntaxImpl(session, scope, name, callback, userData, getClass<T>()); +} - template<typename T> - static void addBuiltinSyntax( - Session* session, - Scope* scope, - char const* name, - SyntaxParseCallback callback, - void* userData = nullptr) +template<typename T> +static void addSimpleModifierSyntax(Session* session, Scope* scope, char const* name) +{ + auto syntaxClass = getClass<T>(); + addBuiltinSyntaxImpl( + session, + scope, + name, + &parseSimpleSyntax, + (void*)syntaxClass.classInfo, + getClass<T>()); +} + +static IROp parseIROp(Parser* parser, Token& outToken) +{ + if (AdvanceIf(parser, TokenType::OpSub)) { - addBuiltinSyntaxImpl(session, scope, name, callback, userData, getClass<T>()); + outToken = parser->ReadToken(); + return IROp(-stringToInt(outToken.getContent())); } - - template<typename T> - static void addSimpleModifierSyntax( - Session* session, - Scope* scope, - char const* name) + else if (parser->LookAheadToken(TokenType::IntegerLiteral)) { - auto syntaxClass = getClass<T>(); - addBuiltinSyntaxImpl(session, scope, name, &parseSimpleSyntax, (void*) syntaxClass.classInfo, getClass<T>()); + outToken = parser->ReadToken(); + return IROp(stringToInt(outToken.getContent())); } - - static IROp parseIROp(Parser* parser, Token& outToken) + else { - if (AdvanceIf(parser, TokenType::OpSub)) - { - outToken = parser->ReadToken(); - return IROp(-stringToInt(outToken.getContent())); - } - else if (parser->LookAheadToken(TokenType::IntegerLiteral)) - { - outToken = parser->ReadToken(); - return IROp(stringToInt(outToken.getContent())); - } - else - { - outToken = parser->ReadToken(TokenType::Identifier);; - auto op = findIROp(outToken.getContent()); + outToken = parser->ReadToken(TokenType::Identifier); + ; + auto op = findIROp(outToken.getContent()); - if (op == kIROp_Invalid) - { - parser->sink->diagnose(outToken, Diagnostics::unimplemented, "unknown intrinsic op"); - } - return op; + if (op == kIROp_Invalid) + { + parser->sink->diagnose(outToken, Diagnostics::unimplemented, "unknown intrinsic op"); } + return op; } +} + +static NodeBase* parseIntrinsicOpModifier(Parser* parser, void* /*userData*/) +{ + IntrinsicOpModifier* modifier = parser->astBuilder->create<IntrinsicOpModifier>(); - static NodeBase* parseIntrinsicOpModifier(Parser* parser, void* /*userData*/) + // We allow a few difference forms here: + // + // First, we can specify the intrinsic op `enum` value directly: + // + // __intrinsic_op(<integer literal>) + // + // Second, we can specify the operation by name: + // + // __intrinsic_op(<identifier>) + // + // Finally, we can leave off the specification, so that the + // op name will be derived from the function name: + // + // __intrinsic_op + // + if (AdvanceIf(parser, TokenType::LParent)) { - IntrinsicOpModifier* modifier = parser->astBuilder->create<IntrinsicOpModifier>(); + modifier->op = parseIROp(parser, modifier->opToken); + parser->ReadToken(TokenType::RParent); + } - // We allow a few difference forms here: - // - // First, we can specify the intrinsic op `enum` value directly: - // - // __intrinsic_op(<integer literal>) - // - // Second, we can specify the operation by name: - // - // __intrinsic_op(<identifier>) - // - // Finally, we can leave off the specification, so that the - // op name will be derived from the function name: - // - // __intrinsic_op - // - if (AdvanceIf(parser, TokenType::LParent)) - { - modifier->op = parseIROp(parser, modifier->opToken); - parser->ReadToken(TokenType::RParent); - } + return modifier; +} - return modifier; - } +static NodeBase* parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/) +{ + auto modifier = parser->astBuilder->create<TargetIntrinsicModifier>(); + modifier->isString = false; - static NodeBase* parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/) + if (AdvanceIf(parser, TokenType::LParent)) { - auto modifier = parser->astBuilder->create<TargetIntrinsicModifier>(); - modifier->isString = false; + modifier->targetToken = parser->ReadToken(TokenType::Identifier); - if (AdvanceIf(parser, TokenType::LParent)) + if (AdvanceIf(parser, TokenType::Comma)) { - modifier->targetToken = parser->ReadToken(TokenType::Identifier); - - if( AdvanceIf(parser, TokenType::Comma) ) + if (parser->LookAheadToken(TokenType::LParent, 1)) { - if(parser->LookAheadToken(TokenType::LParent, 1)) - { - modifier->predicateToken = parser->ReadToken(TokenType::Identifier); - parser->ReadToken(); - modifier->scrutinee = NameLoc(parser->ReadToken(TokenType::Identifier)); - parser->ReadToken(TokenType::RParent); - parser->ReadToken(TokenType::Comma); - } - if( parser->LookAheadToken(TokenType::StringLiteral) ) - { - bool first = true; - do - { - const auto t = parser->ReadToken(); - first - ? void(first = false) - : modifier->definitionString.append(" "); - modifier->definitionString.append(getStringLiteralTokenValue(t)); - modifier->isString = true; - } - while(parser->LookAheadToken(TokenType::StringLiteral)); - } - else + modifier->predicateToken = parser->ReadToken(TokenType::Identifier); + parser->ReadToken(); + modifier->scrutinee = NameLoc(parser->ReadToken(TokenType::Identifier)); + parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::Comma); + } + if (parser->LookAheadToken(TokenType::StringLiteral)) + { + bool first = true; + do { - modifier->definitionIdent = parser->ReadToken(TokenType::Identifier); - } + const auto t = parser->ReadToken(); + first ? void(first = false) : modifier->definitionString.append(" "); + modifier->definitionString.append(getStringLiteralTokenValue(t)); + modifier->isString = true; + } while (parser->LookAheadToken(TokenType::StringLiteral)); + } + else + { + modifier->definitionIdent = parser->ReadToken(TokenType::Identifier); } - - parser->ReadToken(TokenType::RParent); } - return modifier; + parser->ReadToken(TokenType::RParent); } - static NodeBase* parseSpecializedForTargetModifier(Parser* parser, void* /*userData*/) + return modifier; +} + +static NodeBase* parseSpecializedForTargetModifier(Parser* parser, void* /*userData*/) +{ + auto modifier = parser->astBuilder->create<SpecializedForTargetModifier>(); + if (AdvanceIf(parser, TokenType::LParent)) { - auto modifier = parser->astBuilder->create<SpecializedForTargetModifier>(); - if (AdvanceIf(parser, TokenType::LParent)) - { - modifier->targetToken = parser->ReadToken(TokenType::Identifier); - parser->ReadToken(TokenType::RParent); - } - return modifier; + modifier->targetToken = parser->ReadToken(TokenType::Identifier); + parser->ReadToken(TokenType::RParent); } + return modifier; +} - static NodeBase* parseGLSLExtensionModifier(Parser* parser, void* /*userData*/) - { - auto modifier = parser->astBuilder->create<RequiredGLSLExtensionModifier>(); +static NodeBase* parseGLSLExtensionModifier(Parser* parser, void* /*userData*/) +{ + auto modifier = parser->astBuilder->create<RequiredGLSLExtensionModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->extensionNameToken = parser->ReadToken(TokenType::Identifier); - parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::LParent); + modifier->extensionNameToken = parser->ReadToken(TokenType::Identifier); + parser->ReadToken(TokenType::RParent); - return modifier; - } + return modifier; +} - static NodeBase* parseGLSLVersionModifier(Parser* parser, void* /*userData*/) - { - auto modifier = parser->astBuilder->create<RequiredGLSLVersionModifier>(); +static NodeBase* parseGLSLVersionModifier(Parser* parser, void* /*userData*/) +{ + auto modifier = parser->astBuilder->create<RequiredGLSLVersionModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->versionNumberToken = parser->ReadToken(TokenType::IntegerLiteral); - parser->ReadToken(TokenType::RParent); + parser->ReadToken(TokenType::LParent); + modifier->versionNumberToken = parser->ReadToken(TokenType::IntegerLiteral); + parser->ReadToken(TokenType::RParent); - return modifier; - } + return modifier; +} - static SlangResult parseSemanticVersion(Parser* parser, Token& outToken, SemanticVersion& outVersion) - { - parser->ReadToken(TokenType::LParent); - outToken = parser->ReadToken(); - parser->ReadToken(TokenType::RParent); +static SlangResult parseSemanticVersion( + Parser* parser, + Token& outToken, + SemanticVersion& outVersion) +{ + parser->ReadToken(TokenType::LParent); + outToken = parser->ReadToken(); + parser->ReadToken(TokenType::RParent); - UnownedStringSlice content = outToken.getContent(); - // We allow specified as major.minor or as a string (in quotes) - switch (outToken.type) + UnownedStringSlice content = outToken.getContent(); + // We allow specified as major.minor or as a string (in quotes) + switch (outToken.type) + { + case TokenType::FloatingPointLiteral: { - case TokenType::FloatingPointLiteral: - { - break; - } - case TokenType::StringLiteral: - { - // We need to trim quotes if needed - SLANG_ASSERT(content.getLength() >= 2 && content[0] == '"' && content[content.getLength() - 1] == '"'); - content = UnownedStringSlice(content.begin() + 1, content.end() - 1); - break; - } - default: - { - return SLANG_FAIL; - } + break; } - return SemanticVersion::parse(content, outVersion); - } - - static NodeBase* parseSPIRVVersionModifier(Parser* parser, void* /*userData*/) - { - Token token; - SemanticVersion version; - if (SLANG_SUCCEEDED(parseSemanticVersion(parser, token, version))) + case TokenType::StringLiteral: { - auto modifier = parser->astBuilder->create<RequiredSPIRVVersionModifier>(); - modifier->version = version; - return modifier; + // We need to trim quotes if needed + SLANG_ASSERT( + content.getLength() >= 2 && content[0] == '"' && + content[content.getLength() - 1] == '"'); + content = UnownedStringSlice(content.begin() + 1, content.end() - 1); + break; + } + default: + { + return SLANG_FAIL; } - parser->sink->diagnose(token, Diagnostics::invalidSPIRVVersion); - return nullptr; } + return SemanticVersion::parse(content, outVersion); +} - static NodeBase* parseCUDASMVersionModifier(Parser* parser, void* /*userData*/) +static NodeBase* parseSPIRVVersionModifier(Parser* parser, void* /*userData*/) +{ + Token token; + SemanticVersion version; + if (SLANG_SUCCEEDED(parseSemanticVersion(parser, token, version))) { - Token token; - SemanticVersion version; - if (SLANG_SUCCEEDED(parseSemanticVersion(parser, token, version))) - { - auto modifier = parser->astBuilder->create<RequiredCUDASMVersionModifier>(); - modifier->version = version; - return modifier; - } - parser->sink->diagnose(token, Diagnostics::invalidCUDASMVersion); - return nullptr; + auto modifier = parser->astBuilder->create<RequiredSPIRVVersionModifier>(); + modifier->version = version; + return modifier; } - static NodeBase* parseVolatileModifier(Parser* parser, void* /*userData*/) + parser->sink->diagnose(token, Diagnostics::invalidSPIRVVersion); + return nullptr; +} + +static NodeBase* parseCUDASMVersionModifier(Parser* parser, void* /*userData*/) +{ + Token token; + SemanticVersion version; + if (SLANG_SUCCEEDED(parseSemanticVersion(parser, token, version))) { - ModifierListBuilder listBuilder; + auto modifier = parser->astBuilder->create<RequiredCUDASMVersionModifier>(); + modifier->version = version; + return modifier; + } + parser->sink->diagnose(token, Diagnostics::invalidCUDASMVersion); + return nullptr; +} +static NodeBase* parseVolatileModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; - auto hlslMod = parser->astBuilder->create<HLSLVolatileModifier>(); - hlslMod->keywordName = getName(parser, "volatile"); - hlslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(hlslMod); + auto hlslMod = parser->astBuilder->create<HLSLVolatileModifier>(); + hlslMod->keywordName = getName(parser, "volatile"); + hlslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(hlslMod); - auto glslMod = parser->astBuilder->create<GLSLVolatileModifier>(); - glslMod->keywordName = getName(parser, "volatile"); - glslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(glslMod); + auto glslMod = parser->astBuilder->create<GLSLVolatileModifier>(); + glslMod->keywordName = getName(parser, "volatile"); + glslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(glslMod); - return listBuilder.getFirst(); - } + return listBuilder.getFirst(); +} - static NodeBase* parseCoherentModifier(Parser* parser, void* /*userData*/) - { - ModifierListBuilder listBuilder; +static NodeBase* parseCoherentModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; - auto glslMod = parser->astBuilder->create<GloballyCoherentModifier>(); - glslMod->keywordName = getName(parser, "coherent"); - glslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(glslMod); + auto glslMod = parser->astBuilder->create<GloballyCoherentModifier>(); + glslMod->keywordName = getName(parser, "coherent"); + glslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(glslMod); - return listBuilder.getFirst(); - } + return listBuilder.getFirst(); +} - static NodeBase* parseRestrictModifier(Parser* parser, void* /*userData*/) - { - ModifierListBuilder listBuilder; +static NodeBase* parseRestrictModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; - auto glslMod = parser->astBuilder->create<GLSLRestrictModifier>(); - glslMod->keywordName = getName(parser, "restrict"); - glslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(glslMod); + auto glslMod = parser->astBuilder->create<GLSLRestrictModifier>(); + glslMod->keywordName = getName(parser, "restrict"); + glslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(glslMod); - return listBuilder.getFirst(); - } + return listBuilder.getFirst(); +} - static NodeBase* parseReadonlyModifier(Parser* parser, void* /*userData*/) - { - ModifierListBuilder listBuilder; +static NodeBase* parseReadonlyModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; - auto glslMod = parser->astBuilder->create<GLSLReadOnlyModifier>(); - glslMod->keywordName = getName(parser, "readonly"); - glslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(glslMod); + auto glslMod = parser->astBuilder->create<GLSLReadOnlyModifier>(); + glslMod->keywordName = getName(parser, "readonly"); + glslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(glslMod); - return listBuilder.getFirst(); - } + return listBuilder.getFirst(); +} - static NodeBase* parseWriteonlyModifier(Parser* parser, void* /*userData*/) - { - ModifierListBuilder listBuilder; +static NodeBase* parseWriteonlyModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; - auto glslMod = parser->astBuilder->create<GLSLWriteOnlyModifier>(); - glslMod->keywordName = getName(parser, "writeonly"); - glslMod->loc = parser->tokenReader.peekLoc(); - listBuilder.add(glslMod); + auto glslMod = parser->astBuilder->create<GLSLWriteOnlyModifier>(); + glslMod->keywordName = getName(parser, "writeonly"); + glslMod->loc = parser->tokenReader.peekLoc(); + listBuilder.add(glslMod); - return listBuilder.getFirst(); - } - - static NodeBase* parseLayoutModifier(Parser* parser, void* /*userData*/) + return listBuilder.getFirst(); +} + +static NodeBase* parseLayoutModifier(Parser* parser, void* /*userData*/) +{ + ModifierListBuilder listBuilder; + + GLSLLayoutLocalSizeAttribute* numThreadsAttrib = nullptr; + GLSLLayoutDerivativeGroupQuadAttribute* derivativeGroupQuadAttrib = nullptr; + GLSLLayoutDerivativeGroupLinearAttribute* derivativeGroupLinearAttrib = nullptr; + GLSLInputAttachmentIndexLayoutAttribute* inputAttachmentIndexLayoutAttribute = nullptr; + ImageFormat format; + + listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupBegin>()); + + parser->ReadToken(TokenType::LParent); + while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) { - ModifierListBuilder listBuilder; + auto nameAndLoc = expectIdentifier(parser); + const String& nameText = nameAndLoc.name->text; - GLSLLayoutLocalSizeAttribute* numThreadsAttrib = nullptr; - GLSLLayoutDerivativeGroupQuadAttribute* derivativeGroupQuadAttrib = nullptr; - GLSLLayoutDerivativeGroupLinearAttribute* derivativeGroupLinearAttrib = nullptr; - GLSLInputAttachmentIndexLayoutAttribute* inputAttachmentIndexLayoutAttribute = nullptr; - ImageFormat format; + const char localSizePrefix[] = "local_size_"; - listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupBegin>()); - - parser->ReadToken(TokenType::LParent); - while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses)) + int localSizeIndex = -1; + if (nameText.startsWith(localSizePrefix) && + nameText.getLength() == SLANG_COUNT_OF(localSizePrefix) - 1 + 1) { - auto nameAndLoc = expectIdentifier(parser); - const String& nameText = nameAndLoc.name->text; - - const char localSizePrefix[] = "local_size_"; + char lastChar = nameText[SLANG_COUNT_OF(localSizePrefix) - 1]; + localSizeIndex = (lastChar >= 'x' && lastChar <= 'z') ? (lastChar - 'x') : -1; + } - int localSizeIndex = -1; - if (nameText.startsWith(localSizePrefix) && nameText.getLength() == SLANG_COUNT_OF(localSizePrefix) - 1 + 1) + if (localSizeIndex >= 0) + { + if (!numThreadsAttrib) { - char lastChar = nameText[SLANG_COUNT_OF(localSizePrefix) - 1]; - localSizeIndex = (lastChar >= 'x' && lastChar <= 'z') ? (lastChar - 'x') : -1; + numThreadsAttrib = parser->astBuilder->create<GLSLLayoutLocalSizeAttribute>(); + numThreadsAttrib->args.setCount(3); + for (auto& i : numThreadsAttrib->args) + i = nullptr; + + // Just mark the loc and name from the first in the list + numThreadsAttrib->keywordName = getName(parser, "numthreads"); + numThreadsAttrib->loc = nameAndLoc.loc; } - if (localSizeIndex >= 0) + if (AdvanceIf(parser, TokenType::OpAssign)) { - if (!numThreadsAttrib) + auto expr = parseAtomicExpr(parser); + // SLANG_ASSERT(expr); + if (!expr) { - numThreadsAttrib = parser->astBuilder->create<GLSLLayoutLocalSizeAttribute>(); - numThreadsAttrib->args.setCount(3); - for (auto& i : numThreadsAttrib->args) - i = nullptr; - - // Just mark the loc and name from the first in the list - numThreadsAttrib->keywordName = getName(parser, "numthreads"); - numThreadsAttrib->loc = nameAndLoc.loc; + return nullptr; } - if (AdvanceIf(parser, TokenType::OpAssign)) - { - auto expr = parseAtomicExpr(parser); - //SLANG_ASSERT(expr); - if (!expr) - { - return nullptr; - } - - numThreadsAttrib->args[localSizeIndex] = expr; - } + numThreadsAttrib->args[localSizeIndex] = expr; } - else if (nameText == "derivative_group_quadsNV") + } + else if (nameText == "derivative_group_quadsNV") + { + derivativeGroupQuadAttrib = + parser->astBuilder->create<GLSLLayoutDerivativeGroupQuadAttribute>(); + } + else if (nameText == "derivative_group_linearNV") + { + derivativeGroupLinearAttrib = + parser->astBuilder->create<GLSLLayoutDerivativeGroupLinearAttribute>(); + } + else if (nameText == "input_attachment_index") + { + inputAttachmentIndexLayoutAttribute = + parser->astBuilder->create<GLSLInputAttachmentIndexLayoutAttribute>(); + if (AdvanceIf(parser, TokenType::OpAssign)) { - derivativeGroupQuadAttrib = parser->astBuilder->create<GLSLLayoutDerivativeGroupQuadAttribute>(); + auto token = parser->ReadToken(TokenType::IntegerLiteral); + auto intVal = getIntegerLiteralValue(token); + inputAttachmentIndexLayoutAttribute->location = intVal; } - else if (nameText == "derivative_group_linearNV") + } + else if (nameText == "binding" || nameText == "set") + { + GLSLBindingAttribute* attr = listBuilder.find<GLSLBindingAttribute>(); + if (!attr) { - derivativeGroupLinearAttrib = parser->astBuilder->create<GLSLLayoutDerivativeGroupLinearAttribute>(); + attr = parser->astBuilder->create<GLSLBindingAttribute>(); + listBuilder.add(attr); } - else if (nameText == "input_attachment_index") - { - inputAttachmentIndexLayoutAttribute = parser->astBuilder->create<GLSLInputAttachmentIndexLayoutAttribute>(); - if (AdvanceIf(parser, TokenType::OpAssign)) - { - auto token = parser->ReadToken(TokenType::IntegerLiteral); - auto intVal = getIntegerLiteralValue(token); - inputAttachmentIndexLayoutAttribute->location = intVal; - } - } - else if (nameText == "binding" || - nameText == "set") - { - GLSLBindingAttribute* attr = listBuilder.find<GLSLBindingAttribute>(); - if (!attr) - { - attr = parser->astBuilder->create<GLSLBindingAttribute>(); - listBuilder.add(attr); - } + parser->ReadToken(TokenType::OpAssign); - parser->ReadToken(TokenType::OpAssign); + // If the token asked for is not returned found will put in recovering state, and return + // token found + Token valToken = parser->ReadToken(TokenType::IntegerLiteral); + // If wasn't the desired IntegerLiteral return that couldn't parse + if (valToken.type == TokenType::IntegerLiteral) + { + // Work out the value + auto value = getIntegerLiteralValue(valToken); - // If the token asked for is not returned found will put in recovering state, and return token found - Token valToken = parser->ReadToken(TokenType::IntegerLiteral); - // If wasn't the desired IntegerLiteral return that couldn't parse - if (valToken.type == TokenType::IntegerLiteral) + if (nameText == "binding") { - // Work out the value - auto value = getIntegerLiteralValue(valToken); - - if (nameText == "binding") - { - attr->binding = int32_t(value); - } - else - { - attr->set = int32_t(value); - } + attr->binding = int32_t(value); } - } - else if(findImageFormatByName(nameText.getUnownedSlice(), &format)) - { - auto attr = parser->astBuilder->create<FormatAttribute>(); - attr->format = format; - listBuilder.add(attr); - } - else - { - Modifier* modifier = nullptr; - -#define CASE(key, type) if (nameText == #key) { modifier = parser->astBuilder->create<type>(); } else - CASE(push_constant, PushConstantAttribute) - CASE(shaderRecordNV, ShaderRecordAttribute) - CASE(shaderRecordEXT, ShaderRecordAttribute) - CASE(constant_id, VkConstantIdAttribute) - CASE(std140, GLSLStd140Modifier) - CASE(std430, GLSLStd430Modifier) - CASE(scalar, GLSLScalarModifier) - CASE(offset, GLSLOffsetLayoutAttribute) - CASE(location, GLSLLocationLayoutModifier) + else { - modifier = parser->astBuilder->create<GLSLUnparsedLayoutModifier>(); + attr->set = int32_t(value); } - SLANG_ASSERT(modifier); + } + } + else if (findImageFormatByName(nameText.getUnownedSlice(), &format)) + { + auto attr = parser->astBuilder->create<FormatAttribute>(); + attr->format = format; + listBuilder.add(attr); + } + else + { + Modifier* modifier = nullptr; + +#define CASE(key, type) \ + if (nameText == #key) \ + { \ + modifier = parser->astBuilder->create<type>(); \ + } \ + else + CASE(push_constant, PushConstantAttribute) + CASE(shaderRecordNV, ShaderRecordAttribute) + CASE(shaderRecordEXT, ShaderRecordAttribute) + CASE(constant_id, VkConstantIdAttribute) + CASE(std140, GLSLStd140Modifier) + CASE(std430, GLSLStd430Modifier) + CASE(scalar, GLSLScalarModifier) + CASE(offset, GLSLOffsetLayoutAttribute) + CASE(location, GLSLLocationLayoutModifier) + { + modifier = parser->astBuilder->create<GLSLUnparsedLayoutModifier>(); + } + SLANG_ASSERT(modifier); #undef CASE - modifier->keywordName = nameAndLoc.name; - modifier->loc = nameAndLoc.loc; + modifier->keywordName = nameAndLoc.name; + modifier->loc = nameAndLoc.loc; - // Special handling for GLSLLayoutModifier - if (auto glslModifier = as<GLSLLayoutModifier>(modifier)) - { - // not all GLSLLayoutModifier subtypes have an OpAssign after - if (AdvanceIf(parser, TokenType::OpAssign)) - glslModifier->valToken = parser->ReadToken(TokenType::IntegerLiteral); - } - //Special handling for GLSLOffsetLayoutAttribute to add to the byte offset tracker at a binding location - else if (auto glslOffset = as<GLSLOffsetLayoutAttribute>(modifier)) + // Special handling for GLSLLayoutModifier + if (auto glslModifier = as<GLSLLayoutModifier>(modifier)) + { + // not all GLSLLayoutModifier subtypes have an OpAssign after + if (AdvanceIf(parser, TokenType::OpAssign)) + glslModifier->valToken = parser->ReadToken(TokenType::IntegerLiteral); + } + // Special handling for GLSLOffsetLayoutAttribute to add to the byte offset tracker at a + // binding location + else if (auto glslOffset = as<GLSLOffsetLayoutAttribute>(modifier)) + { + if (auto binding = listBuilder.find<GLSLBindingAttribute>()) { - if (auto binding = listBuilder.find<GLSLBindingAttribute>()) - { - // all GLSLOffsetLayoutAttribute have an OpAssign with value token - parser->ReadToken(TokenType::OpAssign); - glslOffset->offset = int64_t(getIntegerLiteralValue(parser->ReadToken(TokenType::IntegerLiteral))); - parser->setBindingOffset(binding->binding, glslOffset->offset); - } - else - { - parser->diagnose(modifier->loc, Diagnostics::missingLayoutBindingModifier); - } + // all GLSLOffsetLayoutAttribute have an OpAssign with value token + parser->ReadToken(TokenType::OpAssign); + glslOffset->offset = int64_t( + getIntegerLiteralValue(parser->ReadToken(TokenType::IntegerLiteral))); + parser->setBindingOffset(binding->binding, glslOffset->offset); } - else if (auto specConstAttr = as<VkConstantIdAttribute>(modifier)) + else { - parser->ReadToken(TokenType::OpAssign); - specConstAttr->location = (int)getIntegerLiteralValue(parser->ReadToken(TokenType::IntegerLiteral)); + parser->diagnose(modifier->loc, Diagnostics::missingLayoutBindingModifier); } - - listBuilder.add(modifier); + } + else if (auto specConstAttr = as<VkConstantIdAttribute>(modifier)) + { + parser->ReadToken(TokenType::OpAssign); + specConstAttr->location = + (int)getIntegerLiteralValue(parser->ReadToken(TokenType::IntegerLiteral)); } - if (AdvanceIf(parser, TokenType::RParent)) - break; - parser->ReadToken(TokenType::Comma); + listBuilder.add(modifier); } -#define CASE(key, type) if (AdvanceIf(parser, #key)) { auto modifier = parser->astBuilder->create<type>(); \ - modifier->location = int(getIntegerLiteralValue(listBuilder.find<GLSLLayoutModifier>()->valToken)); listBuilder.add(modifier); } else + if (AdvanceIf(parser, TokenType::RParent)) + break; + parser->ReadToken(TokenType::Comma); + } + +#define CASE(key, type) \ + if (AdvanceIf(parser, #key)) \ + { \ + auto modifier = parser->astBuilder->create<type>(); \ + modifier->location = \ + int(getIntegerLiteralValue(listBuilder.find<GLSLLayoutModifier>()->valToken)); \ + listBuilder.add(modifier); \ + } \ + else CASE(rayPayloadEXT, VulkanRayPayloadAttribute) CASE(rayPayloadNV, VulkanRayPayloadAttribute) @@ -8397,322 +8367,333 @@ namespace Slang CASE(rayPayloadInNV, VulkanRayPayloadInAttribute) CASE(hitObjectAttributeNV, VulkanHitObjectAttributesAttribute) CASE(callableDataEXT, VulkanCallablePayloadAttribute) - CASE(callableDataInEXT, VulkanCallablePayloadInAttribute) - {} - + CASE(callableDataInEXT, VulkanCallablePayloadInAttribute) {} + #undef CASE - if (numThreadsAttrib) - listBuilder.add(numThreadsAttrib); - if(derivativeGroupQuadAttrib) - listBuilder.add(derivativeGroupQuadAttrib); - if(derivativeGroupLinearAttrib) - listBuilder.add(derivativeGroupLinearAttrib); - if(inputAttachmentIndexLayoutAttribute) - listBuilder.add(inputAttachmentIndexLayoutAttribute); + if (numThreadsAttrib) + listBuilder.add(numThreadsAttrib); + if (derivativeGroupQuadAttrib) + listBuilder.add(derivativeGroupQuadAttrib); + if (derivativeGroupLinearAttrib) + listBuilder.add(derivativeGroupLinearAttrib); + if (inputAttachmentIndexLayoutAttribute) + listBuilder.add(inputAttachmentIndexLayoutAttribute); - listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupEnd>()); + listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupEnd>()); - return listBuilder.getFirst(); - } + return listBuilder.getFirst(); +} - static NodeBase* parseHitAttributeEXTModifier(Parser* parser, void* /*userData*/) +static NodeBase* parseHitAttributeEXTModifier(Parser* parser, void* /*userData*/) +{ + VulkanHitAttributesAttribute* modifier = + parser->astBuilder->create<VulkanHitAttributesAttribute>(); + return modifier; +} + +static NodeBase* parseBuiltinTypeModifier(Parser* parser, void* /*userData*/) +{ + BuiltinTypeModifier* modifier = parser->astBuilder->create<BuiltinTypeModifier>(); + parser->ReadToken(TokenType::LParent); + modifier->tag = + BaseType(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); + parser->ReadToken(TokenType::RParent); + + return modifier; +} + +static NodeBase* parseBuiltinRequirementModifier(Parser* parser, void* /*userData*/) +{ + BuiltinRequirementModifier* modifier = parser->astBuilder->create<BuiltinRequirementModifier>(); + parser->ReadToken(TokenType::LParent); + modifier->kind = BuiltinRequirementKind( + stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); + parser->ReadToken(TokenType::RParent); + + return modifier; +} + +static NodeBase* parseMagicTypeModifier(Parser* parser, void* /*userData*/) +{ + MagicTypeModifier* modifier = parser->astBuilder->create<MagicTypeModifier>(); + parser->ReadToken(TokenType::LParent); + modifier->magicName = parser->ReadToken(TokenType::Identifier).getContent(); + if (AdvanceIf(parser, TokenType::Comma)) { - VulkanHitAttributesAttribute* modifier = parser->astBuilder->create<VulkanHitAttributesAttribute>(); - return modifier; + modifier->tag = + uint32_t(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); } - - static NodeBase* parseBuiltinTypeModifier(Parser* parser, void* /*userData*/) + auto classInfo = parser->astBuilder->findClassInfo(getName(parser, modifier->magicName)); + if (classInfo) { - BuiltinTypeModifier* modifier = parser->astBuilder->create<BuiltinTypeModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->tag = BaseType(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); - parser->ReadToken(TokenType::RParent); - - return modifier; + modifier->magicNodeType = ASTNodeType(classInfo->m_classId); } + // TODO: print diagnostic if the magic type name doesn't correspond to an actual ASTNodeType. + parser->ReadToken(TokenType::RParent); - static NodeBase* parseBuiltinRequirementModifier(Parser* parser, void* /*userData*/) - { - BuiltinRequirementModifier* modifier = parser->astBuilder->create<BuiltinRequirementModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->kind = BuiltinRequirementKind(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); - parser->ReadToken(TokenType::RParent); + return modifier; +} - return modifier; +static NodeBase* parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/) +{ + IntrinsicTypeModifier* modifier = parser->astBuilder->create<IntrinsicTypeModifier>(); + parser->ReadToken(TokenType::LParent); + modifier->irOp = parseIROp(parser, modifier->opToken); + while (AdvanceIf(parser, TokenType::Comma)) + { + auto operand = + uint32_t(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); + modifier->irOperands.add(operand); } + parser->ReadToken(TokenType::RParent); - static NodeBase* parseMagicTypeModifier(Parser* parser, void* /*userData*/) + return modifier; +} +static NodeBase* parseImplicitConversionModifier(Parser* parser, void* /*userData*/) +{ + ImplicitConversionModifier* modifier = parser->astBuilder->create<ImplicitConversionModifier>(); + BuiltinConversionKind builtinKind = kBuiltinConversion_Unknown; + ConversionCost cost = kConversionCost_Default; + if (AdvanceIf(parser, TokenType::LParent)) { - MagicTypeModifier* modifier = parser->astBuilder->create<MagicTypeModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->magicName = parser->ReadToken(TokenType::Identifier).getContent(); + cost = + ConversionCost(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); if (AdvanceIf(parser, TokenType::Comma)) { - modifier->tag = uint32_t(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); + builtinKind = BuiltinConversionKind( + stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); } - auto classInfo = parser->astBuilder->findClassInfo(getName(parser, modifier->magicName)); - if (classInfo) - { - modifier->magicNodeType = ASTNodeType(classInfo->m_classId); - } - // TODO: print diagnostic if the magic type name doesn't correspond to an actual ASTNodeType. parser->ReadToken(TokenType::RParent); - - return modifier; } + modifier->cost = cost; + modifier->builtinConversionKind = builtinKind; + return modifier; +} - static NodeBase* parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/) - { - IntrinsicTypeModifier* modifier = parser->astBuilder->create<IntrinsicTypeModifier>(); - parser->ReadToken(TokenType::LParent); - modifier->irOp = parseIROp(parser, modifier->opToken); - while( AdvanceIf(parser, TokenType::Comma) ) - { - auto operand = uint32_t(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); - modifier->irOperands.add(operand); - } - parser->ReadToken(TokenType::RParent); +static NodeBase* parseAttributeTargetModifier(Parser* parser, void* /*userData*/) +{ + expect(parser, TokenType::LParent); + auto syntaxClassNameAndLoc = expectIdentifier(parser); + expect(parser, TokenType::RParent); - return modifier; - } - static NodeBase* parseImplicitConversionModifier(Parser* parser, void* /*userData*/) - { - ImplicitConversionModifier* modifier = parser->astBuilder->create<ImplicitConversionModifier>(); - BuiltinConversionKind builtinKind = kBuiltinConversion_Unknown; - ConversionCost cost = kConversionCost_Default; - if( AdvanceIf(parser, TokenType::LParent) ) - { - cost = ConversionCost(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); - if (AdvanceIf(parser, TokenType::Comma)) - { - builtinKind = BuiltinConversionKind(stringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent())); - } - parser->ReadToken(TokenType::RParent); - } - modifier->cost = cost; - modifier->builtinConversionKind = builtinKind; - return modifier; - } + auto syntaxClass = parser->astBuilder->findSyntaxClass(syntaxClassNameAndLoc.name); - static NodeBase* parseAttributeTargetModifier(Parser* parser, void* /*userData*/) - { - expect(parser, TokenType::LParent); - auto syntaxClassNameAndLoc = expectIdentifier(parser); - expect(parser, TokenType::RParent); + AttributeTargetModifier* modifier = parser->astBuilder->create<AttributeTargetModifier>(); + modifier->syntaxClass = syntaxClass; - auto syntaxClass = parser->astBuilder->findSyntaxClass(syntaxClassNameAndLoc.name); + return modifier; +} - AttributeTargetModifier* modifier = parser->astBuilder->create<AttributeTargetModifier>(); - modifier->syntaxClass = syntaxClass; +static SyntaxParseInfo _makeParseExpr(const char* keywordName, SyntaxParseCallback callback) +{ + SyntaxParseInfo entry; + entry.classInfo = &Expr::kReflectClassInfo; + entry.keywordName = keywordName; + entry.callback = callback; + return entry; +} +static SyntaxParseInfo _makeParseDecl(const char* keywordName, SyntaxParseCallback callback) +{ + SyntaxParseInfo entry; + entry.keywordName = keywordName; + entry.callback = callback; + entry.classInfo = &Decl::kReflectClassInfo; + return entry; +} +static SyntaxParseInfo _makeParseModifier( + const char* keywordName, + const ReflectClassInfo& classInfo) +{ + // If we just have class info - use simple parser + SyntaxParseInfo entry; + entry.keywordName = keywordName; + entry.callback = &parseSimpleSyntax; + entry.classInfo = &classInfo; + return entry; +} +static SyntaxParseInfo _makeParseModifier(const char* keywordName, SyntaxParseCallback callback) +{ + SyntaxParseInfo entry; + entry.keywordName = keywordName; + entry.callback = callback; + entry.classInfo = &Modifier::kReflectClassInfo; + return entry; +} - return modifier; - } +// Maps a keyword to the associated parsing function +static const SyntaxParseInfo g_parseSyntaxEntries[] = { + // !!!!!!!!!!!!!!!!!!!! Decls !!!!!!!!!!!!!!!!!! + + _makeParseDecl("typedef", parseTypeDef), + _makeParseDecl("associatedtype", parseAssocType), + _makeParseDecl("type_param", parseGlobalGenericTypeParamDecl), + _makeParseDecl("cbuffer", parseHLSLCBufferDecl), + _makeParseDecl("tbuffer", parseHLSLTBufferDecl), + _makeParseDecl("__generic", parseGenericDecl), + _makeParseDecl("__extension", parseExtensionDecl), + _makeParseDecl("extension", parseExtensionDecl), + _makeParseDecl("__init", parseConstructorDecl), + _makeParseDecl("__subscript", parseSubscriptDecl), + _makeParseDecl("property", parsePropertyDecl), + _makeParseDecl("interface", parseInterfaceDecl), + _makeParseDecl("syntax", parseSyntaxDecl), + _makeParseDecl("attribute_syntax", parseAttributeSyntaxDecl), + _makeParseDecl("__import", parseImportDecl), + _makeParseDecl("import", parseImportDecl), + _makeParseDecl("__include", parseIncludeDecl), + _makeParseDecl("module", parseModuleDeclarationDecl), + _makeParseDecl("implementing", parseImplementingDecl), + _makeParseDecl("let", parseLetDecl), + _makeParseDecl("var", parseVarDecl), + _makeParseDecl("func", parseFuncDecl), + _makeParseDecl("typealias", parseTypeAliasDecl), + _makeParseDecl("__generic_value_param", parseGlobalGenericValueParamDecl), + _makeParseDecl("namespace", parseNamespaceDecl), + _makeParseDecl("using", parseUsingDecl), + _makeParseDecl("__ignored_block", parseIgnoredBlockDecl), + _makeParseDecl("__transparent_block", parseTransparentBlockDecl), + _makeParseDecl("__file_decl", parseFileDecl), + _makeParseDecl("__require_capability", parseRequireCapabilityDecl), + + // !!!!!!!!!!!!!!!!!!!!!! Modifer !!!!!!!!!!!!!!!!!!!!!! + + // Add syntax for "simple" modifier keywords. + // These are the ones that just appear as a single + // keyword (no further tokens expected/allowed), + // and which can be represented just by creating + // a new AST node of the corresponding type. + + _makeParseModifier("in", InModifier::kReflectClassInfo), + _makeParseModifier("out", OutModifier::kReflectClassInfo), + _makeParseModifier("inout", InOutModifier::kReflectClassInfo), + _makeParseModifier("__ref", RefModifier::kReflectClassInfo), + _makeParseModifier("__constref", ConstRefModifier::kReflectClassInfo), + _makeParseModifier("const", ConstModifier::kReflectClassInfo), + _makeParseModifier("__builtin", BuiltinModifier::kReflectClassInfo), + _makeParseModifier("highp", GLSLPrecisionModifier::kReflectClassInfo), + _makeParseModifier("lowp", GLSLPrecisionModifier::kReflectClassInfo), + _makeParseModifier("mediump", GLSLPrecisionModifier::kReflectClassInfo), + + _makeParseModifier("__global", ActualGlobalModifier::kReflectClassInfo), + + _makeParseModifier("inline", InlineModifier::kReflectClassInfo), + _makeParseModifier("public", PublicModifier::kReflectClassInfo), + _makeParseModifier("private", PrivateModifier::kReflectClassInfo), + _makeParseModifier("internal", InternalModifier::kReflectClassInfo), + + _makeParseModifier("require", RequireModifier::kReflectClassInfo), + _makeParseModifier("param", ParamModifier::kReflectClassInfo), + _makeParseModifier("extern", ExternModifier::kReflectClassInfo), + + _makeParseModifier("row_major", HLSLRowMajorLayoutModifier::kReflectClassInfo), + _makeParseModifier("column_major", HLSLColumnMajorLayoutModifier::kReflectClassInfo), + + _makeParseModifier("nointerpolation", HLSLNoInterpolationModifier::kReflectClassInfo), + _makeParseModifier("noperspective", HLSLNoPerspectiveModifier::kReflectClassInfo), + _makeParseModifier("linear", HLSLLinearModifier::kReflectClassInfo), + _makeParseModifier("sample", HLSLSampleModifier::kReflectClassInfo), + _makeParseModifier("centroid", HLSLCentroidModifier::kReflectClassInfo), + _makeParseModifier("precise", PreciseModifier::kReflectClassInfo), + _makeParseModifier("shared", HLSLEffectSharedModifier::kReflectClassInfo), + _makeParseModifier("groupshared", HLSLGroupSharedModifier::kReflectClassInfo), + _makeParseModifier("static", HLSLStaticModifier::kReflectClassInfo), + _makeParseModifier("uniform", HLSLUniformModifier::kReflectClassInfo), + _makeParseModifier("volatile", parseVolatileModifier), + _makeParseModifier("coherent", parseCoherentModifier), + _makeParseModifier("restrict", parseRestrictModifier), + _makeParseModifier("readonly", parseReadonlyModifier), + _makeParseModifier("writeonly", parseWriteonlyModifier), + _makeParseModifier("export", HLSLExportModifier::kReflectClassInfo), + _makeParseModifier("dynamic_uniform", DynamicUniformModifier::kReflectClassInfo), + + // Modifiers for geometry shader input + _makeParseModifier("point", HLSLPointModifier::kReflectClassInfo), + _makeParseModifier("line", HLSLLineModifier::kReflectClassInfo), + _makeParseModifier("triangle", HLSLTriangleModifier::kReflectClassInfo), + _makeParseModifier("lineadj", HLSLLineAdjModifier::kReflectClassInfo), + _makeParseModifier("triangleadj", HLSLTriangleAdjModifier::kReflectClassInfo), + + // Modifiers for mesh shader parameters + _makeParseModifier("vertices", HLSLVerticesModifier::kReflectClassInfo), + _makeParseModifier("indices", HLSLIndicesModifier::kReflectClassInfo), + _makeParseModifier("primitives", HLSLPrimitivesModifier::kReflectClassInfo), + _makeParseModifier("payload", HLSLPayloadModifier::kReflectClassInfo), + + // Modifiers for unary operator declarations + _makeParseModifier("__prefix", PrefixModifier::kReflectClassInfo), + _makeParseModifier("__postfix", PostfixModifier::kReflectClassInfo), + + // Modifier to apply to `import` that should be re-exported + _makeParseModifier("__exported", ExportedModifier::kReflectClassInfo), + + // Add syntax for more complex modifiers, which allow + // or expect more tokens after the initial keyword. + + _makeParseModifier("layout", parseLayoutModifier), + _makeParseModifier("hitAttributeEXT", parseHitAttributeEXTModifier), + _makeParseModifier("__intrinsic_op", parseIntrinsicOpModifier), + _makeParseModifier("__target_intrinsic", parseTargetIntrinsicModifier), + _makeParseModifier("__specialized_for_target", parseSpecializedForTargetModifier), + _makeParseModifier("__glsl_extension", parseGLSLExtensionModifier), + _makeParseModifier("__glsl_version", parseGLSLVersionModifier), + _makeParseModifier("__spirv_version", parseSPIRVVersionModifier), + _makeParseModifier("__cuda_sm_version", parseCUDASMVersionModifier), + + _makeParseModifier("__builtin_type", parseBuiltinTypeModifier), + _makeParseModifier("__builtin_requirement", parseBuiltinRequirementModifier), + + _makeParseModifier("__magic_type", parseMagicTypeModifier), + _makeParseModifier("__intrinsic_type", parseIntrinsicTypeModifier), + _makeParseModifier("__implicit_conversion", parseImplicitConversionModifier), + + _makeParseModifier("__attributeTarget", parseAttributeTargetModifier), + + // !!!!!!!!!!!!!!!!!!!!!!! Expr !!!!!!!!!!!!!!!!!!!!!!!!!!! + + _makeParseExpr("this", parseThisExpr), + _makeParseExpr("true", parseTrueExpr), + _makeParseExpr("false", parseFalseExpr), + _makeParseExpr("__return_val", parseReturnValExpr), + _makeParseExpr("nullptr", parseNullPtrExpr), + _makeParseExpr("none", parseNoneExpr), + _makeParseExpr("try", parseTryExpr), + _makeParseExpr("no_diff", parseTreatAsDifferentiableExpr), + _makeParseExpr("__fwd_diff", parseForwardDifferentiate), + _makeParseExpr("__bwd_diff", parseBackwardDifferentiate), + _makeParseExpr("fwd_diff", parseForwardDifferentiate), + _makeParseExpr("bwd_diff", parseBackwardDifferentiate), + _makeParseExpr("__dispatch_kernel", parseDispatchKernel), + _makeParseExpr("sizeof", parseSizeOfExpr), + _makeParseExpr("alignof", parseAlignOfExpr), + _makeParseExpr("countof", parseCountOfExpr), +}; + +ConstArrayView<SyntaxParseInfo> getSyntaxParseInfos() +{ + return makeConstArrayView(g_parseSyntaxEntries, SLANG_COUNT_OF(g_parseSyntaxEntries)); +} - static SyntaxParseInfo _makeParseExpr(const char* keywordName, SyntaxParseCallback callback) - { - SyntaxParseInfo entry; - entry.classInfo = &Expr::kReflectClassInfo; - entry.keywordName = keywordName; - entry.callback = callback; - return entry; - } - static SyntaxParseInfo _makeParseDecl(const char* keywordName, SyntaxParseCallback callback) - { - SyntaxParseInfo entry; - entry.keywordName = keywordName; - entry.callback = callback; - entry.classInfo = &Decl::kReflectClassInfo; - return entry; - } - static SyntaxParseInfo _makeParseModifier(const char* keywordName, const ReflectClassInfo& classInfo) - { - // If we just have class info - use simple parser - SyntaxParseInfo entry; - entry.keywordName = keywordName; - entry.callback = &parseSimpleSyntax; - entry.classInfo = &classInfo; - return entry; - } - static SyntaxParseInfo _makeParseModifier(const char* keywordName, SyntaxParseCallback callback) - { - SyntaxParseInfo entry; - entry.keywordName = keywordName; - entry.callback = callback; - entry.classInfo = &Modifier::kReflectClassInfo; - return entry; - } - - // Maps a keyword to the associated parsing function - static const SyntaxParseInfo g_parseSyntaxEntries[] = - { - // !!!!!!!!!!!!!!!!!!!! Decls !!!!!!!!!!!!!!!!!! - - _makeParseDecl("typedef", parseTypeDef), - _makeParseDecl("associatedtype", parseAssocType), - _makeParseDecl("type_param", parseGlobalGenericTypeParamDecl ), - _makeParseDecl("cbuffer", parseHLSLCBufferDecl ), - _makeParseDecl("tbuffer", parseHLSLTBufferDecl ), - _makeParseDecl("__generic", parseGenericDecl ), - _makeParseDecl("__extension", parseExtensionDecl ), - _makeParseDecl("extension", parseExtensionDecl ), - _makeParseDecl("__init", parseConstructorDecl ), - _makeParseDecl("__subscript", parseSubscriptDecl ), - _makeParseDecl("property", parsePropertyDecl ), - _makeParseDecl("interface", parseInterfaceDecl ), - _makeParseDecl("syntax", parseSyntaxDecl ), - _makeParseDecl("attribute_syntax", parseAttributeSyntaxDecl ), - _makeParseDecl("__import", parseImportDecl ), - _makeParseDecl("import", parseImportDecl ), - _makeParseDecl("__include", parseIncludeDecl ), - _makeParseDecl("module", parseModuleDeclarationDecl), - _makeParseDecl("implementing", parseImplementingDecl), - _makeParseDecl("let", parseLetDecl ), - _makeParseDecl("var", parseVarDecl ), - _makeParseDecl("func", parseFuncDecl ), - _makeParseDecl("typealias", parseTypeAliasDecl ), - _makeParseDecl("__generic_value_param", parseGlobalGenericValueParamDecl ), - _makeParseDecl("namespace", parseNamespaceDecl ), - _makeParseDecl("using", parseUsingDecl ), - _makeParseDecl("__ignored_block", parseIgnoredBlockDecl ), - _makeParseDecl("__transparent_block", parseTransparentBlockDecl), - _makeParseDecl("__file_decl", parseFileDecl), - _makeParseDecl("__require_capability", parseRequireCapabilityDecl), - - // !!!!!!!!!!!!!!!!!!!!!! Modifer !!!!!!!!!!!!!!!!!!!!!! - - // Add syntax for "simple" modifier keywords. - // These are the ones that just appear as a single - // keyword (no further tokens expected/allowed), - // and which can be represented just by creating - // a new AST node of the corresponding type. - - _makeParseModifier("in", InModifier::kReflectClassInfo), - _makeParseModifier("out", OutModifier::kReflectClassInfo), - _makeParseModifier("inout", InOutModifier::kReflectClassInfo), - _makeParseModifier("__ref", RefModifier::kReflectClassInfo), - _makeParseModifier("__constref", ConstRefModifier::kReflectClassInfo), - _makeParseModifier("const", ConstModifier::kReflectClassInfo), - _makeParseModifier("__builtin", BuiltinModifier::kReflectClassInfo), - _makeParseModifier("highp", GLSLPrecisionModifier::kReflectClassInfo), - _makeParseModifier("lowp", GLSLPrecisionModifier::kReflectClassInfo), - _makeParseModifier("mediump", GLSLPrecisionModifier::kReflectClassInfo), - - _makeParseModifier("__global", ActualGlobalModifier::kReflectClassInfo), - - _makeParseModifier("inline", InlineModifier::kReflectClassInfo), - _makeParseModifier("public", PublicModifier::kReflectClassInfo), - _makeParseModifier("private", PrivateModifier::kReflectClassInfo), - _makeParseModifier("internal", InternalModifier::kReflectClassInfo), - - _makeParseModifier("require", RequireModifier::kReflectClassInfo), - _makeParseModifier("param", ParamModifier::kReflectClassInfo), - _makeParseModifier("extern", ExternModifier::kReflectClassInfo), - - _makeParseModifier("row_major", HLSLRowMajorLayoutModifier::kReflectClassInfo), - _makeParseModifier("column_major", HLSLColumnMajorLayoutModifier::kReflectClassInfo), - - _makeParseModifier("nointerpolation", HLSLNoInterpolationModifier::kReflectClassInfo), - _makeParseModifier("noperspective", HLSLNoPerspectiveModifier::kReflectClassInfo), - _makeParseModifier("linear", HLSLLinearModifier::kReflectClassInfo), - _makeParseModifier("sample", HLSLSampleModifier::kReflectClassInfo), - _makeParseModifier("centroid", HLSLCentroidModifier::kReflectClassInfo), - _makeParseModifier("precise", PreciseModifier::kReflectClassInfo), - _makeParseModifier("shared", HLSLEffectSharedModifier::kReflectClassInfo), - _makeParseModifier("groupshared", HLSLGroupSharedModifier::kReflectClassInfo), - _makeParseModifier("static", HLSLStaticModifier::kReflectClassInfo), - _makeParseModifier("uniform", HLSLUniformModifier::kReflectClassInfo), - _makeParseModifier("volatile", parseVolatileModifier), - _makeParseModifier("coherent", parseCoherentModifier), - _makeParseModifier("restrict", parseRestrictModifier), - _makeParseModifier("readonly", parseReadonlyModifier), - _makeParseModifier("writeonly", parseWriteonlyModifier), - _makeParseModifier("export", HLSLExportModifier::kReflectClassInfo), - _makeParseModifier("dynamic_uniform", DynamicUniformModifier::kReflectClassInfo), - - // Modifiers for geometry shader input - _makeParseModifier("point", HLSLPointModifier::kReflectClassInfo), - _makeParseModifier("line", HLSLLineModifier::kReflectClassInfo), - _makeParseModifier("triangle", HLSLTriangleModifier::kReflectClassInfo), - _makeParseModifier("lineadj", HLSLLineAdjModifier::kReflectClassInfo), - _makeParseModifier("triangleadj", HLSLTriangleAdjModifier::kReflectClassInfo), - - // Modifiers for mesh shader parameters - _makeParseModifier("vertices", HLSLVerticesModifier::kReflectClassInfo), - _makeParseModifier("indices", HLSLIndicesModifier::kReflectClassInfo), - _makeParseModifier("primitives", HLSLPrimitivesModifier::kReflectClassInfo), - _makeParseModifier("payload", HLSLPayloadModifier::kReflectClassInfo), - - // Modifiers for unary operator declarations - _makeParseModifier("__prefix", PrefixModifier::kReflectClassInfo), - _makeParseModifier("__postfix", PostfixModifier::kReflectClassInfo), - - // Modifier to apply to `import` that should be re-exported - _makeParseModifier("__exported", ExportedModifier::kReflectClassInfo), - - // Add syntax for more complex modifiers, which allow - // or expect more tokens after the initial keyword. - - _makeParseModifier("layout", parseLayoutModifier), - _makeParseModifier("hitAttributeEXT", parseHitAttributeEXTModifier), - _makeParseModifier("__intrinsic_op", parseIntrinsicOpModifier), - _makeParseModifier("__target_intrinsic", parseTargetIntrinsicModifier), - _makeParseModifier("__specialized_for_target", parseSpecializedForTargetModifier), - _makeParseModifier("__glsl_extension", parseGLSLExtensionModifier), - _makeParseModifier("__glsl_version", parseGLSLVersionModifier), - _makeParseModifier("__spirv_version", parseSPIRVVersionModifier), - _makeParseModifier("__cuda_sm_version", parseCUDASMVersionModifier), - - _makeParseModifier("__builtin_type", parseBuiltinTypeModifier), - _makeParseModifier("__builtin_requirement", parseBuiltinRequirementModifier), - - _makeParseModifier("__magic_type", parseMagicTypeModifier), - _makeParseModifier("__intrinsic_type", parseIntrinsicTypeModifier), - _makeParseModifier("__implicit_conversion", parseImplicitConversionModifier), - - _makeParseModifier("__attributeTarget", parseAttributeTargetModifier), - - // !!!!!!!!!!!!!!!!!!!!!!! Expr !!!!!!!!!!!!!!!!!!!!!!!!!!! - - _makeParseExpr("this", parseThisExpr), - _makeParseExpr("true", parseTrueExpr), - _makeParseExpr("false", parseFalseExpr), - _makeParseExpr("__return_val", parseReturnValExpr), - _makeParseExpr("nullptr", parseNullPtrExpr), - _makeParseExpr("none", parseNoneExpr), - _makeParseExpr("try", parseTryExpr), - _makeParseExpr("no_diff", parseTreatAsDifferentiableExpr), - _makeParseExpr("__fwd_diff", parseForwardDifferentiate), - _makeParseExpr("__bwd_diff", parseBackwardDifferentiate), - _makeParseExpr("fwd_diff", parseForwardDifferentiate), - _makeParseExpr("bwd_diff", parseBackwardDifferentiate), - _makeParseExpr("__dispatch_kernel", parseDispatchKernel), - _makeParseExpr("sizeof", parseSizeOfExpr), - _makeParseExpr("alignof", parseAlignOfExpr), - _makeParseExpr("countof", parseCountOfExpr), - }; +ModuleDecl* populateBaseLanguageModule(ASTBuilder* astBuilder, Scope* scope) +{ + Session* session = astBuilder->getGlobalSession(); - ConstArrayView<SyntaxParseInfo> getSyntaxParseInfos() - { - return makeConstArrayView(g_parseSyntaxEntries, SLANG_COUNT_OF(g_parseSyntaxEntries)); - } + ModuleDecl* moduleDecl = astBuilder->create<ModuleDecl>(); + scope->containerDecl = moduleDecl; - ModuleDecl* populateBaseLanguageModule( - ASTBuilder* astBuilder, - Scope* scope) + // Add syntax for declaration keywords + for (const auto& info : getSyntaxParseInfos()) { - Session* session = astBuilder->getGlobalSession(); - - ModuleDecl* moduleDecl = astBuilder->create<ModuleDecl>(); - scope->containerDecl = moduleDecl; - - // Add syntax for declaration keywords - for (const auto& info : getSyntaxParseInfos()) - { - addBuiltinSyntaxImpl(session, scope, info.keywordName, info.callback, const_cast<ReflectClassInfo*>(info.classInfo), info.classInfo); - } - - return moduleDecl; + addBuiltinSyntaxImpl( + session, + scope, + info.keywordName, + info.callback, + const_cast<ReflectClassInfo*>(info.classInfo), + info.classInfo); } + return moduleDecl; } + +} // namespace Slang |
