Raw File
parser.cpp
#include "parser.h"

#include <assert.h>

#include "compiler.h"
#include "lookup.h"
#include "visitor.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(modifier->As<SharedModifiers>() == 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 = cur->As<T>();
                if (castCur)
                {
                    return castCur;
                }
                cur = cur->next;
            }
            return nullptr;
        }
        template <typename T>
        bool hasType() const
        {
            return find<T>() != nullptr;
        }
        RefPtr<Modifier> getFirst() { return m_result; };
    protected:

        RefPtr<Modifier> m_result;
        RefPtr<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:
        TranslationUnitRequest* translationUnit;

        int anonymousCounter = 0;

        RefPtr<Scope> outerScope;
        RefPtr<Scope> currentScope;

        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)
        {
            RefPtr<Scope> newScope = new 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(
            TokenSpan const& _tokens,
            DiagnosticSink * sink,
            RefPtr<Scope> const& outerScope)
            : tokenReader(_tokens)
            , sink(sink)
            , outerScope(outerScope)
        {}
        Parser(const Parser & other) = default;
        Session* getSession()
        {
            return translationUnit->compileRequest->mSession;
        }

        RefPtr<ModuleDecl> Parse();

        Token ReadToken();
        Token ReadToken(TokenType type);
        Token ReadToken(const char * string);
        bool LookAheadToken(TokenType type, int offset = 0);
        bool LookAheadToken(const char * string, int offset = 0);
        void                                        parseSourceFile(ModuleDecl* program);
        RefPtr<ModuleDecl>					ParseProgram();
        RefPtr<Decl>					ParseStruct();
        RefPtr<ClassDecl>					    ParseClass();
        RefPtr<Stmt>					ParseStatement();
        RefPtr<Stmt>			        parseBlockStatement();
        RefPtr<DeclStmt>			parseVarDeclrStatement(Modifiers modifiers);
        RefPtr<IfStmt>				parseIfStatement();
        RefPtr<ForStmt>				ParseForStatement();
        RefPtr<WhileStmt>			ParseWhileStatement();
        RefPtr<DoWhileStmt>			ParseDoWhileStatement();
        RefPtr<BreakStmt>			ParseBreakStatement();
        RefPtr<ContinueStmt>			ParseContinueStatement();
        RefPtr<ReturnStmt>			ParseReturnStatement();
        RefPtr<ExpressionStmt>		ParseExpressionStatement();
        RefPtr<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 RefPtr<Expr>			ParseInitExpr() { return ParseExpression(Precedence::Assignment); }
        inline RefPtr<Expr>			ParseArgExpr()  { return ParseExpression(Precedence::Assignment); }

        RefPtr<Expr>				ParseLeafExpression();
        RefPtr<ParamDecl>					ParseParameter();
        RefPtr<Expr>				ParseType();
        TypeExp										ParseTypeExp();

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

    // Forward Declarations

    static void ParseDeclBody(
        Parser*						parser,
        ContainerDecl*				containerDecl,
        TokenType	                closingToken);

    static RefPtr<Decl> parseEnumDecl(Parser* parser);

    // Parse the `{}`-delimeted body of an aggregate type declaration
    static void parseAggTypeDeclBody(
        Parser*             parser,
        AggTypeDeclBase*    decl);

    static RefPtr<Modifier> ParseOptSemantics(
        Parser* parser);

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

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

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

    //

    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().Content == 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().Content == 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 (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;
            }
        }

        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().Content == string;
}

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

        return r.PeekTokenType() == type;
    }

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

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

    // 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, TokenType tokenType)
    {
        // If we've run into a syntax error, but haven't recovered inside
        // the block, then try to recover here.
        if (parser->isRecovering)
        {
            TryRecoverBefore(parser, tokenType);
        }
        if (AdvanceIf(parser, tokenType))
            return true;
        if (parser->tokenReader.PeekTokenType() == TokenType::EndOfFile)
        {
            parser->ReadToken(tokenType);
            return true;
        }
        return false;
    }

    RefPtr<ModuleDecl> Parser::Parse()
    {
        return ParseProgram();
    }

    RefPtr<RefObject> ParseTypeDef(Parser* parser, void* /*userData*/)
    {
        RefPtr<TypeDefDecl> typeDefDecl = new 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(RefPtr<Modifier>** ioModifierLink, RefPtr<Modifier> modifier)
    {
        RefPtr<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.
            RefPtr<Modifier> linkMod = *modifierLink;
            if(linkMod.As<SharedModifiers>())
            {
                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(
        RefPtr<ModifiableSyntaxNode>    syntax,
        RefPtr<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.Content);

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

        // Make a 'token'
        const String scopedIdentifier(scopedIdentifierBuilder.ToString());
        Token token(TokenType::Identifier, scopedIdentifier, scopedIdSourceLoc);
        token.ptrValue = getName(parser, token.Content);

        return token;
    }

    // Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers
    static void ParseSquareBracketAttributes(Parser* parser, RefPtr<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);

            RefPtr<UncheckedAttribute> modifier = new UncheckedAttribute();
            modifier->name = nameToken.getName();
            modifier->loc = nameToken.getLoc();
            modifier->scope = parser->currentScope;

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

                while (!AdvanceIfMatch(parser, TokenType::RParent))
                {
                    auto arg = parser->ParseArgExpr();
                    if (arg)
                    {
                        modifier->args.Add(arg);
                    }

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

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


            if (AdvanceIfMatch(parser, TokenType::RBracket))
                break;

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

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

    static TokenType peekTokenType(Parser* parser)
    {
        return parser->tokenReader.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->getSession(),
            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 = dynamic_cast<SyntaxDecl*>(decl) )
        {
            return syntaxDecl;
        }
        else
        {
            return nullptr;
        }
    }

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

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

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

        RefPtr<RefObject> parsedObject = syntaxDecl->parseCallback(parser, syntaxDecl->parseUserData);
        auto syntax = parsedObject.As<T>();

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

                    RefPtr<Modifier> parsedModifier;
                    if (tryParseUsingSyntaxDecl<Modifier>(parser, &parsedModifier))
                    {
                        parsedModifier->name = 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->translationUnit->compileRequest->getNamePool()->getName(text);
    }

    static NameLoc expectIdentifier(Parser* parser)
    {
        return NameLoc(parser->ReadToken(TokenType::Identifier));
    }


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

        auto decl = new 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).Content;
                }

                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))
                {
                    nameToken.Content = nameToken.Content + ":";
                    break;
                }
                ;       // fall-thru
            default:
                parser->sink->diagnose(nameToken.loc, Diagnostics::invalidOperator, nameToken);
                break;
            }

            return NameLoc(
                getName(parser, nameToken.Content),
                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
        RefPtr<Expr>	elementCountExpr;
    };

    // "Unwrapped" information about a declarator
    struct DeclaratorInfo
    {
        RefPtr<Expr>	    typeSpec;
        NameLoc             nameAndLoc;
        RefPtr<Modifier>	semantics;
        RefPtr<Expr>	    initializer;
    };

    // Add a member declaration to its container, and ensure that its
    // parent link is set up correctly.
    static void AddMember(RefPtr<ContainerDecl> container, RefPtr<Decl> member)
    {
        if (container)
        {
            member->ParentDecl = container.Ptr();
            container->Members.Add(member);

            container->memberDictionaryIsValid = false;
        }
    }

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

    static RefPtr<Decl> ParseGenericParamDecl(
        Parser*             parser,
        RefPtr<GenericDecl> genericDecl)
    {
        // simple syntax to introduce a value parameter
        if (AdvanceIf(parser, "let"))
        {
            // default case is a type parameter
            auto paramDecl = new 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
            RefPtr<GenericTypeParamDecl> paramDecl = new 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 = new GenericTypeConstraintDecl();
                parser->FillPosition(paramConstraint);

                auto paramType = DeclRefType::Create(
                    parser->getSession(),
                    DeclRef<Decl>(paramDecl, nullptr));

                auto paramTypeExpr = new SharedTypeExpr();
                paramTypeExpr->loc = paramDecl->loc;
                paramTypeExpr->base.type = paramType;
                paramTypeExpr->type = QualType(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++;
        while (!parser->LookAheadToken(TokenType::OpGreater))
        {
            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;
        }
    }

    static RefPtr<RefObject> ParseGenericDecl(Parser* parser, void*)
    {
        RefPtr<GenericDecl> decl = new GenericDecl();
        parser->FillPosition(decl.Ptr());
        parser->PushScope(decl.Ptr());
        ParseGenericDeclImpl(parser, decl.Ptr(), [=](GenericDecl* genDecl) {return ParseSingleDecl(parser, genDecl); });
        parser->PopScope();
        return decl;
    }

    static void parseParameterList(
        Parser*                 parser,
        RefPtr<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, TokenType::RParent))
        {
            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:
        RefPtr<Scope> scope;
        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*/)
        {}
    };

    static RefPtr<Decl> ParseFuncDeclHeader(
        Parser*                     parser,
        DeclaratorInfo const&       declaratorInfo,
        RefPtr<FuncDecl>  decl,
        RefPtr<GenericDecl> genDecl)
    {
        RefPtr<Decl> retDecl = decl;

        parser->FillPosition(decl.Ptr());
        decl->loc = declaratorInfo.nameAndLoc.loc;

        decl->nameAndLoc = declaratorInfo.nameAndLoc;

        // if return type is a DeclRef type, we need to update its scope to use this function decl's scope
        // so that LookUp can find the generic type parameters declared after the function name
        ReplaceScopeVisitor replaceScopeVisitor;
        replaceScopeVisitor.scope = parser->currentScope;
        declaratorInfo.typeSpec->accept(&replaceScopeVisitor, nullptr);

        decl->ReturnType = TypeExp(declaratorInfo.typeSpec);
        auto parseFuncDeclHeaderInner = [&](GenericDecl *)
        {
            parseParameterList(parser, decl);
            ParseOptSemantics(parser, decl.Ptr());
            return decl;
        };

        if (parser->LookAheadToken(TokenType::OpLess))
        {
            // parse generic parameters
            ParseGenericDeclImpl(parser, genDecl.Ptr(), parseFuncDeclHeaderInner);
            retDecl = genDecl;
        }
        else
            parseFuncDeclHeaderInner(nullptr);

        return retDecl;
    }

    static RefPtr<Decl> ParseFuncDecl(
        Parser*                 parser,
        ContainerDecl*          /*containerDecl*/,
        DeclaratorInfo const&   declaratorInfo,
        bool isGeneric)
    {
        RefPtr<FuncDecl> decl = new FuncDecl();
        RefPtr<Decl> retDecl = decl;
        RefPtr<GenericDecl> genDecl;
        if (isGeneric)
        {
            genDecl = new GenericDecl();
            parser->FillPosition(genDecl);
            parser->PushScope(genDecl);
            retDecl = genDecl;
        }
        parser->PushScope(decl.Ptr());
        ParseFuncDeclHeader(parser, declaratorInfo, decl, genDecl);

        if (AdvanceIf(parser, TokenType::Semicolon))
        {
            // empty body
        }
        else
        {
            decl->Body = parser->parseBlockStatement();
        }

        parser->PopScope();
        if (isGeneric)
        {
            parser->PopScope();
        }
        return retDecl;
    }

    static RefPtr<VarDeclBase> CreateVarDeclForContext(
        ContainerDecl*  containerDecl )
    {
        if (dynamic_cast<StructDecl*>(containerDecl) || dynamic_cast<ClassDecl*>(containerDecl))
        {
            return new StructField();
        }
        else if (dynamic_cast<CallableDecl*>(containerDecl))
        {
            return new ParamDecl();
        }
        else
        {
            return new Variable();
        }
    }

    // Add modifiers to the end of the modifier list for a declaration
    void AddModifiers(Decl* decl, RefPtr<Modifier> modifiers)
    {
        if (!modifiers)
            return;

        RefPtr<Modifier>* link = &decl->modifiers.first;
        while (*link)
        {
            link = &(*link)->next;
        }
        *link = modifiers;
    }

    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,
        RefPtr<VarDeclBase>		decl,
        DeclaratorInfo const&	declaratorInfo)
    {
        parser->FillPosition(decl.Ptr());

        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.Ptr(), declaratorInfo.semantics);

        decl->initExpr = declaratorInfo.initializer;
    }

    static RefPtr<Declarator> ParseDeclarator(Parser* parser);

    static RefPtr<Declarator> ParseDirectAbstractDeclarator(
        Parser* parser)
    {
        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.
                parser->ReadToken(TokenType::LParent);
                declarator = ParseDeclarator(parser);
                parser->ReadToken(TokenType::RParent);
            }
            break;

        default:
            // an empty declarator is allowed
            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)
    {
        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);
            return ptrDeclarator;
        }
        else
        {
            return ParseDirectAbstractDeclarator(parser);
        }
    }

    // A declarator plus optional semantics and initializer
    struct InitDeclarator
    {
        RefPtr<Declarator>				declarator;
        RefPtr<Modifier>				semantics;
        RefPtr<Expr>	initializer;
    };

    // Parse a declarator plus optional semantics
    static InitDeclarator ParseSemanticDeclarator(
        Parser* parser)
    {
        InitDeclarator result;
        result.declarator = ParseDeclarator(parser);
        result.semantics = ParseOptSemantics(parser);
        return result;
    }

    // Parse a declarator plus optional semantics and initializer
    static InitDeclarator ParseInitDeclarator(
        Parser* parser)
    {
        InitDeclarator result = ParseSemanticDeclarator(parser);
        if (AdvanceIf(parser, TokenType::OpAssign))
        {
            result.initializer = parser->ParseInitExpr();
        }
        return result;
    }

    static void UnwrapDeclarator(
        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 = new 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(
        InitDeclarator const&	initDeclarator,
        DeclaratorInfo*			ioInfo)
    {
        UnwrapDeclarator(initDeclarator.declarator, ioInfo);
        ioInfo->semantics = initDeclarator.semantics;
        ioInfo->initializer = initDeclarator.initializer;
    }

    // Either a single declaration, or a group of them
    struct DeclGroupBuilder
    {
        SourceLoc        startPosition;
        RefPtr<Decl>        decl;
        RefPtr<DeclGroup>   group;

        // Add a new declaration to the potential group
        void addDecl(
            RefPtr<Decl>    newDecl)
        {
            SLANG_ASSERT(newDecl);

            if( decl )
            {
                group = new DeclGroup();
                group->loc = startPosition;
                group->decls.Add(decl);
                decl = nullptr;
            }

            if( group )
            {
                group->decls.Add(newDecl);
            }
            else
            {
                decl = newDecl;
            }
        }

        RefPtr<DeclBase> getResult()
        {
            if(group) return group;
            return decl;
        }
    };

    // Pares an argument to an application of a generic
    RefPtr<Expr> ParseGenericArg(Parser* parser)
    {
        return parser->ParseArgExpr();
    }

    // Create a type expression that will refer to the given declaration
    static RefPtr<Expr>
    createDeclRefType(Parser* parser, RefPtr<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 = new VarExpr();
        expr->scope = parser->currentScope.Ptr();
        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
        RefPtr<Decl>                    decl;

        // Put the resulting expression (which should evaluate to a type) here
        RefPtr<Expr>    expr;
    };

    static RefPtr<Expr> parseGenericApp(
        Parser*                         parser,
        RefPtr<Expr>    base)
    {
        RefPtr<GenericAppExpr> genericApp = new GenericAppExpr();

        parser->FillPosition(genericApp.Ptr()); // 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--;

        // TODO: probably want to suppot `>>` and `>=` here as well,
        // and split up those tokens as needed.
        //
        parser->ReadToken(TokenType::OpGreater);

        return genericApp;
    }

    static bool isGenericName(Parser* parser, Name* name)
    {
        auto lookupResult = lookUp(
            parser->getSession(),
            nullptr, // no semantics visitor available yet
            name,
            parser->currentScope);
        if (!lookupResult.isValid() || lookupResult.isOverloaded())
            return false;

        auto decl = lookupResult.item.declRef.getDecl();
        if (auto genericDecl = dynamic_cast<GenericDecl*>(decl))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    static RefPtr<Expr> tryParseGenericApp(
        Parser*                         parser,
        RefPtr<Expr>    base)
    {
        Name * baseName = nullptr;
        if (auto varExpr = base->As<VarExpr>())
            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.mBegin = parser->tokenReader.mCursor;
        tokenSpan.mEnd = parser->tokenReader.mEnd;
        DiagnosticSink newSink;
        newSink.sourceManager = parser->sink->sourceManager;
        Parser newParser(*parser);
        newParser.sink = &newSink;
        auto speculateParseRs = parseGenericApp(&newParser, base);
        if (newSink.errorCount == 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 RefPtr<Expr> parseMemberType(Parser * parser, RefPtr<Expr> base)
    {
        RefPtr<MemberExpr> memberExpr = new MemberExpr();
        parser->ReadToken(TokenType::Dot);
        parser->FillPosition(memberExpr.Ptr());
        memberExpr->BaseExpression = base;
        memberExpr->name = expectIdentifier(parser).name;
        return memberExpr;
    }

    // Parse option `[]` braces after a type expression, that indicate an array type
    static RefPtr<Expr> parsePostfixTypeSuffix(
        Parser* parser,
        RefPtr<Expr> inTypeExpr)
    {
        auto typeExpr = inTypeExpr;
        while (parser->LookAheadToken(TokenType::LBracket))
        {
            RefPtr<IndexExpr> arrType = new 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 TypeSpec parseTypeSpec(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 specififers, as in:
        //
        //      struct Foo { int x } foo;
        //
        // The ideal answer would be to register certain keywords as being able
        // to parse a type specififer, 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 specififers 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;
        }

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

        auto basicType = new VarExpr();
        basicType->scope = parser->currentScope.Ptr();
        basicType->loc = typeName.loc;
        basicType->name = typeName.getNameOrNull();

        RefPtr<Expr> typeExpr = basicType;

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

        // GLSL allows `[]` directly in a type specifier
        if (parser->translationUnit->sourceLanguage == SourceLanguage::GLSL)
        {
            typeExpr = parsePostfixTypeSuffix(parser, typeExpr);
        }

        typeSpec.expr = typeExpr;
        return typeSpec;
    }

    static RefPtr<DeclBase> ParseDeclaratorDecl(
        Parser*         parser,
        ContainerDecl*  containerDecl)
    {
        SourceLoc startPosition = parser->tokenReader.PeekLoc();

        auto typeSpec = parseTypeSpec(parser);

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

        // 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->translationUnit->sourceLanguage == 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);

        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)
        {
            // Looks like a function, so parse it like one.
            UnwrapDeclarator(initDeclarator, &declaratorInfo);
            return ParseFuncDecl(parser, containerDecl, declaratorInfo, parser->tokenReader.PeekTokenType() == TokenType::OpLess);
        }

        // 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(initDeclarator, &declaratorInfo);
            RefPtr<VarDeclBase> firstDecl = CreateVarDeclForContext(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 = new SharedTypeExpr();
        sharedTypeSpec->loc = typeSpec.expr->loc;
        sharedTypeSpec->base = TypeExp(typeSpec.expr);

        for(;;)
        {
            declaratorInfo.typeSpec = sharedTypeSpec;
            UnwrapDeclarator(initDeclarator, &declaratorInfo);

            RefPtr<VarDeclBase> varDecl = CreateVarDeclForContext(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);
        }
    }

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

    //
    // semantic ::= identifier ( '(' args ')' )?
    //
    static RefPtr<Modifier> ParseSemantic(
        Parser* parser)
    {
        if (parser->LookAheadToken("register"))
        {
            RefPtr<HLSLRegisterSemantic> semantic = new HLSLRegisterSemantic();
            parser->FillPosition(semantic);
            parseHLSLRegisterSemantic(parser, semantic.Ptr());
            return semantic;
        }
        else if (parser->LookAheadToken("packoffset"))
        {
            RefPtr<HLSLPackOffsetSemantic> semantic = new HLSLPackOffsetSemantic();
            parser->FillPosition(semantic);
            parseHLSLPackOffsetSemantic(parser, semantic.Ptr());
            return semantic;
        }
        else if (parser->LookAheadToken(TokenType::Identifier))
        {
            RefPtr<HLSLSimpleSemantic> semantic = new 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 RefPtr<Modifier> ParseOptSemantics(
        Parser* parser)
    {
        if (!AdvanceIf(parser, TokenType::Colon))
            return nullptr;

        RefPtr<Modifier> result;
        RefPtr<Modifier>* link = &result;
        SLANG_ASSERT(!*link);

        for (;;)
        {
            RefPtr<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 result;
            }
        }

    }


    static void ParseOptSemantics(
        Parser* parser,
        Decl*	decl)
    {
        AddModifiers(decl, ParseOptSemantics(parser));
    }

    static RefPtr<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.
        RefPtr<StructDecl> bufferDataTypeDecl = new StructDecl();
        RefPtr<Variable> bufferVarDecl = new Variable();

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

        auto reflectionNameToken = parser->ReadToken(TokenType::Identifier);

        // Attach the reflection name to the block so we can use it
        auto reflectionNameModifier = new 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_" + reflectionNameToken.Content);
        bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content);

        addModifier(bufferDataTypeDecl, new ImplicitParameterGroupElementTypeModifier());
        addModifier(bufferVarDecl, new 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 = new VarExpr();
        bufferDataTypeExpr->loc = bufferDataTypeDecl->loc;
        bufferDataTypeExpr->name = bufferDataTypeDecl->nameAndLoc.name;
        bufferDataTypeExpr->scope = parser->currentScope.Ptr();

        // Construct a type exrpession to reference the type constructor
        auto bufferWrapperTypeExpr = new 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 = new GenericAppExpr();
        bufferVarTypeExpr->loc = bufferVarDecl->loc;
        bufferVarTypeExpr->FunctionExpr = bufferWrapperTypeExpr;
        bufferVarTypeExpr->Arguments.Add(bufferDataTypeExpr);

        bufferVarDecl->type.exp = bufferVarTypeExpr;

        // Any semantics applied to the bufer declaration are taken as applying
        // to the variable instead.
        ParseOptSemantics(parser, bufferVarDecl.Ptr());

        // The declarations in the body belong to the data type.
        parseAggTypeDeclBody(parser, bufferDataTypeDecl.Ptr());

        // 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 = new 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 RefPtr<RefObject> parseHLSLCBufferDecl(
        Parser*	parser, void* /*userData*/)
    {
        return ParseHLSLBufferDecl(parser, "ConstantBuffer");
    }

    static RefPtr<RefObject> parseHLSLTBufferDecl(
        Parser*	parser, void* /*userData*/)
    {
        return ParseHLSLBufferDecl(parser, "TextureBuffer");
    }

    static void removeModifier(
        Modifiers&          modifiers,
        RefPtr<Modifier>    modifier)
    {
        RefPtr<Modifier>* link = &modifiers.first;
        while (*link)
        {
            if (*link == modifier)
            {
                *link = (*link)->next;
                return;
            }

            link = &(*link)->next;
        }
    }

    static RefPtr<Decl> parseGLSLBlockDecl(
        Parser*	    parser,
        Modifiers&   modifiers)
    {
        // An GLSL block like this:
        //
        //     uniform Foo { int a; float b; } foo;
        //
        // is treated as syntax sugar for a type declaration
        // and then a global variable declaration using that type:
        //
        //     struct $anonymous { int a; float b; };
        //     Block<$anonymous> foo;
        //
        // where `$anonymous` is a fresh name.
        //
        // If a "local name" like `foo` is not given, then
        // we make the declaration "transparent" so that lookup
        // will see through it to the members inside.


        SourceLoc pos = parser->tokenReader.PeekLoc();

        // The initial name before the `{` is only supposed
        // to be made visible to reflection
        auto reflectionNameToken = parser->ReadToken(TokenType::Identifier);

        // Look at the qualifiers present on the block to decide what kind
        // of block we are looking at. Also *remove* those qualifiers so
        // that they don't interfere with downstream work.
        String blockWrapperTypeName;
        if( auto uniformMod = modifiers.findModifier<HLSLUniformModifier>() )
        {
            removeModifier(modifiers, uniformMod);
            blockWrapperTypeName = "ConstantBuffer";
        }
        else if( auto inMod = modifiers.findModifier<InModifier>() )
        {
            removeModifier(modifiers, inMod);
            blockWrapperTypeName = "__GLSLInputParameterGroup";
        }
        else if( auto outMod = modifiers.findModifier<OutModifier>() )
        {
            removeModifier(modifiers, outMod);
            blockWrapperTypeName = "__GLSLOutputParameterGroup";
        }
        else if( auto bufferMod = modifiers.findModifier<GLSLBufferModifier>() )
        {
            removeModifier(modifiers, bufferMod);
            blockWrapperTypeName = "__GLSLShaderStorageBuffer";
        }
        else
        {
            // Unknown case: just map to a constant buffer and hope for the best
            blockWrapperTypeName = "ConstantBuffer";
        }

        // 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.
        RefPtr<StructDecl> blockDataTypeDecl = new StructDecl();
        RefPtr<Variable> blockVarDecl = new Variable();

        addModifier(blockDataTypeDecl, new ImplicitParameterGroupElementTypeModifier());
        addModifier(blockVarDecl, new ImplicitParameterGroupVariableModifier());

        // Attach the reflection name to the block so we can use it
        auto reflectionNameModifier = new ParameterGroupReflectionName();
        reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken);
        addModifier(blockVarDecl, reflectionNameModifier);

        // Both declarations will have a location that points to the name
        parser->FillPosition(blockDataTypeDecl.Ptr());
        parser->FillPosition(blockVarDecl.Ptr());

        // Generate a unique name for the data type
        blockDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content);

        // 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 blockDataTypeExpr = new VarExpr();
        blockDataTypeExpr->loc = blockDataTypeDecl->loc;
        blockDataTypeExpr->name = blockDataTypeDecl->getName();
        blockDataTypeExpr->scope = parser->currentScope.Ptr();

        // Construct a type exrpession to reference the type constructor
        auto blockWrapperTypeExpr = new VarExpr();
        blockWrapperTypeExpr->loc = pos;
        blockWrapperTypeExpr->name = getName(parser, blockWrapperTypeName);
        // Always need to look this up in the outer scope,
        // so that it won't collide with, e.g., a local variable called `ConstantBuffer`
        blockWrapperTypeExpr->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 blockVarTypeExpr = new GenericAppExpr();
        blockVarTypeExpr->loc = blockVarDecl->loc;
        blockVarTypeExpr->FunctionExpr = blockWrapperTypeExpr;
        blockVarTypeExpr->Arguments.Add(blockDataTypeExpr);

        blockVarDecl->type.exp = blockVarTypeExpr;

        // The declarations in the body belong to the data type.
        parseAggTypeDeclBody(parser, blockDataTypeDecl.Ptr());

        if( parser->LookAheadToken(TokenType::Identifier) )
        {
            // The user gave an explicit name to the block,
            // so we need to use that as our variable name
            blockVarDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));

            // TODO: in this case we make actually have a more complex
            // declarator, including `[]` brackets.
        }
        else
        {
            // synthesize a dummy name
            blockVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + reflectionNameToken.Content);

            // Otherwise we have a transparent declaration, similar
            // to an HLSL `cbuffer`
            auto transparentModifier = new TransparentModifier();
            transparentModifier->loc = pos;
            addModifier(blockVarDecl, transparentModifier);
        }

        // Expect a trailing `;`
        parser->ReadToken(TokenType::Semicolon);

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

        return blockVarDecl;
    }

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

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

                AddMember(decl, inheritanceDecl);

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

    static RefPtr<RefObject> ParseExtensionDecl(Parser* parser, void* /*userData*/)
    {
        RefPtr<ExtensionDecl> decl = new ExtensionDecl();
        parser->FillPosition(decl.Ptr());
        decl->targetType = parser->ParseTypeExp();
        parseOptionalInheritanceClause(parser, decl);
        parseAggTypeDeclBody(parser, decl.Ptr());

        return decl;
    }


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

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

                RefPtr<SharedTypeExpr> paramTypeExpr = new SharedTypeExpr();
                paramTypeExpr->loc = decl->loc;
                paramTypeExpr->base.type = paramType;
                paramTypeExpr->type = QualType(getTypeType(paramType));

                paramConstraint->sub = TypeExp(paramTypeExpr);
                paramConstraint->sup = parser->ParseTypeExp();

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

    RefPtr<RefObject> parseAssocType(Parser * parser, void *)
    {
        RefPtr<AssocTypeDecl> assocTypeDecl = new AssocTypeDecl();

        auto nameToken = parser->ReadToken(TokenType::Identifier);
        assocTypeDecl->nameAndLoc = NameLoc(nameToken);
        assocTypeDecl->loc = nameToken.loc;
        parseOptionalGenericConstraints(parser, assocTypeDecl);
        parser->ReadToken(TokenType::Semicolon);
        return assocTypeDecl;
    }

    RefPtr<RefObject> parseGlobalGenericParamDecl(Parser * parser, void *)
    {
        RefPtr<GlobalGenericParamDecl> genParamDecl = new 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 RefPtr<RefObject> parseInterfaceDecl(Parser* parser, void* /*userData*/)
    {
        RefPtr<InterfaceDecl> decl = new InterfaceDecl();
        parser->FillPosition(decl.Ptr());
        decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));

        parseOptionalInheritanceClause(parser, decl.Ptr());

        parseAggTypeDeclBody(parser, decl.Ptr());

        return decl;
    }

    static RefPtr<RefObject> parseConstructorDecl(Parser* parser, void* /*userData*/)
    {
        RefPtr<ConstructorDecl> decl = new ConstructorDecl();
        parser->FillPosition(decl.Ptr());

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

        if( AdvanceIf(parser, TokenType::Semicolon) )
        {
            // empty body
        }
        else
        {
            decl->Body = parser->parseBlockStatement();
        }
        return decl;
    }

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

        RefPtr<AccessorDecl> decl;
        if( AdvanceIf(parser, "get") )
        {
            decl = new GetterDecl();
        }
        else if( AdvanceIf(parser, "set") )
        {
            decl = new SetterDecl();
        }
        else if( AdvanceIf(parser, "ref") )
        {
            decl = new RefAccessorDecl();
        }
        else
        {
            Unexpected(parser);
            return nullptr;
        }

        AddModifiers(decl, modifiers.first);

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

        return decl;
    }

    static RefPtr<RefObject> ParseSubscriptDecl(Parser* parser, void* /*userData*/)
    {
        RefPtr<SubscriptDecl> decl = new SubscriptDecl();
        parser->FillPosition(decl.Ptr());

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

        if( AdvanceIf(parser, TokenType::LBrace) )
        {
            // We want to parse nested "accessor" declarations
            while( !AdvanceIfMatch(parser, TokenType::RBrace) )
            {
                auto accessor = parseAccessorDecl(parser);
                AddMember(decl, accessor);
            }
        }
        else
        {
            parser->ReadToken(TokenType::Semicolon);

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

        return decl;
    }

    static Token expect(Parser* parser, TokenType tokenType)
    {
        return parser->ReadToken(tokenType);
    }

    // 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.
    static RefPtr<RefObject> parseSimpleSyntax(Parser* /*parser*/, void* userData)
    {
        SyntaxClassBase syntaxClass((SyntaxClassBase::ClassInfo*) userData);
        return (RefObject*) syntaxClass.createInstanceImpl();
    }

    // Parse a declaration of a keyword that can be used to define further syntax.
    static RefPtr<RefObject> 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<RefObject> syntaxClass;
        if (AdvanceIf(parser, TokenType::Colon))
        {
            // User is specifying the class that should be construted
            auto classNameAndLoc = expectIdentifier(parser);

            syntaxClass = parser->getSession()->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?

        RefPtr<SyntaxDecl> syntaxDecl = new 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 RefPtr<ParamDecl> parseAttributeParamDecl(Parser* parser)
    {
        auto nameAndLoc = expectIdentifier(parser);

        RefPtr<ParamDecl> paramDecl = new 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 RefPtr<RefObject> 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);

        RefPtr<AttributeDecl> attrDecl = new AttributeDecl();
        if(AdvanceIf(parser, TokenType::LParent))
        {
            while(!AdvanceIfMatch(parser, TokenType::RParent))
            {
                auto param = parseAttributeParamDecl(parser);

                AddMember(attrDecl, param);

                if(AdvanceIfMatch(parser, TokenType::RParent))
                    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<RefObject> syntaxClass;
        if (AdvanceIf(parser, TokenType::Colon))
        {
            // User is specifying the class that should be construted
            auto classNameAndLoc = expectIdentifier(parser);

            syntaxClass = parser->getSession()->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*/,
        RefPtr<Decl>		decl,
        ContainerDecl*		containerDecl,
        Modifiers			modifiers)
    {
        // Add any modifiers we parsed before the declaration to the list
        // of modifiers on the declaration itself.
        AddModifiers(decl.Ptr(), modifiers.first);

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

    static RefPtr<DeclBase> ParseDeclWithModifiers(
        Parser*             parser,
        ContainerDecl*      containerDecl,
        Modifiers			modifiers )
    {
        RefPtr<DeclBase> decl;

        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 begining of a type in a declarator-based declaration (e.g., `int ...`)
                // - A GLSL block declaration (e.g., `uniform Foo { ... }`)

                // Let's deal with the GLSL block case first. This is something like:
                //
                //     uniform Foo { ... };
                //
                // The `uniform` keyword has already been parsed as a modifier,
                // so the identifier we are looking at is `Foo`. If the token
                // after that is `{`, we assume this is a block.
                //
                // Of course, we only want to allow this syntax when parsing GLSL...
                if (parser->translationUnit->sourceLanguage == SourceLanguage::GLSL)
                {
                    if( parser->LookAheadToken(TokenType::LBrace, 1) )
                    {
                        decl = parseGLSLBlockDecl(parser, modifiers);
                        break;
                    }
                }

                // Next we will check whether we can use the identifier token
                // as a declaration keyword and parse a declaration using
                // its associated callback:
                RefPtr<Decl> parsedDecl;
                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);
                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 = new EmptyDecl();
                decl->loc = loc;
            }
            break;

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

        if (decl)
        {
            if( auto dd = decl.As<Decl>() )
            {
                CompleteDecl(parser, dd, containerDecl, modifiers);
            }
            else if(auto declGroup = decl.As<DeclGroup>())
            {
                // 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 = new SharedModifiers();
                sharedModifiers->next = modifiers.first;
                modifiers.first = sharedModifiers;

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

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

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

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


    // Parse a body consisting of declarations
    static void ParseDeclBody(
        Parser*         parser,
        ContainerDecl*  containerDecl,
        TokenType       closingToken)
    {
        while(!AdvanceIfMatch(parser, closingToken))
        {
            ParseDecl(parser, containerDecl);
        }
    }

    // Parse the `{}`-delimeted body of an aggregate type declaration
    static void parseAggTypeDeclBody(
        Parser*             parser,
        AggTypeDeclBase*    decl)
    {
        // TODO: the scope used for the body might need to be
        // slightly specialized to deal with the complexity
        // of how `this` works.
        //
        // Alternatively, that complexity can be pushed down
        // to semantic analysis so that it doesn't clutter
        // things here.
        parser->PushScope(decl);

        parser->ReadToken(TokenType::LBrace);
        ParseDeclBody(parser, decl, TokenType::RBrace);

        parser->PopScope();
    }


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

        PushScope(program);
        program->loc = tokenReader.PeekLoc();
        program->scope = currentScope;
        ParseDeclBody(this, program, TokenType::EndOfFile);
        PopScope();

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

    RefPtr<ModuleDecl> Parser::ParseProgram()
    {
        RefPtr<ModuleDecl> program = new ModuleDecl();

        parseSourceFile(program.Ptr());

        return program;
    }

    RefPtr<Decl> Parser::ParseStruct()
    {
        RefPtr<StructDecl> rs = new StructDecl();
        RefPtr<Decl> retDecl = rs;
        FillPosition(rs.Ptr());
        ReadToken("struct");

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

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

        if (LookAheadToken(TokenType::OpLess))
        {
            RefPtr<GenericDecl> genDecl = new GenericDecl();
            FillPosition(genDecl.Ptr());
            PushScope(genDecl);
            ParseGenericDeclImpl(this, genDecl.Ptr(), parseStructInner);
            PopScope();
            retDecl = genDecl;
        }
        else
        {
            parseStructInner(nullptr);
        }
        return retDecl;
    }

    RefPtr<ClassDecl> Parser::ParseClass()
    {
        RefPtr<ClassDecl> rs = new ClassDecl();
        FillPosition(rs.Ptr());
        ReadToken("class");
        rs->nameAndLoc = expectIdentifier(this);
        ReadToken(TokenType::LBrace);
        parseOptionalInheritanceClause(this, rs.Ptr());
        parseAggTypeDeclBody(this, rs.Ptr());
        return rs;
    }

    static RefPtr<EnumCaseDecl> parseEnumCaseDecl(Parser* parser)
    {
        RefPtr<EnumCaseDecl> decl = new EnumCaseDecl();
        decl->nameAndLoc = expectIdentifier(parser);

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

        return decl;
    }

    static RefPtr<Decl> parseEnumDecl(Parser* parser)
    {
        RefPtr<EnumDecl> decl = new 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);

        auto parseEnumDeclInner = [&](GenericDecl*)
        {
            parseOptionalInheritanceClause(parser, decl);
            parser->ReadToken(TokenType::LBrace);

            while(!AdvanceIfMatch(parser, TokenType::RBrace))
            {
                RefPtr<EnumCaseDecl> caseDecl = parseEnumCaseDecl(parser);
                AddMember(decl, caseDecl);

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

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

        if (parser->LookAheadToken(TokenType::OpLess))
        {
            RefPtr<GenericDecl> genericDecl = new GenericDecl();
            parser->FillPosition(genericDecl);
            parser->PushScope(genericDecl);
            ParseGenericDeclImpl(parser, genericDecl, parseEnumDeclInner);
            parser->PopScope();
            return genericDecl;
        }
        else
        {
            parseEnumDeclInner(nullptr);
        }

        return decl;
    }

    static RefPtr<Stmt> ParseSwitchStmt(Parser* parser)
    {
        RefPtr<SwitchStmt> stmt = new SwitchStmt();
        parser->FillPosition(stmt.Ptr());
        parser->ReadToken("switch");
        parser->ReadToken(TokenType::LParent);
        stmt->condition = parser->ParseExpression();
        parser->ReadToken(TokenType::RParent);
        stmt->body = parser->parseBlockStatement();
        return stmt;
    }

    static RefPtr<Stmt> ParseCaseStmt(Parser* parser)
    {
        RefPtr<CaseStmt> stmt = new CaseStmt();
        parser->FillPosition(stmt.Ptr());
        parser->ReadToken("case");
        stmt->expr = parser->ParseExpression();
        parser->ReadToken(TokenType::Colon);
        return stmt;
    }

    static RefPtr<Stmt> ParseDefaultStmt(Parser* parser)
    {
        RefPtr<DefaultStmt> stmt = new DefaultStmt();
        parser->FillPosition(stmt.Ptr());
        parser->ReadToken("default");
        parser->ReadToken(TokenType::Colon);
        return stmt;
    }

    static bool isTypeName(Parser* parser, Name* name)
    {
        auto lookupResult = lookUp(
            parser->getSession(),
            nullptr, // no semantics visitor available yet
            name,
            parser->currentScope);
        if(!lookupResult.isValid() || lookupResult.isOverloaded())
            return false;

        auto decl = lookupResult.item.declRef.getDecl();
        if( auto typeDecl = dynamic_cast<AggTypeDecl*>(decl) )
        {
            return true;
        }
        else if( auto typeVarDecl = dynamic_cast<SimpleTypeDecl*>(decl) )
        {
            return true;
        }
        else
        {
            return false;
        }
    }

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

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

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


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

        NameLoc varNameAndLoc = expectIdentifier(parser);
        RefPtr<Variable> varDecl = new Variable();
        varDecl->nameAndLoc = varNameAndLoc;
        varDecl->loc = varNameAndLoc.loc;

        stmt->varDecl = varDecl;

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

        RefPtr<Expr> rangeBeginExpr;
        RefPtr<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;
    }

    RefPtr<Stmt> parseCompileTimeStmt(
        Parser* parser)
    {
        parser->ReadToken(TokenType::Dollar);
        if (parser->LookAheadToken("for"))
        {
            return parseCompileTimeForStmt(parser);
        }
        else
        {
            Unexpected(parser);
            return nullptr;
        }
    }

    RefPtr<Stmt> Parser::ParseStatement()
    {
        auto modifiers = ParseModifiers(this);

        RefPtr<Stmt> statement;
        if (LookAheadToken(TokenType::LBrace))
            statement = parseBlockStatement();
        else if (peekTypeName(this))
            statement = parseVarDeclrStatement(modifiers);
        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 = new DiscardStmt();
            FillPosition(statement.Ptr());
            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(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.

            Token* startPos = tokenReader.mCursor;

            // Try to parse a type (knowing that the type grammar is
            // a subset of the expression grammar, and so this should
            // always succeed).
            RefPtr<Expr> type = ParseType();
            // We don't actually care about the type, though, so
            // don't retain it
            type = nullptr;

            // 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 (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.mCursor = startPos;
                statement = parseVarDeclrStatement(modifiers);
                return statement;
            }

            // Fallback: reset and parse an expression
            tokenReader.mCursor = startPos;
            statement = ParseExpressionStatement();
        }
        else if (LookAheadToken(TokenType::Semicolon))
        {
            statement = new EmptyStmt();
            FillPosition(statement.Ptr());
            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 && !statement->As<DeclStmt>())
        {
            // 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;
    }

    RefPtr<Stmt> Parser::parseBlockStatement()
    {
        RefPtr<ScopeDecl> scopeDecl = new ScopeDecl();
        RefPtr<BlockStmt> blockStatement = new BlockStmt();
        blockStatement->scopeDecl = scopeDecl;
        pushScopeAndSetParent(scopeDecl.Ptr());
        ReadToken(TokenType::LBrace);

        RefPtr<Stmt> body;

        if(!tokenReader.IsAtEnd())
        {
            FillPosition(blockStatement.Ptr());
        }
        while (!AdvanceIfMatch(this, TokenType::RBrace))
        {
            auto stmt = ParseStatement();
            if(stmt)
            {
                if (!body)
                {
                    body = stmt;
                }
                else if (auto seqStmt = body.As<SeqStmt>())
                {
                    seqStmt->stmts.Add(stmt);
                }
                else
                {
                    RefPtr<SeqStmt> newBody = new SeqStmt();
                    newBody->loc = blockStatement->loc;
                    newBody->stmts.Add(body);
                    newBody->stmts.Add(stmt);

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

        if(!body)
        {
            body = new EmptyStmt();
            body->loc = blockStatement->loc;
        }

        blockStatement->body = body;
        return blockStatement;
    }

    RefPtr<DeclStmt> Parser::parseVarDeclrStatement(
        Modifiers modifiers)
    {
        RefPtr<DeclStmt>varDeclrStatement = new DeclStmt();

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

    RefPtr<IfStmt> Parser::parseIfStatement()
    {
        RefPtr<IfStmt> ifStatement = new IfStmt();
        FillPosition(ifStatement.Ptr());
        ReadToken("if");
        ReadToken(TokenType::LParent);
        ifStatement->Predicate = ParseExpression();
        ReadToken(TokenType::RParent);
        ifStatement->PositiveStatement = ParseStatement();
        if (LookAheadToken("else"))
        {
            ReadToken("else");
            ifStatement->NegativeStatement = ParseStatement();
        }
        return ifStatement;
    }

    RefPtr<ForStmt> Parser::ParseForStatement()
    {
        RefPtr<ScopeDecl> scopeDecl = new 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 = translationUnit->sourceLanguage == 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.
        //
        RefPtr<ForStmt> stmt;
        if (brokenScoping)
        {
            stmt = new UnscopedForStmt();
        }
        else
        {
            stmt = new ForStmt();
        }

        stmt->scopeDecl = scopeDecl;

        if(!brokenScoping)
            pushScopeAndSetParent(scopeDecl.Ptr());
        FillPosition(stmt.Ptr());
        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;
    }

    RefPtr<WhileStmt> Parser::ParseWhileStatement()
    {
        RefPtr<WhileStmt> whileStatement = new WhileStmt();
        FillPosition(whileStatement.Ptr());
        ReadToken("while");
        ReadToken(TokenType::LParent);
        whileStatement->Predicate = ParseExpression();
        ReadToken(TokenType::RParent);
        whileStatement->Statement = ParseStatement();
        return whileStatement;
    }

    RefPtr<DoWhileStmt> Parser::ParseDoWhileStatement()
    {
        RefPtr<DoWhileStmt> doWhileStatement = new DoWhileStmt();
        FillPosition(doWhileStatement.Ptr());
        ReadToken("do");
        doWhileStatement->Statement = ParseStatement();
        ReadToken("while");
        ReadToken(TokenType::LParent);
        doWhileStatement->Predicate = ParseExpression();
        ReadToken(TokenType::RParent);
        ReadToken(TokenType::Semicolon);
        return doWhileStatement;
    }

    RefPtr<BreakStmt> Parser::ParseBreakStatement()
    {
        RefPtr<BreakStmt> breakStatement = new BreakStmt();
        FillPosition(breakStatement.Ptr());
        ReadToken("break");
        ReadToken(TokenType::Semicolon);
        return breakStatement;
    }

    RefPtr<ContinueStmt> Parser::ParseContinueStatement()
    {
        RefPtr<ContinueStmt> continueStatement = new ContinueStmt();
        FillPosition(continueStatement.Ptr());
        ReadToken("continue");
        ReadToken(TokenType::Semicolon);
        return continueStatement;
    }

    RefPtr<ReturnStmt> Parser::ParseReturnStatement()
    {
        RefPtr<ReturnStmt> returnStatement = new ReturnStmt();
        FillPosition(returnStatement.Ptr());
        ReadToken("return");
        if (!LookAheadToken(TokenType::Semicolon))
            returnStatement->Expression = ParseExpression();
        ReadToken(TokenType::Semicolon);
        return returnStatement;
    }

    RefPtr<ExpressionStmt> Parser::ParseExpressionStatement()
    {
        RefPtr<ExpressionStmt> statement = new ExpressionStmt();

        FillPosition(statement.Ptr());
        statement->Expression = ParseExpression();

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

    RefPtr<ParamDecl> Parser::ParseParameter()
    {
        RefPtr<ParamDecl> parameter = new ParamDecl();
        parameter->modifiers = ParseModifiers(this);

        DeclaratorInfo declaratorInfo;
        declaratorInfo.typeSpec = ParseType();

        InitDeclarator initDeclarator = ParseInitDeclarator(this);
        UnwrapDeclarator(initDeclarator, &declaratorInfo);

        // Assume it is a variable-like declarator
        CompleteVarDecl(this, parameter, declaratorInfo);
        return parameter;
    }

    RefPtr<Expr> Parser::ParseType()
    {
        auto typeSpec = parseTypeSpec(this);
        if( typeSpec.decl )
        {
            AddMember(currentScope, typeSpec.decl);
        }
        auto typeExpr = typeSpec.expr;

        typeExpr = parsePostfixTypeSuffix(this, typeExpr);

        return typeExpr;
    }



    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 RefPtr<Expr> parseOperator(Parser* parser)
    {
        Token opToken;
        switch(parser->tokenReader.PeekTokenType())
        {
        case TokenType::QuestionMark:
            opToken = parser->ReadToken();
            opToken.Content = "?:";
            break;

        default:
            opToken = parser->ReadToken();
            break;
        }

        auto opExpr = new VarExpr();
        opExpr->name = getName(parser, opToken.Content);
        opExpr->scope = parser->currentScope;
        opExpr->loc = opToken.loc;

        return opExpr;

    }

    static RefPtr<Expr> createInfixExpr(
        Parser*                         /*parser*/,
        RefPtr<Expr>    left,
        RefPtr<Expr>    op,
        RefPtr<Expr>    right)
    {
        RefPtr<InfixExpr> expr = new InfixExpr();
        expr->loc = op->loc;
        expr->FunctionExpr = op;
        expr->Arguments.Add(left);
        expr->Arguments.Add(right);
        return expr;
    }

    static RefPtr<Expr> parseInfixExprWithPrecedence(
        Parser*                         parser,
        RefPtr<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)
            {
                RefPtr<SelectExpr> select = new 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)
            {
                RefPtr<AssignExpr> assignExpr = new AssignExpr();
                assignExpr->loc = op->loc;
                assignExpr->left = expr;
                assignExpr->right = right;

                expr = assignExpr;
            }
            else
            {
                expr = createInfixExpr(parser, expr, op, right);
            }
        }
        return expr;
    }

    RefPtr<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))
            {
                RefPtr<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)
                {
                    RefPtr<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)
                {
                    RefPtr<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 RefPtr<Expr> maybeParseGenericApp(
        Parser*                             parser,

        // TODO: need to support more general expressions here
        RefPtr<Expr>     base)
    {
        if(peekTokenType(parser) != TokenType::OpLess)
            return base;
        return tryParseGenericApp(parser, base);
    }

    static RefPtr<Expr> parsePrefixExpr(Parser* parser);

    // Parse OOP `this` expression syntax
    static RefPtr<RefObject> parseThisExpr(Parser* parser, void* /*userData*/)
    {
        RefPtr<ThisExpr> expr = new ThisExpr();
        expr->scope = parser->currentScope;
        return expr;
    }

    static RefPtr<Expr> parseBoolLitExpr(Parser* /*parser*/, bool value)
    {
        RefPtr<BoolLiteralExpr> expr = new BoolLiteralExpr();
        expr->value = value;
        return expr;
    }

    static RefPtr<RefObject> parseTrueExpr(Parser* parser, void* /*userData*/)
    {
        return parseBoolLitExpr(parser, true);
    }

    static RefPtr<RefObject> parseFalseExpr(Parser* parser, void* /*userData*/)
    {
        return parseBoolLitExpr(parser, false);
    }

    static RefPtr<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:
        // - parenthized 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 hueristics.
        case TokenType::LParent:
            {
                Token openParen = parser->ReadToken(TokenType::LParent);

                if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1))
                {
                    RefPtr<TypeCastExpr> tcexpr = new ExplicitCastExpr();
                    parser->FillPosition(tcexpr.Ptr());
                    tcexpr->FunctionExpr = parser->ParseType();
                    parser->ReadToken(TokenType::RParent);

                    auto arg = parsePrefixExpr(parser);
                    tcexpr->Arguments.Add(arg);

                    return tcexpr;
                }
                else
                {
                    RefPtr<Expr> base = parser->ParseExpression();
                    parser->ReadToken(TokenType::RParent);

                    RefPtr<ParenExpr> parenExpr = new ParenExpr();
                    parenExpr->loc = openParen.loc;
                    parenExpr->base = base;
                    return parenExpr;
                }
            }

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

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

                List<RefPtr<Expr>> exprs;

                for(;;)
                {
                    if(AdvanceIfMatch(parser, TokenType::RBrace))
                        break;

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

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

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

                return initExpr;
            }

        case TokenType::IntegerLiteral:
            {
                RefPtr<IntegerLiteralExpr> constExpr = new IntegerLiteralExpr();
                parser->FillPosition(constExpr.Ptr());

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

                String suffix;
                IntegerLiteralValue value = getIntegerLiteralValue(token, &suffix);

                // Look at any suffix on the value
                char const* suffixCursor = suffix.begin();

                RefPtr<Type> suffixType = nullptr;
                if( suffixCursor && *suffixCursor )
                {
                    int lCount = 0;
                    int uCount = 0;
                    int unknownCount = 0;
                    while(*suffixCursor)
                    {
                        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);
                        suffixType = parser->getSession()->getErrorType();
                    }
                    // `u` or `ul` suffix -> `uint`
                    else if(uCount == 1 && (lCount <= 1))
                    {
                        suffixType = parser->getSession()->getUIntType();
                    }
                    // `l` suffix on integer -> `int` (== `long`)
                    else if(lCount == 1 && !uCount)
                    {
                        suffixType = parser->getSession()->getIntType();
                    }
                    // `ull` suffix -> `uint64_t`
                    else if(uCount == 1 && lCount == 2)
                    {
                        suffixType = parser->getSession()->getUInt64Type();
                    }
                    // `ll` suffix -> `int64_t`
                    else if(uCount == 0 && lCount == 2)
                    {
                        suffixType = parser->getSession()->getInt64Type();
                    }
                    // TODO: do we need suffixes for smaller integer types?
                    else
                    {
                        parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
                        suffixType = parser->getSession()->getErrorType();
                    }
                }

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

                return constExpr;
            }


        case TokenType::FloatingPointLiteral:
            {
                RefPtr<FloatingPointLiteralExpr> constExpr = new FloatingPointLiteralExpr();
                parser->FillPosition(constExpr.Ptr());

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

                String suffix;
                FloatingPointLiteralValue value = getFloatingPointLiteralValue(token, &suffix);

                // Look at any suffix on the value
                char const* suffixCursor = suffix.begin();

                RefPtr<Type> suffixType = nullptr;
                if( suffixCursor && *suffixCursor )
                {
                    int fCount = 0;
                    int lCount = 0;
                    int hCount = 0;
                    int unknownCount = 0;
                    while(*suffixCursor)
                    {
                        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);
                        suffixType = parser->getSession()->getErrorType();
                    }
                    // `f` suffix -> `float`
                    if(fCount == 1 && !lCount)
                    {
                        suffixType = parser->getSession()->getFloatType();
                    }
                    // `l` or `lf` suffix on floating-point literal -> `double`
                    else if(lCount == 1 && (fCount <= 1))
                    {
                        suffixType = parser->getSession()->getDoubleType();
                    }
                    // `h` or `hf` suffix on floating-point literal -> `half`
                    else if(lCount == 1 && (fCount <= 1))
                    {
                        suffixType = parser->getSession()->getHalfType();
                    }
                    // TODO: are there other suffixes we need to handle?
                    else
                    {
                        parser->sink->diagnose(token, Diagnostics::invalidFloatingPOintLiteralSuffix, suffix);
                        suffixType = parser->getSession()->getErrorType();
                    }
                }

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

                return constExpr;
            }

        case TokenType::StringLiteral:
            {
                RefPtr<StringLiteralExpr> constExpr = new StringLiteralExpr();
                auto token = parser->tokenReader.AdvanceToken();
                constExpr->token = token;
                parser->FillPosition(constExpr.Ptr());

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

                RefPtr<Expr> parsedExpr;
                if (tryParseUsingSyntaxDecl<Expr>(parser, &parsedExpr))
                {
                    if (!parsedExpr->loc.isValid())
                    {
                        parsedExpr->loc = nameToken.loc;
                    }
                    return parsedExpr;
                }

                // Default behavior is just to create a name expression
                RefPtr<VarExpr> varExpr = new VarExpr();
                varExpr->scope = parser->currentScope.Ptr();
                parser->FillPosition(varExpr.Ptr());

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

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

                return varExpr;
            }
        }
    }

    static RefPtr<Expr> parsePostfixExpr(Parser* parser)
    {
        auto expr = parseAtomicExpr(parser);
        for(;;)
        {
            switch( peekTokenType(parser) )
            {
            default:
                return expr;

            // Postfix increment/decrement
            case TokenType::OpInc:
            case TokenType::OpDec:
                {
                    RefPtr<OperatorExpr> postfixExpr = new PostfixExpr();
                    parser->FillPosition(postfixExpr.Ptr());
                    postfixExpr->FunctionExpr = parseOperator(parser);
                    postfixExpr->Arguments.Add(expr);

                    expr = postfixExpr;
                }
                break;

            // Subscript operation `a[i]`
            case TokenType::LBracket:
                {
                    RefPtr<IndexExpr> indexExpr = new IndexExpr();
                    indexExpr->BaseExpression = expr;
                    parser->FillPosition(indexExpr.Ptr());
                    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:
                {
                    RefPtr<InvokeExpr> invokeExpr = new InvokeExpr();
                    invokeExpr->FunctionExpr = expr;
                    parser->FillPosition(invokeExpr.Ptr());
                    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;

            // Member access `x.m`
            case TokenType::Dot:
                {
                    RefPtr<MemberExpr> memberExpr = new MemberExpr();

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

                    parser->FillPosition(memberExpr.Ptr());
                    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 RefPtr<Expr> parsePrefixExpr(Parser* parser)
    {
        switch( peekTokenType(parser) )
        {
        default:
            return parsePostfixExpr(parser);

        case TokenType::OpInc:
        case TokenType::OpDec:
        case TokenType::OpNot:
        case TokenType::OpBitNot:
        case TokenType::OpAdd:
        case TokenType::OpSub:
            {
                RefPtr<PrefixExpr> prefixExpr = new PrefixExpr();
                parser->FillPosition(prefixExpr.Ptr());
                prefixExpr->FunctionExpr = parseOperator(parser);
                prefixExpr->Arguments.Add(parsePrefixExpr(parser));
                return prefixExpr;
            }
            break;
        }
    }

    RefPtr<Expr> Parser::ParseLeafExpression()
    {
        return parsePrefixExpr(this);
    }

    RefPtr<Expr> parseTypeFromSourceFile(TranslationUnitRequest*         translationUnit,
        TokenSpan const&                tokens,
        DiagnosticSink*                 sink,
        RefPtr<Scope> const&            outerScope)
    {
        Parser parser(tokens, sink, outerScope);
        parser.translationUnit = translationUnit;
        parser.currentScope = outerScope;
        return parser.ParseType();
    }

    // Parse a source file into an existing translation unit
    void parseSourceFile(
        TranslationUnitRequest*         translationUnit,
        TokenSpan const&                tokens,
        DiagnosticSink*                 sink,
        RefPtr<Scope> const&            outerScope)
    {
        Parser parser(tokens, sink, outerScope);

        parser.translationUnit = translationUnit;


        return parser.parseSourceFile(translationUnit->SyntaxNode.Ptr());
    }

    static void addBuiltinSyntaxImpl(
        Session*                    session,
        Scope*                      scope,
        char const*                 nameText,
        SyntaxParseCallback         callback,
        void*                       userData,
        SyntaxClass<RefObject>      syntaxClass)
    {
        Name* name = session->getNamePool()->getName(nameText);

        RefPtr<SyntaxDecl> syntaxDecl = new 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 RefPtr<RefObject> parseIntrinsicOpModifier(Parser* parser, void* /*userData*/)
    {
        RefPtr<IntrinsicOpModifier> modifier = new 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 fromthe function name:
        //
        //     __intrinsic_op
        //
        if (AdvanceIf(parser, TokenType::LParent))
        {
            if (AdvanceIf(parser, TokenType::OpSub))
            {
                modifier->op = IROp(-StringToInt(parser->ReadToken().Content));
            }
            else if (parser->LookAheadToken(TokenType::IntegerLiteral))
            {
                modifier->op = IROp(StringToInt(parser->ReadToken().Content));
            }
            else
            {
                modifier->opToken = parser->ReadToken(TokenType::Identifier);

                modifier->op = findIROp(modifier->opToken.Content.Buffer());

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

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


        return modifier;
    }

    static RefPtr<RefObject> parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = new 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 RefPtr<RefObject> parseSpecializedForTargetModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = new SpecializedForTargetModifier();
        if (AdvanceIf(parser, TokenType::LParent))
        {
            modifier->targetToken = parser->ReadToken(TokenType::Identifier);
            parser->ReadToken(TokenType::RParent);
        }
        return modifier;
    }

    static RefPtr<RefObject> parseGLSLExtensionModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = new RequiredGLSLExtensionModifier();

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

        return modifier;
    }

    static RefPtr<RefObject> parseGLSLVersionModifier(Parser* parser, void* /*userData*/)
    {
        auto modifier = new RequiredGLSLVersionModifier();

        parser->ReadToken(TokenType::LParent);
        modifier->versionNumberToken = parser->ReadToken(TokenType::IntegerLiteral);
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }



    static RefPtr<RefObject> parseLayoutModifier(Parser* parser, void* /*userData*/)
    {
        ModifierListBuilder listBuilder;

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

            if (nameText == "binding" ||
                nameText == "set")
            {
                GLSLBindingAttribute* attr = listBuilder.find<GLSLBindingAttribute>();
                if (!attr)
                {
                    attr = new GLSLBindingAttribute();
                    listBuilder.add(attr);
                }

                parser->ReadToken(TokenType::OpAssign);

                Token valToken = parser->ReadToken(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
            {
                if (nameText == "push_constant")
                {
                    RefPtr<PushConstantAttribute> modifier(new PushConstantAttribute);

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

                    listBuilder.add(modifier);
                }
                else
                {
                    RefPtr<GLSLLayoutModifier> modifier;

                    // TODO: better handling of this choice (e.g., lookup in scope)
                    if(0) {}
                #define CASE(KEYWORD, CLASS) \
                    else if(nameText == #KEYWORD) modifier = new CLASS()

                    CASE(constant_id,   GLSLConstantIDLayoutModifier);
                    CASE(location,      GLSLLocationLayoutModifier);
                    CASE(local_size_x,  GLSLLocalSizeXLayoutModifier);
                    CASE(local_size_y,  GLSLLocalSizeYLayoutModifier);
                    CASE(local_size_z,  GLSLLocalSizeZLayoutModifier);

                #undef CASE
                    else
                    {
                        modifier = new GLSLUnparsedLayoutModifier();
                    }

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

                    if(AdvanceIf(parser, TokenType::OpAssign))
                    {
                        modifier->valToken = parser->ReadToken(TokenType::IntegerLiteral);
                    }

                    listBuilder.add(modifier);
                }
            }

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

        listBuilder.add(new GLSLLayoutModifierGroupEnd());

        return listBuilder.getFirst();
    }

    static RefPtr<RefObject> parseBuiltinTypeModifier(Parser* parser, void* /*userData*/)
    {
        RefPtr<BuiltinTypeModifier> modifier = new BuiltinTypeModifier();
        parser->ReadToken(TokenType::LParent);
        modifier->tag = BaseType(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }

    static RefPtr<RefObject> parseMagicTypeModifier(Parser* parser, void* /*userData*/)
    {
        RefPtr<MagicTypeModifier> modifier = new MagicTypeModifier();
        parser->ReadToken(TokenType::LParent);
        modifier->name = parser->ReadToken(TokenType::Identifier).Content;
        if (AdvanceIf(parser, TokenType::Comma))
        {
            modifier->tag = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
        }
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }

    static RefPtr<RefObject> parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/)
    {
        RefPtr<IntrinsicTypeModifier> modifier = new IntrinsicTypeModifier();
        parser->ReadToken(TokenType::LParent);
        modifier->irOp = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
        while( AdvanceIf(parser, TokenType::Comma) )
        {
            auto operand = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content));
            modifier->irOperands.Add(operand);
        }
        parser->ReadToken(TokenType::RParent);

        return modifier;
    }
    static RefPtr<RefObject> parseImplicitConversionModifier(Parser* parser, void* /*userData*/)
    {
        RefPtr<ImplicitConversionModifier> modifier = new ImplicitConversionModifier();

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

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

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

        RefPtr<AttributeTargetModifier> modifier = new AttributeTargetModifier();
        modifier->syntaxClass = syntaxClass;

        return modifier;
    }

    RefPtr<ModuleDecl> populateBaseLanguageModule(
        Session*        session,
        RefPtr<Scope>   scope)
    {
        RefPtr<ModuleDecl> moduleDecl = new ModuleDecl();
        scope->containerDecl = moduleDecl;

        // Add syntax for declaration keywords
    #define DECL(KEYWORD, CALLBACK) \
        addBuiltinSyntax<Decl>(session, scope, #KEYWORD, &CALLBACK)
        DECL(typedef,         ParseTypeDef);
        DECL(associatedtype,  parseAssocType);
        DECL(__generic_param, parseGlobalGenericParamDecl);
        DECL(type_param,      parseGlobalGenericParamDecl);
        DECL(cbuffer,         parseHLSLCBufferDecl);
        DECL(tbuffer,         parseHLSLTBufferDecl);
        DECL(__generic,       ParseGenericDecl);
        DECL(__extension,     ParseExtensionDecl);
        DECL(extension,       ParseExtensionDecl);
        DECL(__init,          parseConstructorDecl);
        DECL(__subscript,     ParseSubscriptDecl);
        DECL(interface,       parseInterfaceDecl);
        DECL(syntax,          parseSyntaxDecl);
        DECL(attribute_syntax,parseAttributeSyntaxDecl);
        DECL(__import,        parseImportDecl);
        DECL(import,          parseImportDecl);

    #undef DECL

        // 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.
    #define MODIFIER(KEYWORD, CLASS) \
        addSimpleModifierSyntax<CLASS>(session, scope, #KEYWORD)

        MODIFIER(in,        InModifier);
        MODIFIER(input,     InputModifier);
        MODIFIER(out,       OutModifier);
        MODIFIER(inout,     InOutModifier);
        MODIFIER(__ref,     RefModifier);
        MODIFIER(const,     ConstModifier);
        MODIFIER(instance,  InstanceModifier);
        MODIFIER(__builtin, BuiltinModifier);

        MODIFIER(inline,    InlineModifier);
        MODIFIER(public,    PublicModifier);
        MODIFIER(require,   RequireModifier);
        MODIFIER(param,     ParamModifier);
        MODIFIER(extern,    ExternModifier);

        MODIFIER(row_major,     HLSLRowMajorLayoutModifier);
        MODIFIER(column_major,  HLSLColumnMajorLayoutModifier);

        MODIFIER(nointerpolation,   HLSLNoInterpolationModifier);
        MODIFIER(noperspective,     HLSLNoPerspectiveModifier);
        MODIFIER(linear,            HLSLLinearModifier);
        MODIFIER(sample,            HLSLSampleModifier);
        MODIFIER(centroid,          HLSLCentroidModifier);
        MODIFIER(precise,           HLSLPreciseModifier);
        MODIFIER(shared,            HLSLEffectSharedModifier);
        MODIFIER(groupshared,       HLSLGroupSharedModifier);
        MODIFIER(static,            HLSLStaticModifier);
        MODIFIER(uniform,           HLSLUniformModifier);
        MODIFIER(volatile,          HLSLVolatileModifier);

        // Modifiers for geometry shader input
        MODIFIER(point,         HLSLPointModifier);
        MODIFIER(line,          HLSLLineModifier);
        MODIFIER(triangle,      HLSLTriangleModifier);
        MODIFIER(lineadj,       HLSLLineAdjModifier);
        MODIFIER(triangleadj,   HLSLTriangleAdjModifier);

        // Modifiers for unary operator declarations
        MODIFIER(__prefix,   PrefixModifier);
        MODIFIER(__postfix,  PostfixModifier);

        // Modifier to apply to `import` that should be re-exported
        MODIFIER(__exported,  ExportedModifier);

    #undef MODIFIER

        // Add syntax for more complex modifiers, which allow
        // or expect more tokens after the initial keyword.
    #define MODIFIER(KEYWORD, CALLBACK) \
        addBuiltinSyntax<Modifier>(session, scope, #KEYWORD, &CALLBACK)

        MODIFIER(layout,            parseLayoutModifier);

        MODIFIER(__intrinsic_op,        parseIntrinsicOpModifier);
        MODIFIER(__target_intrinsic,    parseTargetIntrinsicModifier);
        MODIFIER(__specialized_for_target,    parseSpecializedForTargetModifier);
        MODIFIER(__glsl_extension,  parseGLSLExtensionModifier);
        MODIFIER(__glsl_version,    parseGLSLVersionModifier);

        MODIFIER(__builtin_type,    parseBuiltinTypeModifier);
        MODIFIER(__magic_type,      parseMagicTypeModifier);
        MODIFIER(__intrinsic_type,    parseIntrinsicTypeModifier);
        MODIFIER(__implicit_conversion,     parseImplicitConversionModifier);

        MODIFIER(__attributeTarget, parseAttributeTargetModifier);


#undef MODIFIER

        // Add syntax for expression keywords
    #define EXPR(KEYWORD, CALLBACK) \
        addBuiltinSyntax<Expr>(session, scope, #KEYWORD, &CALLBACK)

        EXPR(this,  parseThisExpr);
        EXPR(true,  parseTrueExpr);
        EXPR(false, parseFalseExpr);

    #undef EXPR

        return moduleDecl;
    }

}
back to top