https://github.com/shader-slang/slang
Raw File
Tip revision: d06a78d935b2743494d47ed5cd3f36e38ac9c5ac authored by Yong He on 04 February 2022, 03:17:30 UTC
Add gfx interop to allow more direct D3D12 usage scenarios. (#2117)
Tip revision: d06a78d
slang-parser.cpp
#include "slang-parser.h"

#include <assert.h>
#include <float.h>

#include "slang-compiler.h"
#include "slang-lookup.h"
#include "slang-visitor.h"

#include "../core/slang-semantic-version.h"

namespace Slang
{
    // pre-declare
    static Name* getName(Parser* parser, String const& text);

    // Helper class useful to build a list of modifiers. 
    struct ModifierListBuilder
    {
        ModifierListBuilder()
        {
            m_next = &m_result;
        }
        void add(Modifier* modifier)
        {
            // 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
        {
            Modifier* cur = m_result;
            while (cur)
            {
                T* castCur = as<T>(cur);
                if (castCur)
                {
                    return castCur;
                }
                cur = cur->next;
            }
            return nullptr;
        }
        template <typename T>
        bool hasType() const
        {
            return find<T>() != nullptr;
        }
        Modifier* getFirst() { return m_result; };
    protected:

        Modifier* m_result = nullptr;
        Modifier** m_next;
    };

    enum Precedence : int
    {
        Invalid = -1,
        Comma,
        Assignment,
        TernaryConditional,
        LogicalOr,
        LogicalAnd,
        BitOr,
        BitXor,
        BitAnd,
        EqualityComparison,
        RelationalComparison,
        BitShift,
        Additive,
        Multiplicative,
        Prefix,
        Postfix,
    };

    // TODO: implement two pass parsing for file reference and struct type recognition

    class Parser
    {
    public:
        NamePool*       namePool;
        SourceLanguage  sourceLanguage;
        ASTBuilder*     astBuilder;

        NamePool* getNamePool() { return namePool; }
        SourceLanguage getSourceLanguage() { return sourceLanguage; }

        int anonymousCounter = 0;

        Scope* outerScope = nullptr;
        Scope* currentScope = nullptr;

        TokenReader tokenReader;
        DiagnosticSink* sink;
        int genericDepth = 0;

        // Have we seen any `import` declarations? If so, we need
        // to parse function bodies completely, even if we are in
        // "rewrite" mode.
        bool haveSeenAnyImportDecls = 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 PushScope(ContainerDecl* containerDecl)
        {
            // TODO(JS):
            // 
            // Previously Scope was ref counted. This meant that if a scope was pushed, but not used when popped
            // it's memory would be freed.
            //
            // Here the memory is consumed and will not be freed until the astBuilder goes out of scope.

            Scope* newScope = astBuilder->create<Scope>();
            newScope->containerDecl = containerDecl;
            newScope->parent = currentScope;

            currentScope = newScope;
        }

        void pushScopeAndSetParent(ContainerDecl* containerDecl)
        {
            containerDecl->parentDecl = currentScope->containerDecl;
            PushScope(containerDecl);
        }

        void PopScope()
        {
            currentScope = currentScope->parent;
        }
        Parser(
            ASTBuilder* inAstBuilder,
            TokenSpan const& _tokens,
            DiagnosticSink * sink,
            Scope*           outerScope)
            : tokenReader(_tokens)
            , astBuilder(inAstBuilder)
            , sink(sink)
            , outerScope(outerScope)
        {}
        Parser(const Parser & other) = default;

        //Session* getSession() { return m_session; }

        Token ReadToken();
        Token ReadToken(TokenType type);
        Token ReadToken(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);

        void                                        parseSourceFile(ModuleDecl* program);
        Decl*					ParseStruct();
        ClassDecl*					    ParseClass();
        Stmt*					ParseStatement();
        Stmt*			        parseBlockStatement();
        DeclStmt*			parseVarDeclrStatement(Modifiers modifiers);
        IfStmt*				parseIfStatement();
        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); }

        Expr*				ParseLeafExpression();
        ParamDecl*					ParseParameter();
        Expr*				ParseType();
        TypeExp										ParseTypeExp();

        Parser & operator = (const Parser &) = delete;
    };

    // Forward Declarations

    enum class MatchedTokenType
    {
        Parentheses,
        SquareBrackets,
        CurlyBraces,
        File,
    };

        /// Parse declarations making up the body of `parent`, up to a matching token for `matchType`
    static void parseDecls(
        Parser*             parser,
        ContainerDecl*      parent,
        MatchedTokenType    matchType);

        /// Parse a body consisting of declarations enclosed in `{}`, as the children of `parent`.
    static void parseDeclBody(
        Parser*         parser,
        ContainerDecl*  parent);

    static Decl* parseEnumDecl(Parser* parser);

    static Modifiers _parseOptSemantics(
        Parser* parser);

    static void _parseOptSemantics(
        Parser* parser,
        Decl*	decl);

    static DeclBase* ParseDecl(
        Parser*			parser,
        ContainerDecl*	containerDecl);

    static Decl* ParseSingleDecl(
        Parser*			parser,
        ContainerDecl*	containerDecl);

    static void parseModernParamList(
        Parser*         parser,
        CallableDecl*   decl);

    static TokenType peekTokenType(Parser* parser);

    static Expr* _parseGenericArg(Parser* parser);

    //

    static void Unexpected(
        Parser*     parser)
    {
        // Don't emit "unexpected token" errors if we are in recovering mode
        if (!parser->isRecovering)
        {
            parser->sink->diagnose(parser->tokenReader.peekLoc(), Diagnostics::unexpectedToken,
                parser->tokenReader.peekTokenType());

            // Switch into recovery mode, to suppress additional errors
            parser->isRecovering = true;
        }
    }

    static void Unexpected(
        Parser*     parser,
        char const* expected)
    {
        // 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);

            // Switch into recovery mode, to suppress additional errors
            parser->isRecovering = true;
        }
    }

    static void Unexpected(
        Parser*     parser,
        TokenType    expected)
    {
        // Don't emit "unexpected token" errors if we are in recovering mode
        if (!parser->isRecovering)
        {
            parser->sink->diagnose(parser->tokenReader.peekLoc(), Diagnostics::unexpectedTokenExpectedTokenType,
                parser->tokenReader.peekTokenType(),
                expected);

            // Switch into recovery mode, to suppress additional errors
            parser->isRecovering = true;
        }
    }

    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)
    {
        TokenType tokenType = reader->advanceToken().type;
        switch (tokenType)
        {
        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;
    }

    // Skip balanced
    static TokenType SkipToMatchingToken(
        TokenReader*                reader,
        TokenType    tokenType)
    {
        for (;;)
        {
            if (reader->isAtEnd()) return TokenType::EndOfFile;
            if (reader->peekTokenType() == tokenType)
            {
                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)
    {
        switch (tokenType)
        {
        case TokenType::EndOfFile:
        case TokenType::RBracket:
        case TokenType::RParent:
        case TokenType::RBrace:
            return true;

        default:
            return false;
        }
    }


    // 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)
        {
            isRecovering = false;
            return tokenReader.advanceToken();
        }

        if (!isRecovering)
        {
            Unexpected(this, expected);
            return tokenReader.peekToken();
        }
        else
        {
            // 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)
                {
                    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);
            }
        }
    }

    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.

        bool lookingForEOF = false;
        bool lookingForRCurly = false;
        bool lookingForRParen = false;
        bool lookingForRSquare = false;

        for (int ii = 0; ii < recoverBeforeCount; ++ii)
        {
            switch (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;
            }
        }
        for (int ii = 0; ii < recoverAfterCount; ++ii)
        {
            switch (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* 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)
            {
                if (peek == recoverBefore[ii])
                {
                    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)
            {
                if (peek == recoverAfter[ii])
                {
                    tokenReader->advanceToken();
                    parser->isRecovering = false;
                    return true;
                }
            }

            // Don't try to skip past end of file
            if (peek == TokenType::EndOfFile)
                return false;

            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)
                {
                    // 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;
                }
            }

            // 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 (skipped == recoverAfter[ii])
                {
                    parser->isRecovering = false;
                    return true;
                }
            }
        }
    }

    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)
    {
        TokenType recoverBefore[] = { TokenType::RBrace };
        TokenType recoverAfter[] = { TokenType::Semicolon };
        return TryRecover(parser, recoverBefore, 1, recoverAfter, 1);
    }

    Token Parser::ReadToken(TokenType expected)
    {
        if (tokenReader.peekTokenType() == expected)
        {
            isRecovering = false;
            return tokenReader.advanceToken();
        }

        if (!isRecovering)
        {
            Unexpected(this, expected);
            return tokenReader.peekToken();
        }
        else
        {
            // Try to find a place to recover
            if (TryRecoverBefore(this, expected))
            {
                isRecovering = false;
                return tokenReader.advanceToken();
            }

            return tokenReader.peekToken();
        }
    }

    bool Parser::LookAheadToken(const char* string, int offset)
    {
        TokenReader r = tokenReader;
        for (int ii = 0; ii < offset; ++ii)
            r.advanceToken();

        return r.peekTokenType() == TokenType::Identifier
            && r.peekToken().getContent() == string;
}

    bool Parser::LookAheadToken(TokenType type, int offset)
    {
        TokenReader r = tokenReader;
        for (int ii = 0; ii < offset; ++ii)
            r.advanceToken();

        return r.peekTokenType() == type;
    }

    bool Parser::LookAheadToken(TokenType type)
    {
        return tokenReader.peekTokenType() == type;
    }

    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))
        {
            parser->ReadToken();
            return true;
        }
        return false;
    }

    bool AdvanceIf(Parser* parser, TokenType tokenType, Token* outToken)
    {
        if (parser->LookAheadToken(tokenType))
        {
            *outToken = parser->ReadToken();
            return true;
        }
        return false;
    }

    // Consume a token and return true it if matches, otherwise false
    bool AdvanceIf(Parser* parser, char const* text)
    {
        if (parser->LookAheadToken(text))
        {
            parser->ReadToken();
            return true;
        }
        return false;
    }

        /// 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;
    }

    // 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)];

        // 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)
        {
            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))
            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.
        //
        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;
    }

    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>();

        // TODO(tfoley): parse an actual declarator
        auto type = parser->ParseTypeExp();

        auto nameToken = parser->ReadToken(TokenType::Identifier);
        typeDefDecl->loc = nameToken.loc;

        typeDefDecl->nameAndLoc = NameLoc(nameToken);
        typeDefDecl->type = type;

        return typeDefDecl;
    }

    // Add a modifier to a list of modifiers being built
    static void AddModifier(Modifier*** ioModifierLink, Modifier* modifier)
    {
        Modifier**& modifierLink = *ioModifierLink;

        // 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(;;)
        {
            // At end of the chain? Done.
            if(!*modifierLink)
                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;
        }

        // 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;

        // Do the splice.
        *spliceLink = *modifierLink;

        *modifierLink = modifier;
        modifierLink = &modifier->next;
    }

    void addModifier(
        ModifiableSyntaxNode*    syntax,
        Modifier*                modifier)
    {
        auto modifierLink = &syntax->modifiers.first;
        AddModifier(&modifierLink, modifier);
    }

    // 
    // '::'? identifier ('::' identifier)* 
    static Token parseAttributeName(Parser* parser)
    {
        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); 
        }

        const Token firstIdentifier = parser->ReadToken(TokenType::Identifier);
        if (initialTokenType != TokenType::Scope && parser->tokenReader.peekTokenType() != TokenType::Scope)
        {
            return firstIdentifier;
        }

        // Build up scoped string
        StringBuilder scopedIdentifierBuilder;
        if (initialTokenType == TokenType::Scope)
        {
            scopedIdentifierBuilder.Append('_'); 
        }
        scopedIdentifierBuilder.Append(firstIdentifier.getContent());

        while (parser->tokenReader.peekTokenType() == TokenType::Scope)
        {
            parser->ReadToken(TokenType::Scope);
            scopedIdentifierBuilder.Append('_'); 
            
            const Token nextIdentifier(parser->ReadToken(TokenType::Identifier));
            scopedIdentifierBuilder.Append(nextIdentifier.getContent());
        }

        // Make a 'token'
        SourceManager* sourceManager = parser->sink->getSourceManager();
        const UnownedStringSlice scopedIdentifier(sourceManager->allocateStringSlice(scopedIdentifierBuilder.getUnownedSlice()));   
        Token token(TokenType::Identifier, scopedIdentifier, scopedIdSourceLoc);

        // Get the name pool
        auto namePool = parser->getNamePool();

        // Since it's an Identifier have to set the name.
        token.setName(namePool->getName(token.getContent()));

        return token;
    }

    // Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers
    static void ParseSquareBracketAttributes(Parser* parser, Modifier*** ioModifierLink)
    {
        parser->ReadToken(TokenType::LBracket);

        const bool hasDoubleBracket = AdvanceIf(parser, TokenType::LBracket);

        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 nameToken = parseAttributeName(parser);

            UncheckedAttribute* modifier = parser->astBuilder->create<UncheckedAttribute>();
            modifier->keywordName = nameToken.getName();
            modifier->loc = nameToken.getLoc();
            modifier->scope = parser->currentScope;

            if (AdvanceIf(parser, TokenType::LParent))
            {
                // HLSL-style `[name(arg0, ...)]` attribute

                while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
                {
                    auto arg = parser->ParseArgExpr();
                    if (arg)
                    {
                        modifier->args.add(arg);
                    }

                    if (AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
                        break;

                    parser->ReadToken(TokenType::Comma);
                }
            }
            AddModifier(ioModifierLink, modifier);


            if (AdvanceIfMatch(parser, MatchedTokenType::SquareBrackets))
                break;

            parser->ReadToken(TokenType::Comma);
        }

        if (hasDoubleBracket)
        {
            // Read the second ]
            parser->ReadToken(TokenType::RBracket);
        }
    }

    static TokenType peekTokenType(Parser* parser)
    {
        return parser->tokenReader.peekTokenType();
    }

        /// 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();
    }

    static Token advanceToken(Parser* parser)
    {
        return parser->ReadToken();
    }

    static Token peekToken(Parser* parser)
    {
        return parser->tokenReader.peekToken();
    }

    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);

        // 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) )
        {
            return syntaxDecl;
        }
        else
        {
            return nullptr;
        }
    }

    template<typename T>
    bool tryParseUsingSyntaxDecl(
        Parser*     parser,
        SyntaxDecl* syntaxDecl,
        T**  outSyntax)
    {
        if (!syntaxDecl)
            return false;

        if (!syntaxDecl->syntaxClass.isSubClassOf<T>())
            return false;

        // Consume the token that specified the keyword
        auto keywordToken = advanceToken(parser);

        NodeBase* parsedObject = syntaxDecl->parseCallback(parser, syntaxDecl->parseUserData);
        if (!parsedObject)
        {
            return false;
        }

        auto syntax = as<T>(parsedObject);
        if (syntax)
        {
            if (!syntax->loc.isValid())
            {
                syntax->loc = keywordToken.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");
        }

        *outSyntax = syntax;
        return true;
    }

    template<typename T>
    bool tryParseUsingSyntaxDecl(
        Parser*     parser,
        T**  outSyntax)
    {
        if (peekTokenType(parser) != TokenType::Identifier)
            return false;

        auto nameToken = peekToken(parser);
        auto name = nameToken.getName();

        auto syntaxDecl = tryLookUpSyntaxDecl(parser, name);

        if (!syntaxDecl)
            return false;

        return tryParseUsingSyntaxDecl(parser, syntaxDecl, outSyntax);
    }

    static Modifiers ParseModifiers(Parser* parser)
    {
        Modifiers modifiers;
        Modifier** modifierLink = &modifiers.first;
        for (;;)
        {
            SourceLoc loc = parser->tokenReader.peekLoc();

            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;

            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);

                    Modifier* parsedModifier = nullptr;
                    if (tryParseUsingSyntaxDecl<Modifier>(parser, &parsedModifier))
                    {
                        parsedModifier->keywordName = nameToken.getName();
                        if (!parsedModifier->loc.isValid())
                        {
                            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;
            }
        }
    }

    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 NameLoc expectIdentifier(Parser* parser)
    {
        return NameLoc(parser->ReadToken(TokenType::Identifier));
    }

    static NodeBase* parseImportDecl(
        Parser* parser, void* /*userData*/)
    {
        parser->haveSeenAnyImportDecls = true;

        auto decl = parser->astBuilder->create<ImportDecl>();
        decl->scope = parser->currentScope;

        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);

            // 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))
                {
                    sb << "/";
                    sb << parser->ReadToken(TokenType::Identifier).getContent();
                }

                moduleNameAndLoc.name = getName(parser, sb.ProduceString());
            }

            decl->moduleNameAndLoc = moduleNameAndLoc;
        }

        parser->ReadToken(TokenType::Semicolon);

        return decl;
    }

    static NameLoc ParseDeclName(
        Parser* parser)
    {
        Token nameToken;
        if (AdvanceIf(parser, "operator"))
        {
            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;

            // 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;
            }

            return NameLoc(
                getName(parser, nameToken.getContent()),
                nameToken.loc);
        }
        else
        {
            nameToken = parser->ReadToken(TokenType::Identifier);
            return NameLoc(nameToken);
        }
    }

    // A "declarator" as used in C-style languages
    struct Declarator : RefObject
    {
        // Different cases of declarator appear as "flavors" here
        enum class Flavor
        {
            name,
            Pointer,
            Array,
        };
        Flavor flavor;
    };

    // The most common case of declarator uses a simple name
    struct NameDeclarator : Declarator
    {
        NameLoc nameAndLoc;
    };

    // A declarator that declares a pointer type
    struct PointerDeclarator : Declarator
    {
        // location of the `*` token
        SourceLoc starLoc;

        RefPtr<Declarator>				inner;
    };

    // A declarator that declares an array type
    struct ArrayDeclarator : Declarator
    {
        RefPtr<Declarator>				inner;

        // location of the `[` token
        SourceLoc openBracketLoc;

        // The expression that yields the element count, or NULL
        Expr*	elementCountExpr = nullptr;
    };

    // "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)
        {
            member->parentDecl = container;
            container->members.add(member);
        }
    }

    static void AddMember(Scope* scope, Decl* member)
    {
        if (scope)
        {
            AddMember(scope->containerDecl, member);
        }
    }

    static Decl* ParseGenericParamDecl(
        Parser*             parser,
        GenericDecl* genericDecl)
    {
        // simple syntax to introduce a value parameter
        if (AdvanceIf(parser, "let"))
        {
            // 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))
            {
                paramDecl->initExpr = parser->ParseInitExpr();
            }
            return paramDecl;
        }
        else
        {
            // default case is a type parameter
            GenericTypeParamDecl* paramDecl = parser->astBuilder->create<GenericTypeParamDecl>();
            parser->FillPosition(paramDecl);
            paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
            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 paramType = DeclRefType::create(
                    parser->astBuilder,
                    DeclRef<Decl>(paramDecl, nullptr));

                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();

                AddMember(genericDecl, paramConstraint);


            }
            if (AdvanceIf(parser, TokenType::OpAssign))
            {
                paramDecl->initType = parser->ParseTypeExp();
            }
            return paramDecl;
        }
    }

    template<typename TFunc>
    static void ParseGenericDeclImpl(
        Parser* parser, GenericDecl* decl, const TFunc & parseInnerFunc)
    {
        parser->ReadToken(TokenType::OpLess);
        parser->genericDepth++;
        for (;;)
        {
            const TokenType tokenType = parser->tokenReader.peekTokenType();
            if (tokenType == TokenType::OpGreater ||
                tokenType == TokenType::EndOfFile)
            {
                break;
            }

            AddMember(decl, ParseGenericParamDecl(parser, decl));

            if (parser->LookAheadToken(TokenType::OpGreater))
                break;

            parser->ReadToken(TokenType::Comma);
        }
        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;
        }
    }

    template<typename ParseFunc>
    static Decl* parseOptGenericDecl(
        Parser* parser, const ParseFunc& parseInner)
    {
        // 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
        {
            return parseInner(nullptr);
        }
    }

    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);

        // 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 bunc hof 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;
        }

        while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
        {
            AddMember(decl, parser->ParseParameter());
            if (AdvanceIf(parser, TokenType::RParent))
                break;
            parser->ReadToken(TokenType::Comma);
        }
    }

    // systematically replace all scopes in an expression tree
    class ReplaceScopeVisitor : public ExprVisitor<ReplaceScopeVisitor>
    {
    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);
            expr->indexExpression->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 visitExpr(Expr* /*expr*/)
        {}
    };

        /// Parse an optional body statement for a declaration that can have a body.
    static Stmt* parseOptBody(Parser* parser)
    {
        if (AdvanceIf(parser, TokenType::Semicolon))
        {
            // empty body
            return nullptr;
        }
        else
        {
            return parser->parseBlockStatement();
        }
    }

        /// 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*)
        {
            // HACK: The return type of the function will already have been
            // parsed in a scope that didn't include the function's generic
            // parameters.
            //
            // We will use a visitor here to try and replace the scope associated
            // with any name expressiosn in the reuslt type.
            //
            // TODO: This should be fixed by not associating scopes with
            // such expressions at parse time, and instead pushing down scopes
            // as part of the state during semantic checking.
            //
            ReplaceScopeVisitor replaceScopeVisitor;
            replaceScopeVisitor.scope = parser->currentScope;
            declaratorInfo.typeSpec->accept(&replaceScopeVisitor, nullptr);

            decl->returnType = TypeExp(declaratorInfo.typeSpec);

            parser->PushScope(decl);

            parseParameterList(parser, decl);
            _parseOptSemantics(parser, decl);
            decl->body = parseOptBody(parser);

            parser->PopScope();

            return decl;
        });
    }

    static VarDeclBase* CreateVarDeclForContext(
        ASTBuilder* astBuilder, 
        ContainerDecl*  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>();
        }
    }

    // Add modifiers to the end of the modifier list for a declaration
    static void _addModifiers(Decl* decl, Modifiers const& modifiers)
    {
        if (!modifiers.first)
            return;

        Modifier** link = &decl->modifiers.first;
        while (*link)
        {
            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);
    }

    static Name* generateName(Parser* parser)
    {
        return generateName(parser, "anonymous_" + String(parser->anonymousCounter++));
    }


    // 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);

        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);

        decl->initExpr = declaratorInfo.initializer;
    }

    typedef unsigned int DeclaratorParseOptions;
    enum
    {
        kDeclaratorParseOptions_None = 0,
        kDeclaratorParseOption_AllowEmpty = 1 << 0,
    };

    static RefPtr<Declarator> parseDeclarator(
        Parser*                 parser,
        DeclaratorParseOptions  options);

    static RefPtr<Declarator> parseDirectAbstractDeclarator(
        Parser*                 parser,
        DeclaratorParseOptions  options)
    {
        RefPtr<Declarator> declarator;
        switch( parser->tokenReader.peekTokenType() )
        {
        case TokenType::Identifier:
            {
                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->ReadToken(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;
        }

        // postifx additions
        for( ;;)
        {
            switch( parser->tokenReader.peekTokenType() )
            {
            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);

                    declarator = arrayDeclarator;
                    continue;
                }

            case TokenType::LParent:
                break;

            default:
                break;
            }

            break;
        }

        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 )
        {
            auto ptrDeclarator = new PointerDeclarator();
            ptrDeclarator->starLoc = parser->tokenReader.peekLoc();
            ptrDeclarator->flavor = Declarator::Flavor::Pointer;

            parser->ReadToken(TokenType::OpMul);

            // TODO(tfoley): allow qualifiers like `const` here?

            ptrDeclarator->inner = parseDeclarator(parser, options);
            return ptrDeclarator;
        }
        else
        {
            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
    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))
        {
            result.initializer = parser->ParseInitExpr();
        }
        return result;
    }

    static void UnwrapDeclarator(
        ASTBuilder*         astBuilder, 
        RefPtr<Declarator>	declarator,
        DeclaratorInfo*		ioInfo)
    {
        while( declarator )
        {
            switch(declarator->flavor)
            {
            case Declarator::Flavor::name:
                {
                    auto nameDeclarator = (NameDeclarator*) declarator.Ptr();
                    ioInfo->nameAndLoc = nameDeclarator->nameAndLoc;
                    return;
                }
                break;

            case Declarator::Flavor::Pointer:
                {
                    auto ptrDeclarator = (PointerDeclarator*) declarator.Ptr();

                    // TODO(tfoley): we don't support pointers for now
                    // ioInfo->typeSpec = new PointerTypeExpr(ioInfo->typeSpec);

                    declarator = ptrDeclarator->inner;
                }
                break;

            case Declarator::Flavor::Array:
                {
                    // TODO(tfoley): we don't support pointers for now
                    auto arrayDeclarator = (ArrayDeclarator*) declarator.Ptr();

                    auto arrayTypeExpr = astBuilder->create<IndexExpr>();
                    arrayTypeExpr->loc = arrayDeclarator->openBracketLoc;
                    arrayTypeExpr->baseExpression = ioInfo->typeSpec;
                    arrayTypeExpr->indexExpression = arrayDeclarator->elementCountExpr;
                    ioInfo->typeSpec = arrayTypeExpr;

                    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;
    }

    // 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)
        {
            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;
            }
        }

        DeclBase* getResult()
        {
            if(group) return group;
            return decl;
        }
    };

    // 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

        auto expr = parser->astBuilder->create<VarExpr>();
        expr->scope = parser->currentScope;
        expr->loc = decl->getNameLoc();
        expr->name = decl->getName();
        return expr;
    }

    // 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;

        // Put the resulting expression (which should evaluate to a type) here
        Expr*    expr = nullptr;
    };

    static Expr* parseGenericApp(
        Parser*                         parser,
        Expr*    base)
    {
        GenericAppExpr* genericApp = parser->astBuilder->create<GenericAppExpr>();

        parser->FillPosition(genericApp); // set up scope for lookup
        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--;

        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 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;

        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;

        // Setup without diagnostic lexer, or SourceLocationLine output
        // as this sink is just to *try* generic application
        DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr);

        Parser newParser(*parser);
        newParser.sink = &newSink;

        /* auto speculateParseRs = */parseGenericApp(&newParser, base);

        if (newSink.getErrorCount() == 0)
        {
            // disambiguate based on FOLLOW set
            switch (peekTokenType(&newParser))
            {
            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:
            {
                return parseGenericApp(parser, base);
            }
            }
        }
        return base;
    }
    static Expr* parseMemberType(Parser * parser, Expr* base)
    {
        // When called the :: or . have been consumed, so don't need to consume here.

        MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>();

        parser->FillPosition(memberExpr);
        memberExpr->baseExpression = base;
        memberExpr->name = expectIdentifier(parser).name;
        return memberExpr;
    }

    // Parse option `[]` braces after a type expression, that indicate an array type
    static Expr* parsePostfixTypeSuffix(
        Parser* parser,
        Expr* inTypeExpr)
    {
        auto typeExpr = inTypeExpr;
        while (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->indexExpression = parser->ParseExpression();
            }
            parser->ReadToken(TokenType::RBracket);
            typeExpr = arrType;
        }
        return typeExpr;
    }

    static Expr* parseTaggedUnionType(Parser* parser)
    {
        TaggedUnionTypeExpr* taggedUnionType = parser->astBuilder->create<TaggedUnionTypeExpr>();

        parser->ReadToken(TokenType::LParent);
        while(!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
        {
            auto caseType = parser->ParseTypeExp();
            taggedUnionType->caseTypes.add(caseType);

            if(AdvanceIf(parser, TokenType::RParent))
                break;

            parser->ReadToken(TokenType::Comma);
        }

        return taggedUnionType;
    }

    static NodeBase* parseTaggedUnionType(Parser* parser, void* /*unused*/)
    {
        return parseTaggedUnionType(parser);
    }

        /// Parse a `This` type expression
    static Expr* parseThisTypeExpr(Parser* parser)
    {
        ThisTypeExpr* expr = parser->astBuilder->create<ThisTypeExpr>();
        expr->scope = parser->currentScope;
        return expr;
    }

    static NodeBase* parseThisTypeExpr(Parser* parser, void* /*userData*/)
    {
        return parseThisTypeExpr(parser);
    }

        /// Apply the given `modifiers` (if any) to the given `typeExpr`
    static Expr* _applyModifiersToTypeExpr(Parser* parser, Expr* typeExpr, Modifiers const& modifiers)
    {
        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;
        }
    }

        /// 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)
        {
            // We want to detect whether we have a type modifier or not.
            //
            auto typeModifier = as<TypeModifier>(baseModifier);

            // The easy case is when we *don't* have a type modifier.
            //
            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;

                // 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);
    }

    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.
            //
            Modifiers modifiers = inModifiers;
            typeSpec.expr = _applyTypeModifiersToTypeExpr(parser, typeSpec.expr, modifiers);

            // 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;
        }
        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;
        }
        // TODO: This case would not be needed if we had the
        // code below dispatch into `parseAtomicExpr`, which
        // already includes logic for keyword lookup.
        //
        // Leaving this case here for now to avoid breaking anything.
        //
        else if(AdvanceIf(parser, "__TaggedUnion"))
        {
            typeSpec.expr = parseTaggedUnionType(parser);
            return typeSpec;
        }
        else if(AdvanceIf(parser, "This"))
        {
            typeSpec.expr = parseThisTypeExpr(parser);
            return typeSpec;
        }

        Token typeName = parser->ReadToken(TokenType::Identifier);

        auto basicType = parser->astBuilder->create<VarExpr>();
        basicType->scope = parser->currentScope;
        basicType->loc = typeName.loc;
        basicType->name = typeName.getNameOrNull();

        Expr* typeExpr = basicType;

        bool shouldLoop = true;
        while (shouldLoop)
        {
            switch (peekTokenType(parser))
            {
            case TokenType::OpLess:
                typeExpr = parseGenericApp(parser, typeExpr);
                break;
            case TokenType::Scope:
                parser->ReadToken(TokenType::Scope);
                typeExpr = parseMemberType(parser, typeExpr);
                break;
            case TokenType::Dot:
                parser->ReadToken(TokenType::Dot);
                typeExpr = parseMemberType(parser, typeExpr);
                break;
            default:
                shouldLoop = false;
            }
        }

        typeSpec.expr = typeExpr;
        return typeSpec;
    }

        /// 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);

        // 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);

        return typeSpec;
    }

        /// 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);

        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);

        // 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);

        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)
        {
            if(typeSpec.decl)
            {
                if(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();
                }
            }
        }


        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)
        {
            // Looks like a function, so parse it like one.
            UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo);
            return parseTraditionalFuncDecl(parser, declaratorInfo);
        }

        // Otherwise we are looking at a variable declaration, which could be one in a sequence...

        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);

            declGroupBuilder.addDecl(firstDecl);
            return declGroupBuilder.getResult();
        }

        // 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.

        auto sharedTypeSpec = parser->astBuilder->create<SharedTypeExpr>();
        sharedTypeSpec->loc = typeSpec.expr->loc;
        sharedTypeSpec->base = TypeExp(typeSpec.expr);

        for(;;)
        {
            declaratorInfo.typeSpec = sharedTypeSpec;
            UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo);

            VarDeclBase* varDecl = CreateVarDeclForContext(parser->astBuilder, containerDecl);
            CompleteVarDecl(parser, varDecl, declaratorInfo);

            declGroupBuilder.addDecl(varDecl);

            // end of the sequence?
            if(AdvanceIf(parser, TokenType::Semicolon))
                return declGroupBuilder.getResult();

            // ad-hoc recovery, to avoid infinite loops
            if( parser->isRecovering )
            {
                parser->ReadToken(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...

            if (!AdvanceIf(parser, TokenType::Comma))
            {
                parser->ReadToken(TokenType::Semicolon);
                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)
    {
        semantic->registerName = parser->ReadToken(TokenType::Identifier);
        if (AdvanceIf(parser, TokenType::Dot))
        {
            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)
    {
        // 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))
        {
            semantic->spaceName = parser->ReadToken(TokenType::Identifier);
        }

        parser->ReadToken(TokenType::RParent);
    }

    /// 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);

        // Expect a parenthized list of additional arguments
        parser->ReadToken(TokenType::LParent);

        // First and only argument is a required register name and optional component mask
        parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic);

        parser->ReadToken(TokenType::RParent);

        parser->sink->diagnose(semantic, Diagnostics::packOffsetNotSupported);
    }

    static RayPayloadAccessSemantic* _parseRayPayloadAccessSemantic(Parser* parser, RayPayloadAccessSemantic* semantic)
    {
        parser->FillPosition(semantic);

        // Read the keyword that introduced the semantic
        semantic->name = parser->ReadToken(TokenType::Identifier);

        parser->ReadToken(TokenType::LParent);

        for(;;)
        {
            if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
                break;

            auto stageName = parser->ReadToken(TokenType::Identifier);
            semantic->stageNameTokens.add(stageName);

            if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
                break;

            expect(parser, TokenType::Comma);
        }

        return semantic;
    }

    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;
        }
        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
        {
            // expect an identifier, just to produce an error message
            parser->ReadToken(TokenType::Identifier);
            return nullptr;
        }
    }

    //
    // opt-semantics ::= (':' semantic)*
    //
    static Modifiers _parseOptSemantics(
        Parser* parser)
    {
        Modifiers modifiers;

        if (!AdvanceIf(parser, TokenType::Colon))
            return modifiers;

        Modifier** link = &modifiers.first;
        SLANG_ASSERT(!*link);

        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))
            {
                return modifiers;
            }
        }

    }


    static void _parseOptSemantics(
        Parser* parser,
        Decl*	decl)
    {
        _addModifiers(decl, _parseOptSemantics(parser));
    }

    static Decl* ParseHLSLBufferDecl(
        Parser*	parser,
        String  bufferWrapperTypeName)
    {
        // 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();

        // 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>();
        VarDecl* bufferVarDecl = parser->astBuilder->create<VarDecl>();

        // Both declarations will have a location that points to the name
        parser->FillPosition(bufferDataTypeDecl);
        parser->FillPosition(bufferVarDecl);

        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);

        // Both the buffer variable and its type need to have names generated
        bufferVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + String(reflectionNameToken.getContent()));
        bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + String(reflectionNameToken.getContent()));

        addModifier(bufferDataTypeDecl, parser->astBuilder->create<ImplicitParameterGroupElementTypeModifier>());
        addModifier(bufferVarDecl, parser->astBuilder->create<ImplicitParameterGroupVariableModifier>());

        // 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 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;

        // Construct a type expression that represents the type for the variable,
        // which is the wrapper type applied to the data type
        auto bufferVarTypeExpr = parser->astBuilder->create<GenericAppExpr>();
        bufferVarTypeExpr->loc = bufferVarDecl->loc;
        bufferVarTypeExpr->functionExpr = bufferWrapperTypeExpr;
        bufferVarTypeExpr->arguments.add(bufferDataTypeExpr);

        bufferVarDecl->type.exp = bufferVarTypeExpr;

        // 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);

        // All HLSL buffer declarations are "transparent" in 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;

        // 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);

        return bufferVarDecl;
    }

    static NodeBase* parseHLSLCBufferDecl(
        Parser*	parser, void* /*userData*/)
    {
        return ParseHLSLBufferDecl(parser, "ConstantBuffer");
    }

    static NodeBase* parseHLSLTBufferDecl(
        Parser*	parser, void* /*userData*/)
    {
        return ParseHLSLBufferDecl(parser, "TextureBuffer");
    }

    static void parseOptionalInheritanceClause(Parser* parser, AggTypeDeclBase* decl)
    {
        if (AdvanceIf(parser, TokenType::Colon))
        {
            do
            {
                auto base = parser->ParseTypeExp();

                auto inheritanceDecl = parser->astBuilder->create<InheritanceDecl>();
                inheritanceDecl->loc = base.exp->loc;
                inheritanceDecl->nameAndLoc.name = getName(parser, "$inheritance");
                inheritanceDecl->base = base;

                AddMember(decl, inheritanceDecl);

            } while (AdvanceIf(parser, TokenType::Comma));
        }
    }

    static NodeBase* parseExtensionDecl(Parser* parser, void* /*userData*/)
    {
        ExtensionDecl* decl = parser->astBuilder->create<ExtensionDecl>();
        parser->FillPosition(decl);
        decl->targetType = parser->ParseTypeExp();
        parseOptionalInheritanceClause(parser, decl);
        parseDeclBody(parser, decl);

        return decl;
    }


    static void parseOptionalGenericConstraints(Parser* parser, ContainerDecl* decl)
    {
        if (AdvanceIf(parser, TokenType::Colon))
        {
            do
            {
                GenericTypeConstraintDecl* paramConstraint = parser->astBuilder->create<GenericTypeConstraintDecl>();
                parser->FillPosition(paramConstraint);

                // substitution needs to be filled during check
                DeclRefType* paramType = DeclRefType::create(parser->astBuilder, DeclRef<Decl>(decl, nullptr));

                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);
                paramConstraint->sup = parser->ParseTypeExp();

                AddMember(decl, paramConstraint);
            } while (AdvanceIf(parser, TokenType::Comma));
        }
    }

    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);
        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;

        if(AdvanceIf(parser, TokenType::Colon))
        {
            genericParamDecl->type = parser->ParseTypeExp();
        }

        if(AdvanceIf(parser, TokenType::OpAssign))
        {
            genericParamDecl->initExpr = parser->ParseInitExpr();
        }

        parser->ReadToken(TokenType::Semicolon);
        return genericParamDecl;
    }

    static NodeBase* parseInterfaceDecl(Parser* parser, void* /*userData*/)
    {
        InterfaceDecl* decl = parser->astBuilder->create<InterfaceDecl>();
        parser->FillPosition(decl);
        decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));

        parseOptionalInheritanceClause(parser, decl);

        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.
        //
        // TODO: We should eventually 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 { ... } }
        //
        // TODO: Support we also support the degenerate case of
        // a namesapce without a name? Should that be treated as
        // an anonymous (and implicitly imported) namespace, or
        // something else?
        //
        // TODO: Should we support a shorthand syntax for putting
        // the rest of the current scope/file into a namespace:
        //
        //      namespace A.B;
        //
        //      ...
        //
        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).
        //
        // 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).
        //
        NamespaceDecl* namespaceDecl = nullptr;
        NodeBase* result = nullptr;
        //
        // 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);

            // 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.
            //
            // Eventually we should make `builtMemberDictionary()`
            // incremental, so that it only has to process members
            // added since the last time it was invoked.
            //
            buildMemberDictionary(parentDecl);

            // 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->memberDictionary.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 )
            {
                namespaceDecl = parser->astBuilder->create<NamespaceDecl>();
                namespaceDecl->nameAndLoc = nameAndLoc;

                // In the case where we are creating the first
                // declaration of the given namesapce, we need
                // to use it as the return value of the parsing
                // callback, so that it is appropriately added
                // to the parent declaration.
                //
                result = namespaceDecl;
            }
        }

        // 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);

        return result;
    }

    static NodeBase* parseUsingDecl(Parser* parser, void* /*userData*/)
    {
        UsingDecl* decl = parser->astBuilder->create<UsingDecl>();
        parser->FillPosition(decl);

        // 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;

        // 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.

        // 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);
        }

        // 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();

        expect(parser, TokenType::Semicolon);

        return decl;
    }

    static NodeBase* parseConstructorDecl(Parser* parser, void* /*userData*/)
    {
        ConstructorDecl* decl = parser->astBuilder->create<ConstructorDecl>();
        parser->FillPosition(decl);
        parser->PushScope(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");

        parseParameterList(parser, decl);

        decl->body = parseOptBody(parser);

        parser->PopScope();
        return decl;
    }

    static AccessorDecl* parseAccessorDecl(Parser* parser)
    {
        Modifiers modifiers = ParseModifiers(parser);

        AccessorDecl* decl = nullptr;
        auto loc = peekToken(parser).loc;
        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;

        _addModifiers(decl, modifiers);

        parser->PushScope(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);
        }

        if( parser->tokenReader.peekTokenType() == TokenType::LBrace )
        {
            decl->body = parser->parseBlockStatement();
        }
        else
        {
            parser->ReadToken(TokenType::Semicolon);
        }

        parser->PopScope();


        return decl;
    }

    static void parseStorageDeclBody(Parser* parser, ContainerDecl* decl)
    {
        if( AdvanceIf(parser, TokenType::LBrace) )
        {
            // We want to parse nested "accessor" declarations
            while( !AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces) )
            {
                auto accessor = parseAccessorDecl(parser);
                AddMember(decl, accessor);
            }
        }
        else
        {
            parser->ReadToken(TokenType::Semicolon);

            // empty body should be treated like `{ get; }`
        }
    }

    static NodeBase* parseSubscriptDecl(Parser* parser, void* /*userData*/)
    {
        SubscriptDecl* decl = parser->astBuilder->create<SubscriptDecl>();
        parser->FillPosition(decl);
        parser->PushScope(decl);

        // TODO: the use of this name here is a bit magical...
        decl->nameAndLoc.name = getName(parser, "operator[]");

        parseParameterList(parser, decl);

        if( AdvanceIf(parser, TokenType::RightArrow) )
        {
            decl->returnType = parser->ParseTypeExp();
        }

        parseStorageDeclBody(parser, decl);

        parser->PopScope();
        return decl;
    }

        /// 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;

        switch(peekTokenType(parser, 1))
        {
        default:
            return false;

        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->FillPosition(decl);
        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))
        {
            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();

            auto declarator = parseDeclarator(parser, kDeclaratorParseOptions_None);
            UnwrapDeclarator(parser->astBuilder, declarator, &declaratorInfo);

            // 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).

            decl->nameAndLoc = declaratorInfo.nameAndLoc;
            decl->type = TypeExp(declaratorInfo.typeSpec);
        }

        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))
        {
            decl->type = parser->ParseTypeExp();
        }

        if(AdvanceIf(parser, TokenType::OpAssign))
        {
            decl->initExpr = parser->ParseInitExpr();
        }
    }

    static void parseModernVarDeclCommon(
        Parser*         parser,
        VarDecl* decl)
    {
        parseModernVarDeclBaseCommon(parser, decl);
        expect(parser, TokenType::Semicolon);
    }

    static NodeBase* parseLetDecl(
        Parser* parser, void* /*userData*/)
    {
        LetDecl* decl = parser->astBuilder->create<LetDecl>();
        parseModernVarDeclCommon(parser, decl);
        return decl;
    }

    static NodeBase* parseVarDecl(
        Parser* parser, void* /*userData*/)
    {
        VarDecl* decl = parser->astBuilder->create<VarDecl>();
        parseModernVarDeclCommon(parser, decl);
        return decl;
    }

        /// 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();

        InitDeclarator initDeclarator = parseInitDeclarator(parser, options);
        UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo);

        // Assume it is a variable-like declarator
        CompleteVarDecl(parser, decl, declaratorInfo);
    }

    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 void parseModernParamList(
        Parser*                 parser,
        CallableDecl*    decl)
    {
        parser->ReadToken(TokenType::LParent);

        while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
        {
            AddMember(decl, parseModernParamDecl(parser));
            if (AdvanceIf(parser, TokenType::RParent))
                break;
            parser->ReadToken(TokenType::Comma);
        }
    }

    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*)
        {
            parser->PushScope(decl);
            parseModernParamList(parser, decl);
            if(AdvanceIf(parser, TokenType::RightArrow))
            {
                decl->returnType = parser->ParseTypeExp();
            }
            decl->body = parseOptBody(parser);
            parser->PopScope();
            return decl;
        });
    }

    static NodeBase* parseTypeAliasDecl(
        Parser* parser, void* /*userData*/)
    {
        TypeAliasDecl* decl = parser->astBuilder->create<TypeAliasDecl>();

        parser->FillPosition(decl);
        decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));

        return parseOptGenericDecl(parser, [&](GenericDecl*)
        {
            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);
    }

    // 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))
        {
            // User is specifying the class that should be construted
            auto classNameAndLoc = expectIdentifier(parser);

            syntaxClass = parser->astBuilder->findSyntaxClass(classNameAndLoc.name);
        }

        // 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;

        // 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);

            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;
            }
        }

        // 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
        }

        expect(parser, TokenType::Semicolon);

        // TODO: skip creating the declaration if anything failed, just to not screw things
        // up for downstream code?

        SyntaxDecl* syntaxDecl = parser->astBuilder->create<SyntaxDecl>();
        syntaxDecl->nameAndLoc = nameAndLoc;
        syntaxDecl->loc = nameAndLoc.loc;
        syntaxDecl->syntaxClass = syntaxClass;
        syntaxDecl->parseCallback = parseCallback;
        syntaxDecl->parseUserData = parseUserData;
        return syntaxDecl;
    }

    // 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);

        ParamDecl* paramDecl = parser->astBuilder->create<ParamDecl>();
        paramDecl->nameAndLoc = nameAndLoc;

        if(AdvanceIf(parser, TokenType::Colon))
        {
            paramDecl->type = parser->ParseTypeExp();
        }

        if(AdvanceIf(parser, TokenType::OpAssign))
        {
            paramDecl->initExpr = parser->ParseInitExpr();
        }

        return paramDecl;
    }

    // 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:
        //
        // 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.

        expect(parser, TokenType::LBracket);

        // First we parse the attribute name.
        auto nameAndLoc = expectIdentifier(parser);

        AttributeDecl* attrDecl = parser->astBuilder->create<AttributeDecl>();
        if(AdvanceIf(parser, TokenType::LParent))
        {
            while(!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
            {
                auto param = parseAttributeParamDecl(parser);

                AddMember(attrDecl, param);

                if(AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
                    break;

                expect(parser, TokenType::Comma);
            }
        }

        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.

        // 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);
        }
        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.
        }

        expect(parser, TokenType::Semicolon);

        // 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;
    }

    // Finish up work on a declaration that was parsed
    static void CompleteDecl(
        Parser*				/*parser*/,
        Decl*		decl,
        ContainerDecl*		containerDecl,
        Modifiers			modifiers)
    {
        // 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;
        _addModifiers(declToModify, modifiers);

        // Make sure the decl is properly nested inside its lexical parent
        if (containerDecl)
        {
            AddMember(containerDecl, decl);
        }
    }

    static DeclBase* ParseDeclWithModifiers(
        Parser*             parser,
        ContainerDecl*      containerDecl,
        Modifiers           modifiers )
    {
        DeclBase* decl = nullptr;

        auto loc = parser->tokenReader.peekLoc();

        switch (peekTokenType(parser))
        {
        case TokenType::Identifier:
            {
                // 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))
                {
                    decl = parsedDecl;
                    break;
                }

                // 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;
            }
            break;

        // If nothing else matched, we try to parse an "ordinary" declarator-based declaration
        default:
            decl = ParseDeclaratorDecl(parser, containerDecl, modifiers);
            break;
        }

        if (decl)
        {
            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;

                for( auto subDecl : declGroup->decls )
                {
                    CompleteDecl(parser, subDecl, containerDecl, modifiers);
                }
            }
        }
        return decl;
    }

    static DeclBase* ParseDecl(
        Parser*         parser,
        ContainerDecl*  containerDecl)
    {
        Modifiers modifiers = ParseModifiers(parser);
        return ParseDeclWithModifiers(parser, containerDecl, modifiers);
    }

    static Decl* ParseSingleDecl(
        Parser*			parser,
        ContainerDecl*	containerDecl)
    {
        auto declBase = ParseDecl(parser, containerDecl);
        if(!declBase)
            return nullptr;
        if( auto decl = as<Decl>(declBase) )
        {
            return decl;
        }
        else if( auto declGroup = as<DeclGroup>(declBase) )
        {
            if( declGroup->decls.getCount() == 1 )
            {
                return declGroup->decls[0];
            }
        }

        parser->sink->diagnose(declBase->loc, Diagnostics::unimplemented, "didn't expect multiple declarations here");
        return nullptr;
    }


    static void parseDecls(
        Parser*             parser,
        ContainerDecl*      containerDecl,
        MatchedTokenType    matchType)
    {
        while(!AdvanceIfMatch(parser, matchType))
        {
            ParseDecl(parser, containerDecl);
        }
    }

    static void parseDeclBody(
        Parser*         parser,
        ContainerDecl*  parent)
    {
        parser->PushScope(parent);

        parser->ReadToken(TokenType::LBrace);
        parseDecls(parser, parent, MatchedTokenType::CurlyBraces);

        parser->PopScope();
    }


    void Parser::parseSourceFile(ModuleDecl* program)
    {
        if (outerScope)
        {
            currentScope = outerScope;
        }

        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())
        {
            program->loc = tokenReader.peekLoc();
        }

        parseDecls(this, program, MatchedTokenType::File);
        PopScope();

        SLANG_RELEASE_ASSERT(currentScope == outerScope);
        currentScope = nullptr;
    }

    Decl* Parser::ParseStruct()
    {
        StructDecl* rs = astBuilder->create<StructDecl>();
        FillPosition(rs);
        ReadToken("struct");

        // 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);
        }

        // TODO: support `struct` declaration without tag
        rs->nameAndLoc = expectIdentifier(this);

        return parseOptGenericDecl(this, [&](GenericDecl*)
        {
            // We allow for an inheritance clause on a `struct`
            // so that it can conform to interfaces.
            parseOptionalInheritanceClause(this, rs);
            parseDeclBody(this, rs);
            return rs;
        });
    }

    ClassDecl* Parser::ParseClass()
    {
        ClassDecl* rs = astBuilder->create<ClassDecl>();
        FillPosition(rs);
        ReadToken("class");
        rs->nameAndLoc = expectIdentifier(this);

        parseOptionalInheritanceClause(this, rs);

        parseDeclBody(this, rs);
        return rs;
    }

    static EnumCaseDecl* parseEnumCaseDecl(Parser* parser)
    {
        EnumCaseDecl* decl = parser->astBuilder->create<EnumCaseDecl>();
        parser->FillPosition(decl);
        
        decl->nameAndLoc = expectIdentifier(parser);

        if(AdvanceIf(parser, TokenType::OpAssign))
        {
            decl->tagExpr = parser->ParseArgExpr();
        }

        return decl;
    }

    static Decl* parseEnumDecl(Parser* parser)
    {
        EnumDecl* decl = parser->astBuilder->create<EnumDecl>();
        parser->FillPosition(decl);

        parser->ReadToken("enum");

        // 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.
        //
        AdvanceIf(parser, "class");

        decl->nameAndLoc = expectIdentifier(parser);


        return parseOptGenericDecl(parser, [&](GenericDecl*)
        {
            parseOptionalInheritanceClause(parser, decl);
            parser->ReadToken(TokenType::LBrace);

            while(!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces))
            {
                EnumCaseDecl* caseDecl = parseEnumCaseDecl(parser);
                AddMember(decl, caseDecl);

                if(AdvanceIf(parser, TokenType::RBrace))
                    break;

                parser->ReadToken(TokenType::Comma);
            }
            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* 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)
    {
        DefaultStmt* stmt = parser->astBuilder->create<DefaultStmt>();
        parser->FillPosition(stmt);
        parser->ReadToken("default");
        parser->ReadToken(TokenType::Colon);
        return stmt;
    }

    GpuForeachStmt* ParseGpuForeachStmt(Parser* parser)
    {
        // Hard-coding parsing of the following:
        // __GPU_FOREACH(renderer, gridDims, LAMBDA(uint3 dispatchThreadID) {
        //  kernelCall(args, ...); });

        // 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->FillPosition(stmt);
        parser->ReadToken("__GPU_FOREACH");
        parser->ReadToken(TokenType::LParent);
        stmt->device = parser->ParseArgExpr();
        parser->ReadToken(TokenType::Comma);
        stmt->gridDims = parser->ParseArgExpr();

        parser->ReadToken(TokenType::Comma);
        parser->ReadToken("LAMBDA");
        parser->ReadToken(TokenType::LParent);

        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::RParent);
        parser->ReadToken(TokenType::LBrace);

        parser->pushScopeAndSetParent(scopeDecl);
        AddMember(parser->currentScope, varDecl);

        stmt->kernelCall = parser->ParseExpression();

        parser->PopScope();

        parser->ReadToken(TokenType::Semicolon);
        parser->ReadToken(TokenType::RBrace);

        parser->ReadToken(TokenType::RParent);

        parser->ReadToken(TokenType::Semicolon);

        return stmt;
    }

    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))
        {
            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, SubstitutionSet());

                auto lookupResult = lookUpDirectAndTransparentMembers(
                    parser->astBuilder,
                    nullptr, // no semantics visitor available yet
                    staticMemberExpr->name,
                    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))
        {
            // Do the lookup in the current scope
            auto lookupResult = lookUp(
                parser->astBuilder,
                nullptr, // no semantics visitor available yet
                varExpr->name,
                parser->currentScope);
            if (!lookupResult.isValid() || lookupResult.isOverloaded())
                return nullptr;

            return lookupResult.item.declRef.getDecl();
        }

        return nullptr;
    }

    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;

        return _isType(lookupResult.item.declRef.getDecl());
    }

    static bool peekTypeName(Parser* parser)
    {
        if(!parser->LookAheadToken(TokenType::Identifier))
            return false;

        auto name = parser->tokenReader.peekToken().getName();
        return isTypeName(parser, name);
    }

    Stmt* parseCompileTimeForStmt(
        Parser* parser)
    {
        ScopeDecl* scopeDecl = parser->astBuilder->create<ScopeDecl>();
        CompileTimeForStmt* stmt = parser->astBuilder->create<CompileTimeForStmt>();
        stmt->scopeDecl = scopeDecl;


        parser->ReadToken("for");
        parser->ReadToken(TokenType::LParent);

        NameLoc varNameAndLoc = expectIdentifier(parser);
        VarDecl* varDecl = parser->astBuilder->create<VarDecl>();
        varDecl->nameAndLoc = varNameAndLoc;
        varDecl->loc = varNameAndLoc.loc;

        stmt->varDecl = varDecl;

        parser->ReadToken("in");
        parser->ReadToken("Range");
        parser->ReadToken(TokenType::LParent);

        Expr* rangeBeginExpr = nullptr;
        Expr* rangeEndExpr = parser->ParseArgExpr();
        if (AdvanceIf(parser, TokenType::Comma))
        {
            rangeBeginExpr = rangeEndExpr;
            rangeEndExpr = parser->ParseArgExpr();
        }

        stmt->rangeBeginExpr = rangeBeginExpr;
        stmt->rangeEndExpr = rangeEndExpr;

        parser->ReadToken(TokenType::RParent);
        parser->ReadToken(TokenType::RParent);

        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* Parser::ParseStatement()
    {
        auto modifiers = ParseModifiers(this);

        Stmt* statement = nullptr;
        if (LookAheadToken(TokenType::LBrace))
            statement = parseBlockStatement();
        else if (LookAheadToken("if"))
            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("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(TokenType::Identifier))
        {
            // 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();

            // 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.
            //
            Expr* type = ParseType();

            // 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;

            // 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 lookahead token is `*`, then we have to decide
            // whether to parse a pointer declarator or a multiply
            // expression. 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 (LookAheadToken(TokenType::Identifier))
            {
                // 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;
            }

            // Fallback: reset and parse an expression
            tokenReader.setCursor(startPos);
            statement = ParseExpressionStatement();
        }
        else if (LookAheadToken(TokenType::Semicolon))
        {
            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();
        }

        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;
        }

        return statement;
    }

    Stmt* Parser::parseBlockStatement()
    {
        if(!beginMatch(this, MatchedTokenType::CurlyBraces))
        {
            auto emptyStmt = astBuilder->create<EmptyStmt>();
            FillPosition(emptyStmt);
            return emptyStmt;
        }

        ScopeDecl* scopeDecl = astBuilder->create<ScopeDecl>();
        BlockStmt* blockStatement = astBuilder->create<BlockStmt>();
        blockStatement->scopeDecl = scopeDecl;
        pushScopeAndSetParent(scopeDecl);

        Stmt* body = nullptr;


        if(!tokenReader.isAtEnd())
        {
            FillPosition(blockStatement);
        }

        Token closingBraceToken;
        while (!AdvanceIfMatch(this, MatchedTokenType::CurlyBraces, &closingBraceToken))
        {
            auto stmt = ParseStatement();
            if(stmt)
            {
                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);

                    body = newBody;
                }
            }
            TryRecover(this);
        }
        PopScope();

        // Save the closing braces source loc
        blockStatement->closingSourceLoc = closingBraceToken.loc;

        if(!body)
        {
            body = astBuilder->create<EmptyStmt>();
            body->loc = blockStatement->loc;
        }

        blockStatement->body = body;
        return blockStatement;
    }

    DeclStmt* Parser::parseVarDeclrStatement(
        Modifiers modifiers)
    {
        DeclStmt*varDeclrStatement = astBuilder->create<DeclStmt>();

        FillPosition(varDeclrStatement);
        auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers);
        varDeclrStatement->decl = decl;
        return varDeclrStatement;
    }

    IfStmt* Parser::parseIfStatement()
    {
        IfStmt* ifStatement = astBuilder->create<IfStmt>();
        FillPosition(ifStatement);
        ReadToken("if");
        ReadToken(TokenType::LParent);
        ifStatement->predicate = ParseExpression();
        ReadToken(TokenType::RParent);
        ifStatement->positiveStatement = ParseStatement();
        if (LookAheadToken("else"))
        {
            ReadToken("else");
            ifStatement->negativeStatement = ParseStatement();
        }
        return ifStatement;
    }

    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;

        // 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>();
        }

        stmt->scopeDecl = scopeDecl;

        if(!brokenScoping)
            pushScopeAndSetParent(scopeDecl);
        FillPosition(stmt);
        ReadToken("for");
        ReadToken(TokenType::LParent);
        if (peekTypeName(this))
        {
            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();

        if (!brokenScoping)
            PopScope();

        return stmt;
    }

    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;
    }

    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;
    }

    BreakStmt* Parser::ParseBreakStatement()
    {
        BreakStmt* breakStatement = astBuilder->create<BreakStmt>();
        FillPosition(breakStatement);
        ReadToken("break");
        ReadToken(TokenType::Semicolon);
        return breakStatement;
    }

    ContinueStmt* Parser::ParseContinueStatement()
    {
        ContinueStmt* continueStatement = astBuilder->create<ContinueStmt>();
        FillPosition(continueStatement);
        ReadToken("continue");
        ReadToken(TokenType::Semicolon);
        return continueStatement;
    }

    ReturnStmt* Parser::ParseReturnStatement()
    {
        ReturnStmt* returnStatement = astBuilder->create<ReturnStmt>();
        FillPosition(returnStatement);
        ReadToken("return");
        if (!LookAheadToken(TokenType::Semicolon))
            returnStatement->expression = ParseExpression();
        ReadToken(TokenType::Semicolon);
        return returnStatement;
    }

    ExpressionStmt* Parser::ParseExpressionStatement()
    {
        ExpressionStmt* statement = astBuilder->create<ExpressionStmt>();

        FillPosition(statement);
        statement->expression = ParseExpression();

        ReadToken(TokenType::Semicolon);
        return statement;
    }

    ParamDecl* Parser::ParseParameter()
    {
        ParamDecl* parameter = astBuilder->create<ParamDecl>();
        parameter->modifiers = ParseModifiers(this);

        _parseTraditionalParamDeclCommonBase(this, parameter, kDeclaratorParseOption_AllowEmpty);

        return parameter;
    }

        /// 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 )
        {
            AddMember(parser->currentScope, typeSpec.decl);
        }
        return typeSpec.expr;
    }

        /// 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);
    }

    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.

            auto loc = peekToken(parser).loc;
            if(!AdvanceIf(parser, TokenType::OpBitAnd))
                break;

            auto rightExpr = _parsePostfixTypeExpr(parser);

            auto andExpr = parser->astBuilder->create<AndTypeExpr>();
            andExpr->loc = loc;
            andExpr->left = TypeExp(leftExpr);
            andExpr->right = TypeExp(rightExpr);
            leftExpr = andExpr;
        }

        return leftExpr;
    }

        /// Parse an infix type expression.
        ///
        /// Currently, the only infix type expression we support is the `&`
        /// operator for forming interface conjunctions.
        ///
    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, TokenType type)
    {
        switch(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 Precedence::Invalid;
        }
    }

    static Expr* parseOperator(Parser* parser)
    {
        Token opToken;
        switch(parser->tokenReader.peekTokenType())
        {
        case TokenType::QuestionMark:
            opToken = parser->ReadToken();
            opToken.setContent(UnownedStringSlice::fromLiteral("?:"));
            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;

        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* parseInfixExprWithPrecedence(
        Parser*                         parser,
        Expr*    inExpr,
        Precedence                      prec)
    {
        auto expr = inExpr;
        for(;;)
        {
            auto opTokenType = parser->tokenReader.peekTokenType();
            auto opPrec = GetOpLevel(parser, opTokenType);
            if(opPrec < prec)
                break;

            auto op = parseOperator(parser);

            // Special case the `?:` operator since it is the
            // one non-binary case we need to deal with.
            if(opTokenType == TokenType::QuestionMark)
            {
                SelectExpr* select = parser->astBuilder->create<SelectExpr>();
                select->loc = op->loc;
                select->functionExpr = op;

                select->arguments.add(expr);

                select->arguments.add(parser->ParseExpression(opPrec));
                parser->ReadToken(TokenType::Colon);
                select->arguments.add(parser->ParseExpression(opPrec));

                expr = select;
                continue;
            }

            auto right = parser->ParseLeafExpression();

            for(;;)
            {
                auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.peekTokenType());

                if((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) ? (nextOpPrec < opPrec) : (nextOpPrec <= opPrec))
                    break;

                right = parseInfixExprWithPrecedence(parser, right, nextOpPrec);
            }

            if (opTokenType == 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);
            }
        }
        return expr;
    }

    Expr* Parser::ParseExpression(Precedence level)
    {
        auto expr = ParseLeafExpression();
        return parseInfixExprWithPrecedence(this, expr, level);

#if 0

        if (level == Precedence::Prefix)
            return ParseLeafExpression();
        if (level == Precedence::TernaryConditional)
        {
            // parse select clause
            auto condition = ParseExpression(Precedence(level + 1));
            if (LookAheadToken(TokenType::QuestionMark))
            {
                SelectExpr* select = new SelectExpr();
                FillPosition(select.Ptr());

                select->Arguments.Add(condition);

                select->FunctionExpr = parseOperator(this);

                select->Arguments.Add(ParseExpression(level));
                ReadToken(TokenType::Colon);
                select->Arguments.Add(ParseExpression(level));
                return select;
            }
            else
                return condition;
        }
        else
        {
            if (GetAssociativityFromLevel(level) == Associativity::Left)
            {
                auto left = ParseExpression(Precedence(level + 1));
                while (GetOpLevel(this, tokenReader.PeekTokenType()) == level)
                {
                    OperatorExpr* tmp = new InfixExpr();
                    tmp->FunctionExpr = parseOperator(this);

                    tmp->Arguments.Add(left);
                    FillPosition(tmp.Ptr());
                    tmp->Arguments.Add(ParseExpression(Precedence(level + 1)));
                    left = tmp;
                }
                return left;
            }
            else
            {
                auto left = ParseExpression(Precedence(level + 1));
                if (GetOpLevel(this, tokenReader.PeekTokenType()) == level)
                {
                    OperatorExpr* tmp = new InfixExpr();
                    tmp->Arguments.Add(left);
                    FillPosition(tmp.Ptr());
                    tmp->FunctionExpr = parseOperator(this);
                    tmp->Arguments.Add(ParseExpression(level));
                    left = tmp;
                }
                return left;
            }
        }
#endif
    }

    // 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);
    }

    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;
    }

    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* parseFalseExpr(Parser* parser, void* /*userData*/)
    {
        return parseBoolLitExpr(parser, false);
    }

    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
    };

    static FloatFixKind _fixFloatLiteralValue(BaseType type, IRFloatingPointValue value, IRFloatingPointValue& outValue)
    {
        IRFloatingPointValue epsilon = 1e-10f;

        // Check the value is finite for checking narrowing to literal type losing information
        if (_isFinite(value))
        {
            switch (type)
            {
                case BaseType::Float:
                {
                    // Fix out of range
                    if (value > FLT_MAX)
                    {
                        if (Math::AreNearlyEqual(value, FLT_MAX, epsilon))
                        {
                            outValue = FLT_MAX;
                            return FloatFixKind::Truncated;
                        }
                        else
                        {
                            outValue = float(INFINITY);
                            return FloatFixKind::Unrepresentable;
                        }
                    }
                    else if (value < -FLT_MAX)
                    {
                        if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon))
                        {
                            outValue = -FLT_MAX;
                            return FloatFixKind::Truncated;
                        }
                        else
                        {
                            outValue = -float(INFINITY);
                            return FloatFixKind::Unrepresentable;
                        }
                    }
                    else if (value && float(value) == 0.0f)
                    {
                        outValue = 0.0f;
                        return FloatFixKind::Zeroed;
                    }
                    break;
                }
                case BaseType::Double:
                {
                    // All representable
                    break;
                }
                case BaseType::Half:
                {
                    // Fix out of range
                    if (value > SLANG_HALF_MAX)
                    {
                        if (Math::AreNearlyEqual(value, FLT_MAX, epsilon))
                        {
                            outValue = SLANG_HALF_MAX;
                            return FloatFixKind::Truncated;
                        }
                        else
                        {
                            outValue = float(INFINITY);
                            return FloatFixKind::Unrepresentable;
                        }
                    }
                    else if (value < -SLANG_HALF_MAX)
                    {
                        if (Math::AreNearlyEqual(-value, FLT_MAX, epsilon))
                        {
                            outValue = -SLANG_HALF_MAX;
                            return FloatFixKind::Truncated;
                        }
                        else
                        {
                            outValue = -float(INFINITY);
                            return FloatFixKind::Unrepresentable;
                        }
                    }
                    else if (value && Math::Abs(value) < SLANG_HALF_SUB_NORMAL_MIN)
                    {
                        outValue = 0.0f;
                        return FloatFixKind::Zeroed;
                    }
                    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'.

        const BaseTypeInfo& info = BaseTypeInfo::getInfo(baseType);
        SLANG_ASSERT(info.flags & BaseTypeInfo::Flag::Integer);
        SLANG_COMPILE_TIME_ASSERT(sizeof(value) == sizeof(uint64_t));

        // 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);

            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;
            }

            const IntegerLiteralValue maskedValue = 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);
            }

            value = truncatedValue;
        }

        return value;
    }

    static bool _isCast(Parser* parser, Expr* expr)
    {

        // 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);

        // 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)
        {
            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 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:
            {
                // + - 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.

                if (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);

                    // Rewind
                    parser->tokenReader.setCursor(cursor);

                    const bool isBinary = (nextToken.flags & TokenFlag::AfterWhitespace) == (opToken.flags & TokenFlag::AfterWhitespace);

                    // If it's binary it's not a cast
                    return !isBinary;
                }
                break;
            }
            default: break;
        }

        // We'll assume it's not a cast
        return false;
    }

    static Expr* parseAtomicExpr(Parser* parser)
    {
        switch( peekTokenType(parser) )
        {
        default:
            // TODO: should this return an error expression instead of NULL?
            parser->sink->diagnose(parser->tokenReader.peekLoc(), Diagnostics::syntaxError);
            return nullptr;

        // 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:
            {
                Token openParen = parser->ReadToken(TokenType::LParent);

                if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1))
                {
                    TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>();
                    parser->FillPosition(tcexpr);
                    tcexpr->functionExpr = parser->ParseType();
                    parser->ReadToken(TokenType::RParent);

                    auto arg = parsePrefixExpr(parser);
                    tcexpr->arguments.add(arg);

                    return tcexpr;
                }
                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 = parser->ParseExpression();

                    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 = 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;
                    }
                }
            }

        // An initializer list `{ expr, ... }`
        case TokenType::LBrace:
            {
                InitializerListExpr* initExpr = parser->astBuilder->create<InitializerListExpr>();
                parser->FillPosition(initExpr);

                // Initializer list
                parser->ReadToken(TokenType::LBrace);

                List<Expr*> exprs;

                for(;;)
                {
                    if(AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces))
                        break;

                    auto expr = parser->ParseArgExpr();
                    if( expr )
                    {
                        initExpr->args.add(expr);
                    }

                    if(AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces))
                        break;

                    parser->ReadToken(TokenType::Comma);
                }

                return initExpr;
            }

        case TokenType::IntegerLiteral:
            {
                IntegerLiteralExpr* constExpr = parser->astBuilder->create<IntegerLiteralExpr>();
                parser->FillPosition(constExpr);

                auto token = parser->tokenReader.advanceToken();
                constExpr->token = token;

                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();

                // 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;

                    int lCount = 0;
                    int uCount = 0;
                    int unknownCount = 0;
                    while(suffixCursor < suffixEnd)
                    {
                        switch( *suffixCursor++ )
                        {
                        case 'l': case 'L':
                            lCount++;
                            break;

                        case 'u': case 'U':
                            uCount++;
                            break;

                        default:
                            unknownCount++;
                            break;
                        }
                    }

                    if(unknownCount)
                    {
                        parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
                        suffixBaseType = BaseType::Void;
                    }
                    // `u` or `ul` suffix -> `uint`
                    else if(uCount == 1 && (lCount <= 1))
                    {
                        suffixBaseType = BaseType::UInt;
                    }
                    // `l` suffix on integer -> `int` (== `long`)
                    else if(lCount == 1 && !uCount)
                    {
                        suffixBaseType = BaseType::Int; 
                    }
                    // `ull` suffix -> `uint64_t`
                    else if(uCount == 1 && lCount == 2)
                    {
                        suffixBaseType = BaseType::UInt64;
                    }
                    // `ll` suffix -> `int64_t`
                    else if(uCount == 0 && lCount == 2)
                    {
                        suffixBaseType = BaseType::Int64;
                    }
                    // TODO: do we need suffixes for smaller integer types?
                    else
                    {
                        parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
                        suffixBaseType = BaseType::Void;
                    }
                }

                value = _fixIntegerLiteral(suffixBaseType, value, &token, parser->sink);

                ASTBuilder* astBuilder = parser->astBuilder;
                Type* suffixType = (suffixBaseType == BaseType::Void) ? astBuilder->getErrorType() : astBuilder->getBuiltinType(suffixBaseType);

                constExpr->value = value;
                constExpr->type = QualType(suffixType);

                return constExpr;
            }


        case TokenType::FloatingPointLiteral:
            {
                FloatingPointLiteralExpr* constExpr = parser->astBuilder->create<FloatingPointLiteralExpr>();
                parser->FillPosition(constExpr);

                auto token = parser->tokenReader.advanceToken();
                constExpr->token = token;

                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();

                // 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)
                    {
                        switch( *suffixCursor++ )
                        {
                        case 'f': case 'F':
                            fCount++;
                            break;

                        case 'l': case 'L':
                            lCount++;
                            break;

                        case 'h': case 'H':
                            hCount++;
                            break;

                        default:
                            unknownCount++;
                            break;
                        }
                    }

                    if (unknownCount)
                    {
                        parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix);
                        suffixBaseType = BaseType::Void;
                    }
                    // `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::Void;
                    }
                }

                // 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);

                switch (fixType)
                {
                    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;
                    }
                }

                ASTBuilder* astBuilder = parser->astBuilder;

                Type* suffixType = (suffixBaseType == BaseType::Void) ? astBuilder->getErrorType() : astBuilder->getBuiltinType(suffixBaseType);

                constExpr->value = fixedValue;
                constExpr->type = QualType(suffixType);

                return 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
                {
                    StringBuilder sb;
                    sb << getStringLiteralTokenValue(token);
                    while (parser->LookAheadToken(TokenType::StringLiteral))
                    {
                        token = parser->tokenReader.advanceToken();
                        sb << getStringLiteralTokenValue(token);
                    }
                    constExpr->value = sb.ProduceString();
                }

                return constExpr;
            }

        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))
                {
                    if (!parsedExpr->loc.isValid())
                    {
                        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);

                auto nameAndLoc = expectIdentifier(parser);
                varExpr->name = nameAndLoc.name;

                if(peekTokenType(parser) == TokenType::OpLess)
                {
                    return maybeParseGenericApp(parser, varExpr);
                }

                return varExpr;
            }
        }
    }

    static Expr* parsePostfixExpr(Parser* parser)
    {
        auto expr = parseAtomicExpr(parser);
        for(;;)
        {
            switch( peekTokenType(parser) )
            {
            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);

                    expr = postfixExpr;
                }
                break;

            // Subscript operation `a[i]`
            case TokenType::LBracket:
                {
                    IndexExpr* indexExpr = parser->astBuilder->create<IndexExpr>();
                    indexExpr->baseExpression = expr;
                    parser->FillPosition(indexExpr);
                    parser->ReadToken(TokenType::LBracket);
                    // TODO: eventually we may want to support multiple arguments inside the `[]`
                    if (!parser->LookAheadToken(TokenType::RBracket))
                    {
                        indexExpr->indexExpression = parser->ParseExpression();
                    }
                    parser->ReadToken(TokenType::RBracket);

                    expr = indexExpr;
                }
                break;

            // Call oepration `f(x)`
            case TokenType::LParent:
                {
                    InvokeExpr* invokeExpr = parser->astBuilder->create<InvokeExpr>();
                    invokeExpr->functionExpr = expr;
                    parser->FillPosition(invokeExpr);
                    parser->ReadToken(TokenType::LParent);
                    while (!parser->tokenReader.isAtEnd())
                    {
                        if (!parser->LookAheadToken(TokenType::RParent))
                            invokeExpr->arguments.add(parser->ParseArgExpr());
                        else
                        {
                            break;
                        }
                        if (!parser->LookAheadToken(TokenType::Comma))
                            break;
                        parser->ReadToken(TokenType::Comma);
                    }
                    parser->ReadToken(TokenType::RParent);

                    expr = invokeExpr;
                }
                break;

            // Scope access `x::m`
            case TokenType::Scope:
            {
                StaticMemberExpr* staticMemberExpr = parser->astBuilder->create<StaticMemberExpr>();

                // TODO(tfoley): why would a member expression need this?
                staticMemberExpr->scope = parser->currentScope;

                parser->FillPosition(staticMemberExpr);
                staticMemberExpr->baseExpression = expr;
                parser->ReadToken(TokenType::Scope);
                staticMemberExpr->name = expectIdentifier(parser).name;

                if (peekTokenType(parser) == TokenType::OpLess)
                    expr = maybeParseGenericApp(parser, staticMemberExpr);
                else
                    expr = staticMemberExpr;

                break;
            }
            // Member access `x.m`
            case TokenType::Dot:
                {
                    MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>();

                    // TODO(tfoley): why would a member expression need this?
                    memberExpr->scope = parser->currentScope;

                    parser->FillPosition(memberExpr);
                    memberExpr->baseExpression = expr;
                    parser->ReadToken(TokenType::Dot);
                    memberExpr->name = expectIdentifier(parser).name;

                    if (peekTokenType(parser) == TokenType::OpLess)
                        expr = maybeParseGenericApp(parser, memberExpr);
                    else
                        expr = memberExpr;
                }
                break;
            }
        }
    }

    static IRIntegerValue _foldIntegerPrefixOp(TokenType tokenType, IRIntegerValue value)
    {
        switch (tokenType)
        {
            case TokenType::OpBitNot:   return ~value;
            case TokenType::OpAdd:      return value;
            case TokenType::OpSub:      return -value;
            default:
            {
                SLANG_ASSERT(!"Unexpected op");
                return value;
            }
        }
    }

    static IRFloatingPointValue _foldFloatPrefixOp(TokenType tokenType, IRFloatingPointValue value)
    {
        switch (tokenType)
        {
            case TokenType::OpAdd:      return value;
            case TokenType::OpSub:      return -value;
            default:
            {
                SLANG_ASSERT(!"Unexpected op");
                return value;
            }
        }
    }

    static Expr* parsePrefixExpr(Parser* parser)
    {
        auto tokenType = peekTokenType(parser);
        switch( tokenType )
        {
        default:
            return parsePostfixExpr(parser);

        case TokenType::OpNot:
        case TokenType::OpInc:
        case TokenType::OpDec:
        {
            PrefixExpr* prefixExpr = parser->astBuilder->create<PrefixExpr>();
            parser->FillPosition(prefixExpr);
            prefixExpr->functionExpr = parseOperator(parser);

            auto arg = parsePrefixExpr(parser);

            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);

                if (auto intLit = as<IntegerLiteralExpr>(arg))
                {
                    IntegerLiteralExpr* newLiteral = parser->astBuilder->create<IntegerLiteralExpr>(*intLit);

                    IRIntegerValue value = _foldIntegerPrefixOp(tokenType, newLiteral->value);

                    // Need to get the basic type, so we can fit to underlying type
                    if (auto basicExprType = as<BasicExpressionType>(intLit->type.type))
                    {
                        value = _fixIntegerLiteral(basicExprType->baseType, value, nullptr, nullptr);
                    }

                    newLiteral->value = value;
                    return newLiteral;
                }
                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;
            }
        
            break;
        }
    }

    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)
        {
            // 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;

            typeExpr = parsePostfixTypeSuffix(parser, typeExpr);
            typeExpr = _parseInfixTypeExprSuffix(parser, typeExpr);

            return typeExpr;
        }

        return parser->ParseArgExpr();
    }

    Expr* parseTermFromSourceFile(
        ASTBuilder*                     astBuilder,
        TokenSpan const&                tokens,
        DiagnosticSink*                 sink,
        Scope*                          outerScope,
        NamePool*                       namePool,
        SourceLanguage                  sourceLanguage)
    {
        Parser parser(astBuilder, tokens, sink, outerScope);
        parser.currentScope = outerScope;
        parser.namePool = namePool;
        parser.sourceLanguage = sourceLanguage;
        return parser.ParseExpression();
    }

    // Parse a source file into an existing translation unit
    void parseSourceFile(
        ASTBuilder*                     astBuilder,
        TranslationUnitRequest*         translationUnit,
        TokenSpan const&                tokens,
        DiagnosticSink*                 sink,
        Scope*                          outerScope)
    {
        Parser parser(astBuilder, tokens, sink, outerScope);
        parser.namePool = translationUnit->getNamePool();
        parser.sourceLanguage = translationUnit->sourceLanguage;

        return parser.parseSourceFile(translationUnit->getModuleDecl());
    }

    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();

        SyntaxDecl* syntaxDecl = globalASTBuilder->create<SyntaxDecl>();
        syntaxDecl->nameAndLoc = NameLoc(name);
        syntaxDecl->syntaxClass = syntaxClass;
        syntaxDecl->parseCallback = callback;
        syntaxDecl->parseUserData = userData;

        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 addSimpleModifierSyntax(
        Session*        session,
        Scope*          scope,
        char const*     name)
    {
        auto syntaxClass = getClass<T>();
        addBuiltinSyntaxImpl(session, scope, name, &parseSimpleSyntax, (void*) syntaxClass.classInfo, getClass<T>());
    }

    static NodeBase* parseIntrinsicOpModifier(Parser* parser, void* /*userData*/)
    {
        IntrinsicOpModifier* modifier = parser->astBuilder->create<IntrinsicOpModifier>();

        // 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))
        {
            if (AdvanceIf(parser, TokenType::OpSub))
            {
                modifier->op = IROp(-StringToInt(parser->ReadToken().getContent()));
            }
            else if (parser->LookAheadToken(TokenType::IntegerLiteral))
            {
                modifier->op = IROp(StringToInt(parser->ReadToken().getContent()));
            }
            else
            {
                modifier->opToken = parser->ReadToken(TokenType::Identifier);

                modifier->op = findIROp(modifier->opToken.getContent());

                if (modifier->op == kIROp_Invalid)
                {
                    parser->sink->diagnose(modifier->opToken, Diagnostics::unimplemented, "unknown intrinsic op");
                }
            }

            parser->ReadToken(TokenType::RParent);
        }


        return modifier;
    }

    static NodeBase* parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = parser->astBuilder->create<TargetIntrinsicModifier>();

        if (AdvanceIf(parser, TokenType::LParent))
        {
            modifier->targetToken = parser->ReadToken(TokenType::Identifier);

            if( AdvanceIf(parser, TokenType::Comma) )
            {
                if( parser->LookAheadToken(TokenType::StringLiteral) )
                {
                    modifier->definitionToken = parser->ReadToken();
                }
                else
                {
                    modifier->definitionToken = parser->ReadToken(TokenType::Identifier);
                }
            }

            parser->ReadToken(TokenType::RParent);
        }

        return modifier;
    }

    static NodeBase* parseSpecializedForTargetModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = parser->astBuilder->create<SpecializedForTargetModifier>();
        if (AdvanceIf(parser, TokenType::LParent))
        {
            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>();

        parser->ReadToken(TokenType::LParent);
        modifier->extensionNameToken = parser->ReadToken(TokenType::Identifier);
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }

    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);

        return modifier;
    }


    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)
        {
            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;
            }
        }
        return SemanticVersion::parse(content, outVersion);
    }

    static NodeBase* parseSPIRVVersionModifier(Parser* parser, void* /*userData*/)
    {
        Token token;
        SemanticVersion version;
        if (SLANG_SUCCEEDED(parseSemanticVersion(parser, token, version)))
        {
            auto modifier = parser->astBuilder->create<RequiredSPIRVVersionModifier>();
            modifier->version = version;
            return modifier;
        }
        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)))
        {
            auto modifier = parser->astBuilder->create<RequiredCUDASMVersionModifier>();
            modifier->version = version;
            return modifier;
        }
        parser->sink->diagnose(token, Diagnostics::invalidCUDASMVersion);
        return nullptr;
    }

    static NodeBase* parseLayoutModifier(Parser* parser, void* /*userData*/)
    {
        ModifierListBuilder listBuilder;

        UncheckedAttribute* numThreadsAttrib = nullptr;

        listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupBegin>());
        
        parser->ReadToken(TokenType::LParent);
        while (!AdvanceIfMatch(parser, MatchedTokenType::Parentheses))
        {
            auto nameAndLoc = expectIdentifier(parser);
            const String& nameText = nameAndLoc.name->text;

            const char localSizePrefix[] = "local_size_";

            int localSizeIndex = -1;
            if (nameText.startsWith(localSizePrefix) && nameText.getLength() == SLANG_COUNT_OF(localSizePrefix) - 1 + 1)
            {
                char lastChar = nameText[SLANG_COUNT_OF(localSizePrefix) - 1];
                localSizeIndex = (lastChar >= 'x' && lastChar <= 'z') ? (lastChar - 'x') : -1;
            }

            if (localSizeIndex >= 0)
            {
                if (!numThreadsAttrib)
                {
                    numThreadsAttrib = parser->astBuilder->create<UncheckedAttribute>();
                    numThreadsAttrib->args.setCount(3);

                    // Just mark the loc and name from the first in the list
                    numThreadsAttrib->keywordName = getName(parser, "numthreads");
                    numThreadsAttrib->loc = nameAndLoc.loc;
                    numThreadsAttrib->scope = parser->currentScope;
                }

                if (AdvanceIf(parser, TokenType::OpAssign))
                {
                    auto expr = parseAtomicExpr(parser);
                    //SLANG_ASSERT(expr);
                    if (!expr)
                    {
                        return nullptr;
                    }

                    numThreadsAttrib->args[localSizeIndex] = expr;
                }
            }
            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);

                // 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 (nameText == "binding")
                    {
                        attr->binding = int32_t(value);
                    }
                    else
                    {
                        attr->set = int32_t(value);
                    }
                }
            }
            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(constant_id,   GLSLConstantIDLayoutModifier) 
                CASE(location, GLSLLocationLayoutModifier) 
                {
                    modifier = parser->astBuilder->create<GLSLUnparsedLayoutModifier>();
                }
                SLANG_ASSERT(modifier);
#undef CASE

                modifier->keywordName = nameAndLoc.name;
                modifier->loc = nameAndLoc.loc;

                // Special handling for GLSLLayoutModifier
                if (auto glslModifier = as<GLSLLayoutModifier>(modifier))
                {
                    if (AdvanceIf(parser, TokenType::OpAssign))
                    {
                        glslModifier->valToken = parser->ReadToken(TokenType::IntegerLiteral);
                    }
                }

                listBuilder.add(modifier);
            }

            if (AdvanceIf(parser, TokenType::RParent))
                break;
            parser->ReadToken(TokenType::Comma);
        }

        if (numThreadsAttrib)
        {
            listBuilder.add(numThreadsAttrib);
        }

        listBuilder.add(parser->astBuilder->create<GLSLLayoutModifierGroupEnd>());

        return listBuilder.getFirst();
    }

    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* 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))
        {
            modifier->tag = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent()));
        }
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }

    static NodeBase* parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/)
    {
        IntrinsicTypeModifier* modifier = parser->astBuilder->create<IntrinsicTypeModifier>();
        parser->ReadToken(TokenType::LParent);
        modifier->irOp = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent()));
        while( AdvanceIf(parser, TokenType::Comma) )
        {
            auto operand = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent()));
            modifier->irOperands.add(operand);
        }
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }
    static NodeBase* parseImplicitConversionModifier(Parser* parser, void* /*userData*/)
    {
        ImplicitConversionModifier* modifier = parser->astBuilder->create<ImplicitConversionModifier>();

        ConversionCost cost = kConversionCost_Default;
        if( AdvanceIf(parser, TokenType::LParent) )
        {
            cost = ConversionCost(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).getContent()));
            parser->ReadToken(TokenType::RParent);
        }
        modifier->cost = cost;
        return modifier;
    }

    static NodeBase* parseAttributeTargetModifier(Parser* parser, void* /*userData*/)
    {
        expect(parser, TokenType::LParent);
        auto syntaxClassNameAndLoc = expectIdentifier(parser);
        expect(parser, TokenType::RParent);

        auto syntaxClass = parser->astBuilder->findSyntaxClass(syntaxClassNameAndLoc.name);

        AttributeTargetModifier* modifier = parser->astBuilder->create<AttributeTargetModifier>();
        modifier->syntaxClass = syntaxClass;

        return modifier;
    }

    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("let",               parseLetDecl ),
        _makeParseDecl("var",               parseVarDecl ),
        _makeParseDecl("func",              parseFuncDecl ),
        _makeParseDecl("typealias",         parseTypeAliasDecl ),
        _makeParseDecl("__generic_value_param", parseGlobalGenericValueParamDecl ),
        _makeParseDecl("namespace",         parseNamespaceDecl ),
        _makeParseDecl("using",             parseUsingDecl ),

        // !!!!!!!!!!!!!!!!!!!!!! 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("input",         InputModifier::kReflectClassInfo),
        _makeParseModifier("out",           OutModifier::kReflectClassInfo),
        _makeParseModifier("inout",         InOutModifier::kReflectClassInfo),
        _makeParseModifier("__ref",         RefModifier::kReflectClassInfo),
        _makeParseModifier("const",         ConstModifier::kReflectClassInfo),
        _makeParseModifier("instance",      InstanceModifier::kReflectClassInfo),
        _makeParseModifier("__builtin",     BuiltinModifier::kReflectClassInfo),

        _makeParseModifier("inline",        InlineModifier::kReflectClassInfo),
        _makeParseModifier("public",        PublicModifier::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",      HLSLVolatileModifier::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 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("__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("__magic_type",          parseMagicTypeModifier),
        _makeParseModifier("__intrinsic_type",      parseIntrinsicTypeModifier),
        _makeParseModifier("__implicit_conversion", parseImplicitConversionModifier),

        _makeParseModifier("__attributeTarget",     parseAttributeTargetModifier),

        // !!!!!!!!!!!!!!!!!!!!!!! Expr !!!!!!!!!!!!!!!!!!!!!!!!!!!

        _makeParseExpr("this",  parseThisExpr),
        _makeParseExpr("This",  parseThisTypeExpr),
        _makeParseExpr("true",  parseTrueExpr),
        _makeParseExpr("false", parseFalseExpr),
        _makeParseExpr("__TaggedUnion", parseTaggedUnionType),
    };

    ConstArrayView<SyntaxParseInfo> getSyntaxParseInfos()
    {
        return makeConstArrayView(g_parseSyntaxEntries, SLANG_COUNT_OF(g_parseSyntaxEntries));
    }

    ModuleDecl* populateBaseLanguageModule(
        ASTBuilder*     astBuilder,
        Scope*          scope)
    {
        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;
    }

}
back to top