#include "parser.h"

#include "compiler-core/slang-name-convention-util.h"
#include "core/slang-io.h"
#include "core/slang-string-util.h"
#include "identifier-lookup.h"
#include "options.h"

namespace CppParse
{
using namespace Slang;

// If fails then we need more bits to identify types
SLANG_COMPILE_TIME_ASSERT(int(Node::Kind::CountOf) <= 8 * sizeof(uint32_t));

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Parser !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Parser::Parser(NodeTree* nodeTree, DiagnosticSink* sink)
    : m_sink(sink), m_nodeTree(nodeTree), m_nodeTypeEnabled(0)
{
    // Enable types by default
    const Node::Kind defaultEnabled[] = {
        Node::Kind::ClassType,
        Node::Kind::StructType,
        Node::Kind::Namespace,
        Node::Kind::AnonymousNamespace,
        Node::Kind::Field,

        // These are disabled by default because AST uses macro magic to build up the types
        // Node::Type::TypeDef,
        // Node::Type::Enum,
        // Node::Type::EnumClass,

        Node::Kind::Callable,
    };
    setKindsEnabled(defaultEnabled, SLANG_COUNT_OF(defaultEnabled));
}

void Parser::setKindEnabled(Node::Kind kind, bool isEnabled)
{
    if (isEnabled)
    {
        m_nodeTypeEnabled |= (NodeTypeBitType(1) << int(kind));
    }
    else
    {
        m_nodeTypeEnabled &= ~(NodeTypeBitType(1) << int(kind));
    }
}

void Parser::setKindsEnabled(const Node::Kind* kinds, Index kindsCount, bool isEnabled)
{
    for (Index i = 0; i < kindsCount; ++i)
    {
        setKindEnabled(kinds[i], isEnabled);
    }
}

bool Parser::_isMarker(const UnownedStringSlice& name)
{
    return name.startsWith(m_options->m_markPrefix.getUnownedSlice()) &&
           name.endsWith(m_options->m_markSuffix.getUnownedSlice());
}

SlangResult Parser::expect(TokenType type, Token* outToken)
{
    if (m_reader.peekTokenType() != type)
    {
        m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::expectingToken, type);
        return SLANG_FAIL;
    }

    if (outToken)
    {
        *outToken = m_reader.advanceToken();
    }
    else
    {
        m_reader.advanceToken();
    }
    return SLANG_OK;
}

bool Parser::advanceIfToken(TokenType type, Token* outToken)
{
    if (m_reader.peekTokenType() == type)
    {
        Token token = m_reader.advanceToken();
        if (outToken)
        {
            *outToken = token;
        }
        return true;
    }
    return false;
}

bool Parser::advanceIfMarker(Token* outToken)
{
    const Token peekToken = m_reader.peekToken();
    if (peekToken.type == TokenType::Identifier && _isMarker(peekToken.getContent()))
    {
        m_reader.advanceToken();
        if (outToken)
        {
            *outToken = peekToken;
        }
        return true;
    }
    return false;
}

bool Parser::advanceIfStyle(IdentifierStyle style, Token* outToken)
{
    if (m_reader.peekTokenType() == TokenType::Identifier)
    {
        IdentifierStyle readStyle =
            m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());
        if (readStyle == style)
        {
            Token token = m_reader.advanceToken();
            if (outToken)
            {
                *outToken = token;
            }
            return true;
        }
    }
    return false;
}


SlangResult Parser::pushAnonymousNamespace()
{
    m_currentScope = m_currentScope->getAnonymousNamespace();

    if (m_sourceOrigin)
    {
        m_sourceOrigin->addNode(m_currentScope);
    }

    // Add the to the scope stack so can pop.
    m_scopeStack.add(m_currentScope);

    return SLANG_OK;
}

SlangResult Parser::pushScope(ScopeNode* scopeNode)
{
    // We can only have one 'special' scope.
    SLANG_ASSERT(scopeNode || m_scopeStack.getLast());

    // We keep to track.
    m_scopeStack.add(scopeNode);

    // If we pass nullptr, we don't update the current scope.
    if (scopeNode == nullptr)
    {
        return SLANG_OK;
    }

    if (m_sourceOrigin)
    {
        m_sourceOrigin->addNode(scopeNode);
    }

    if (scopeNode->m_name.hasContent())
    {
        // For anonymous namespace, we should look if we already have one and just reopen that.
        // Doing so will mean will find anonymous namespace clashes

        if (Node* foundNode = m_currentScope->findChild(scopeNode->m_name.getContent()))
        {
            if (scopeNode->isClassLike())
            {
                m_sink->diagnose(
                    m_reader.peekToken(),
                    CPPDiagnostics::typeAlreadyDeclared,
                    scopeNode->m_name.getContent());
                m_sink->diagnose(
                    foundNode->m_name,
                    CPPDiagnostics::seeDeclarationOf,
                    scopeNode->m_name.getContent());
                return SLANG_FAIL;
            }

            if (foundNode->m_kind == Node::Kind::Namespace)
            {
                if (foundNode->m_kind != scopeNode->m_kind)
                {
                    // Different types can't work
                    m_sink->diagnose(
                        m_reader.peekToken(),
                        CPPDiagnostics::typeAlreadyDeclared,
                        scopeNode->m_name.getContent());
                    return SLANG_FAIL;
                }

                ScopeNode* foundScopeNode = as<ScopeNode>(foundNode);
                SLANG_ASSERT(foundScopeNode);

                // Make sure the node is empty, as we are *not* going to add it, we are just going
                // to use the pre-existing namespace
                SLANG_ASSERT(scopeNode->m_children.getCount() == 0);

                // We can just use the pre-existing namespace
                m_currentScope = foundScopeNode;
                return SLANG_OK;
            }
        }
    }

    m_currentScope->addChild(scopeNode);
    m_currentScope = scopeNode;
    return SLANG_OK;
}

SlangResult Parser::popScope()
{
    if (m_scopeStack.getCount() <= 0)
    {
        m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::scopeNotClosed);
        return SLANG_FAIL;
    }

    ScopeNode* topScope = m_scopeStack.getLast();
    m_scopeStack.removeLast();

    // If the top is nullptr, we don't change the current scope
    if (topScope == nullptr)
    {
        return SLANG_OK;
    }

    m_currentScope = m_currentScope->m_parentScope;
    return SLANG_OK;
}

SlangResult Parser::_maybeConsumeScope()
{
    // Look for either ; or { to open scope
    while (true)
    {
        const TokenType type = m_reader.peekTokenType();
        if (type == TokenType::Semicolon)
        {
            m_reader.advanceToken();
            return SLANG_OK;
        }
        else if (type == TokenType::LBrace)
        {
            // m_reader.advanceToken();
            return consumeToClosingBrace();
        }
        else if (type == TokenType::EndOfFile)
        {
            return SLANG_OK;
        }

        m_reader.advanceToken();
    }
}

SlangResult Parser::consumeToClosingBrace(const Token* inOpenBraceToken)
{
    Token openToken;
    if (inOpenBraceToken)
    {
        openToken = *inOpenBraceToken;
    }
    else
    {
        openToken = m_reader.advanceToken();
    }
    SLANG_ASSERT(openToken.type == TokenType::LBrace);

    while (true)
    {
        switch (m_reader.peekTokenType())
        {
        case TokenType::EndOfFile:
            {
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::didntFindMatchingBrace);
                m_sink->diagnose(openToken, CPPDiagnostics::seeOpen);
                return SLANG_FAIL;
            }
        case TokenType::LBrace:
            {
                SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
                break;
            }
        case TokenType::RBrace:
            {
                m_reader.advanceToken();
                return SLANG_OK;
            }
        default:
            {
                m_reader.advanceToken();
                break;
            }
        }
    }
}


SlangResult Parser::_parseEnum()
{
    // We are looking for
    // enum ([class name] | [name]) [: base] ( { | ; )

    Token enumToken;

    // consume enum
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &enumToken));

    if (!m_currentScope->canContainTypes())
    {
        m_sink->diagnose(enumToken.loc, CPPDiagnostics::cannotDeclareTypeInScope);
        return SLANG_FAIL;
    }

    Node::Kind kind = Node::Kind::Enum;

    Token nameToken;
    if (advanceIfToken(TokenType::Identifier, &nameToken))
    {
        const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(nameToken.getContent());

        if (style == IdentifierStyle::Class)
        {
            kind = Node::Kind::EnumClass;
            SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &nameToken));
        }
        else if (style == IdentifierStyle::None)
        {
            // It holds the name then
        }
        else
        {
            m_sink->diagnose(
                nameToken.loc,
                CPPDiagnostics::expectingIdentifier,
                nameToken.getContent());
            return SLANG_FAIL;
        }
    }

    RefPtr<EnumNode> node = new EnumNode(kind);
    node->m_name = nameToken;
    node->m_reflectionType = m_currentScope->getContainedReflectionType();

    if (advanceIfToken(TokenType::Colon))
    {
        // We may have tokens up to { or ;
        List<Token> backingTokens;

        while (true)
        {
            TokenType tokenType = m_reader.peekTokenType();
            if (tokenType == TokenType::Semicolon || tokenType == TokenType::LBrace ||
                tokenType == TokenType::EndOfFile)
            {
                break;
            }

            backingTokens.add(m_reader.advanceToken());
        }

        // TODO - Look up the backing type. It can only be an integral. We can assume it must be
        // defined before lookup for our uses here. If we can't find the type, we could assume it's
        // size is undefined

        if (backingTokens.getCount() > 0)
        {
            node->m_backingTokens.swapWith(backingTokens);
        }
    }

    pushScope(node);

    if (advanceIfToken(TokenType::Semicolon))
    {
        if (nameToken.type != TokenType::Invalid)
        {
            Node* node = m_currentScope->findChild(nameToken.getContent());
            if (node)
            {
                // Strictly speaking we should check the backing type etc, match, but for now ignore
                // and assume it's ok

                if (node->m_kind == kind)
                {
                    return SLANG_OK;
                }
                m_sink->diagnose(
                    nameToken.loc,
                    CPPDiagnostics::typeAlreadyDeclared,
                    nameToken.getContent());
                return SLANG_FAIL;
            }
            return popScope();
        }
    }

    SLANG_RETURN_ON_FAIL(expect(TokenType::LBrace));

    while (true)
    {
        TokenType tokenType = m_reader.peekTokenType();
        if (tokenType == TokenType::RBrace)
        {
            break;
        }

        RefPtr<EnumCaseNode> caseNode(new EnumCaseNode);

        // We could also check if the name is a valid identifier for name, for now just assume.
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &caseNode->m_name));

        if (node->findChild(caseNode->m_name.getContent()))
        {
            m_sink->diagnose(
                caseNode->m_name.loc,
                CPPDiagnostics::identifierAlreadyDefined,
                caseNode->m_name.getContent());
            return SLANG_FAIL;
        }

        caseNode->m_reflectionType = m_currentScope->getContainedReflectionType();

        // Add the value
        node->addChild(caseNode);

        if (advanceIfToken(TokenType::OpAssign))
        {
            List<Token> valueTokens;
            SLANG_RETURN_ON_FAIL(_parseExpression(valueTokens));

            if (valueTokens.getCount() > 0)
            {
                caseNode->m_valueTokens.swapWith(valueTokens);
            }
        }

        tokenType = m_reader.peekTokenType();
        if (tokenType == TokenType::Comma)
        {
            m_reader.advanceToken();
            continue;
        }

        break;
    }

    SLANG_RETURN_ON_FAIL(expect(TokenType::RBrace));
    SLANG_RETURN_ON_FAIL(expect(TokenType::Semicolon));

    return popScope();
}

SlangResult Parser::_consumeTemplate()
{
    // Skip the current 'template' token.
    m_reader.advanceToken();

    // Consume everything in <>
    SLANG_RETURN_ON_FAIL(expect(TokenType::OpLess));

    {
        Index arrowCount = 1;
        while (true)
        {
            auto tokenType = m_reader.peekTokenType();

            if (tokenType == TokenType::OpLess)
            {
                m_reader.advanceToken();
                arrowCount++;
            }
            else if (tokenType == TokenType::OpGreater)
            {
                m_reader.advanceToken();
                if (arrowCount == 1)
                {
                    break;
                }
                --arrowCount;
            }
            else if (tokenType == TokenType::OpRsh)
            {
                if (arrowCount < 2)
                {
                    m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedTemplateClose);
                    return SLANG_FAIL;
                }
                m_reader.advanceToken();
                if (arrowCount == 2)
                {
                    break;
                }
                arrowCount -= 2;
            }
            else if (tokenType == TokenType::EndOfFile)
            {
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedEndOfFile);
                return SLANG_FAIL;
            }
            else
            {
                m_reader.advanceToken();
            }
        }
    }

    // Search for { or ; to consume remaining
    while (true)
    {
        auto tokenType = m_reader.peekTokenType();

        switch (tokenType)
        {
        case TokenType::EndOfFile:
            {
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedEndOfFile);
                return SLANG_FAIL;
            }
        case TokenType::Semicolon:
            {
                // Ends with semicolon if it's a template pre-declaration
                m_reader.advanceToken();
                return SLANG_OK;
            }
        case TokenType::LBrace:
            {
                // If ends with {, means could be body of a struct/class or a body of a
                // function/method. Consume it
                SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
                // If we hit a ; just consume and ignore
                advanceIfToken(TokenType::Semicolon);
                return SLANG_OK;
            }
        default:
            {
                // Consume
                m_reader.advanceToken();
                break;
            }
        }
    }
}

SlangResult Parser::_maybeParseNode(Node::Kind kind)
{
    // We are looking for
    // struct/class identifier [: [public|private|protected] Identifier ] {
    // [public|private|proctected:]* marker ( identifier );

    if (kind == Node::Kind::Namespace)
    {
        // consume namespace
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));

        Token name;
        if (advanceIfToken(TokenType::LBrace))
        {
            return pushAnonymousNamespace();
        }
        else if (advanceIfToken(TokenType::Identifier, &name))
        {
            if (advanceIfToken(TokenType::LBrace))
            {
                // Okay looks like we are opening a namespace
                RefPtr<ScopeNode> node(new ScopeNode(Node::Kind::Namespace));
                node->m_name = name;

                node->m_reflectionType = m_currentScope->getContainedReflectionType();
                // Push the node
                return pushScope(node);
            }
        }

        // Just ignore it then
        return SLANG_OK;
    }
    else if (Node::isKindEnumLike(kind))
    {
        return _parseEnum();
    }

    // Must be class | struct

    SLANG_ASSERT(kind == Node::Kind::ClassType || kind == Node::Kind::StructType);

    Token name;

    // consume class | struct
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));
    // Next is the class name
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &name));


    if (m_reader.peekTokenType() == TokenType::Semicolon)
    {
        // pre declaration;
        return SLANG_OK;
    }

    RefPtr<ClassLikeNode> node(new ClassLikeNode(kind));
    node->m_name = name;

    // We default to the containing scope for reflection type.
    if (!m_options->m_requireMark)
    {
        node->m_reflectionType = m_currentScope->getContainedReflectionType();
    }
    else
    {
        // Defaults to not reflected
        SLANG_ASSERT(!node->isReflected());
    }

    if (advanceIfToken(TokenType::Colon))
    {
        // Could have public
        advanceIfStyle(IdentifierStyle::Access);

        if (!advanceIfToken(TokenType::Identifier, &node->m_super))
        {
            return SLANG_OK;
        }
    }

    // We only accept a single super class. Consume everything afterwards until we hit the { brace

    if (m_reader.peekTokenType() != TokenType::LBrace)
    {
        // Consume up until we see a brace else it's an error
        while (true)
        {
            const TokenType peekTokenType = m_reader.peekTokenType();
            if (peekTokenType == TokenType::EndOfFile)
            {
                // Expecting brace
                m_sink->diagnose(
                    m_reader.peekToken(),
                    CPPDiagnostics::expectingToken,
                    TokenType::LBrace);
                return SLANG_FAIL;
            }
            else if (peekTokenType == TokenType::LBrace)
            {
                break;
            }
            m_reader.advanceToken();
        }

        return pushScope(node);
    }

    const Token braceToken = m_reader.advanceToken();
    // TODO: PR #2159 (jsmall-nvidia) - braceToken is set but never used.
    SLANG_UNUSED(braceToken);

    // Push the class scope
    return pushScope(node);
}

SlangResult Parser::_consumeToSync()
{
    while (true)
    {
        TokenType type = m_reader.peekTokenType();

        switch (type)
        {
        case TokenType::Semicolon:
            {
                m_reader.advanceToken();
                return SLANG_OK;
            }
        case TokenType::Pound:
        case TokenType::EndOfFile:
        case TokenType::LBrace:
        case TokenType::RBrace:
            {
                return SLANG_OK;
            }
        }

        m_reader.advanceToken();
    }
}

SlangResult Parser::_maybeParseTemplateArg(Index& ioTemplateDepth)
{
    switch (m_reader.peekTokenType())
    {
    case TokenType::Identifier:
        {
            TokenReader::ParsingCursor nameCursor;
            SLANG_RETURN_ON_FAIL(_maybeParseType(ioTemplateDepth, nameCursor));
            return SLANG_OK;
        }
    case TokenType::IntegerLiteral:
        {
            m_reader.advanceToken();
            return SLANG_OK;
        }
    default:
        break;
    }
    return SLANG_FAIL;
}

SlangResult Parser::_maybeParseTemplateArgs(Index& ioTemplateDepth)
{
    if (!advanceIfToken(TokenType::OpLess))
    {
        return SLANG_FAIL;
    }

    ioTemplateDepth++;

    while (true)
    {
        if (ioTemplateDepth == 0)
        {
            return SLANG_OK;
        }

        switch (m_reader.peekTokenType())
        {
        case TokenType::OpGreater:
            {
                if (ioTemplateDepth <= 0)
                {
                    m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
                    return SLANG_FAIL;
                }
                ioTemplateDepth--;
                m_reader.advanceToken();
                return SLANG_OK;
            }
        case TokenType::OpRsh:
            {
                if (ioTemplateDepth <= 1)
                {
                    m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
                    return SLANG_FAIL;
                }
                ioTemplateDepth -= 2;
                m_reader.advanceToken();
                return SLANG_OK;
            }
        default:
            {
                while (true)
                {
                    SLANG_RETURN_ON_FAIL(_maybeParseTemplateArg(ioTemplateDepth));

                    if (m_reader.peekTokenType() == TokenType::Comma)
                    {
                        m_reader.advanceToken();
                        // If there is a comma parse another arg
                        continue;
                    }
                    break;
                }
                break;
            }
        }
    }
}

SlangResult Parser::_maybeConsume(IdentifierStyle style)
{
    while (advanceIfStyle(style))
        ;
    return SLANG_OK;
}

// True if two of these token types of the same type placed immediately after one another
// produce a different token. Can be conservative, as if not strictly required
// it will just mean more spacing in the output
static bool _canRepeatTokenType(TokenType type)
{
    switch (type)
    {
    case TokenType::OpAdd:
    case TokenType::OpSub:
    case TokenType::OpAnd:
    case TokenType::OpOr:
    case TokenType::OpGreater:
    case TokenType::OpLess:
    case TokenType::Identifier:
    case TokenType::OpAssign:
    case TokenType::Colon:
        {
            return false;
        }
    default:
        break;
    }
    return true;
}

// Returns true if there needs to be a space between the previous token type, and the current token
// type for correct output. It is assumed that the token stream is appropriate.
// The implementation might need more sophistication, but this at least avoids Blah const *  ->
// Blahconst*
static bool _tokenConcatNeedsSpace(TokenType prev, TokenType cur)
{
    if ((cur == TokenType::OpAssign) || (prev == cur && !_canRepeatTokenType(cur)))
    {
        return true;
    }
    return false;
}

void Parser::_getTypeTokens(
    TokenReader::ParsingCursor start,
    TokenReader::ParsingCursor nameCursor,
    List<Token>& outToks)
{
    auto endCursor = m_reader.getCursor();
    m_reader.setCursor(start);

    while (!m_reader.isAtCursor(endCursor))
    {
        if (m_reader.getCursor() == nameCursor)
        {
            m_reader.advanceToken();
        }
        else
        {
            outToks.add(m_reader.advanceToken());
        }
    }
}

UnownedStringSlice Parser::_concatType(
    TokenReader::ParsingCursor start,
    TokenReader::ParsingCursor nameCursor)
{
    List<Token> toks;
    _getTypeTokens(start, nameCursor, toks);
    return _concatTokens(toks.getBuffer(), toks.getCount());
}

UnownedStringSlice Parser::_concatTokens(const Token* toks, Index toksCount)
{
    StringBuilder buf;

    TokenType prevTokenType = TokenType::Unknown;
    for (Index i = 0; i < toksCount; ++i)
    {
        const auto token = toks[i];

        // Check if we need a space between tokens
        if (_tokenConcatNeedsSpace(prevTokenType, token.type))
        {
            buf << " ";
        }

        buf << token.getContent();

        prevTokenType = token.type;
    }

    StringSlicePool* typePool = m_nodeTree->m_typePool;
    return typePool->getSlice(typePool->add(buf));
}

UnownedStringSlice Parser::_concatTokens(TokenReader::ParsingCursor start)
{
    auto endCursor = m_reader.getCursor();

    m_reader.setCursor(start);

    TokenType prevTokenType = TokenType::Unknown;

    StringBuilder buf;
    while (!m_reader.isAtCursor(endCursor))
    {
        const Token token = m_reader.advanceToken();
        // Check if we need a space between tokens
        if (_tokenConcatNeedsSpace(prevTokenType, token.type))
        {
            buf << " ";
        }
        buf << token.getContent();

        prevTokenType = token.type;
    }

    StringSlicePool* typePool = m_nodeTree->m_typePool;
    return typePool->getSlice(typePool->add(buf));
}

SlangResult Parser::_maybeParseType(
    Index& ioTemplateDepth,
    TokenReader::ParsingCursor& outNameCursor)
{
    outNameCursor = TokenReader::ParsingCursor();

    while (true)
    {
        if (m_reader.peekTokenType() == TokenType::Identifier)
        {
            const IdentifierStyle style =
                m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());

            if (style == IdentifierStyle::TypeModifier ||
                style == IdentifierStyle::IntegerModifier || style == IdentifierStyle::Class ||
                style == IdentifierStyle::Struct)
            {
                // These are ok keywords in this context
            }
            else if (hasFlag(style, IdentifierFlag::Keyword))
            {
                return SLANG_FAIL;
            }
        }

        _maybeConsume(IdentifierStyle::TypeModifier);

        if (advanceIfStyle(IdentifierStyle::IntegerModifier))
        {
            // Consume the integer typename (if there is one)
            const Token peekToken = m_reader.peekToken();
            if (peekToken.type == TokenType::Identifier)
            {
                const IdentifierStyle style =
                    m_nodeTree->m_identifierLookup->get(peekToken.getContent());
                if (style == IdentifierStyle::IntegerType)
                {
                    m_reader.advanceToken();
                }
            }
            break;
        }

        advanceIfToken(TokenType::Scope);
        while (true)
        {
            // if we have a struct/class prefix in front of a name just consume it.
            if (m_reader.peekTokenType() == TokenType::Identifier)
            {
                const IdentifierStyle style =
                    m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());
                if (style == IdentifierStyle::Class || style == IdentifierStyle::Struct)
                {
                    m_reader.advanceToken();
                }
            }

            Token identifierToken;
            if (!advanceIfToken(TokenType::Identifier, &identifierToken))
            {
                return SLANG_FAIL;
            }

            const IdentifierStyle style =
                m_nodeTree->m_identifierLookup->get(identifierToken.getContent());
            if (hasFlag(style, IdentifierFlag::Keyword))
            {
                return SLANG_FAIL;
            }

            if (advanceIfToken(TokenType::Scope))
            {
                continue;
            }
            break;
        }

        if (m_reader.peekTokenType() == TokenType::OpLess)
        {
            SLANG_RETURN_ON_FAIL(_maybeParseTemplateArgs(ioTemplateDepth));
        }

        if (m_reader.peekTokenType() == TokenType::Scope)
        {
            // Skip the scope and repeat
            m_reader.advanceToken();
            continue;
        }

        break;
    }

    // Strip all the consts etc modifiers
    _maybeConsume(IdentifierStyle::TypeModifier);

    // It's a reference and we are done
    if (advanceIfToken(TokenType::OpBitAnd))
    {
        return SLANG_OK;
    }

    while (true)
    {
        if (advanceIfToken(TokenType::OpMul))
        {
            // Strip all the consts
            _maybeConsume(IdentifierStyle::TypeModifier);
            continue;
        }
        break;
    }

    if (advanceIfToken(TokenType::LParent))
    {
        // TODO(JS):
        // Doesn't handle all the modifiers just (*SomeName)

        SLANG_RETURN_ON_FAIL(expect(TokenType::OpMul));
        outNameCursor = m_reader.getCursor();
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));

        SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));

        // We need to parse and add the params
        if (m_reader.peekTokenType() != TokenType::LParent)
        {
            m_sink->diagnose(
                m_reader.peekToken(),
                CPPDiagnostics::expectingToken,
                TokenType::LParent);
            return SLANG_FAIL;
        }

        // Consume the params
        SLANG_RETURN_ON_FAIL(_consumeBalancedParens());
    }
    else if (m_reader.peekTokenType() == TokenType::Identifier)
    {
        auto potentialNameCursor = m_reader.getCursor();
        m_reader.advanceToken();
        if (m_reader.peekTokenType() == TokenType::LBracket)
        {
            outNameCursor = potentialNameCursor;
            while (advanceIfToken(TokenType::LBracket))
            {
                List<Token> exprToks;
                SLANG_RETURN_ON_FAIL(_parseExpression(exprToks));
                SLANG_RETURN_ON_FAIL(expect(TokenType::RBracket));
            }
        }
        else
        {
            // Wasn't an array type..., so rewind
            m_reader.setCursor(potentialNameCursor);
        }
    }

    return SLANG_OK;
}

SlangResult Parser::_maybeParseType(List<Token>& outToks, Token& outName)
{
    // Set to unknown
    outName = Token();

    auto startCursor = m_reader.getCursor();

    TokenReader::ParsingCursor nameCursor;

    Index templateDepth = 0;
    SlangResult res = _maybeParseType(templateDepth, nameCursor);
    if (SLANG_FAILED(res) && m_sink->getErrorCount())
    {
        return res;
    }

    if (templateDepth != 0)
    {
        m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
        return SLANG_FAIL;
    }

    auto endCursor = m_reader.getCursor();
    m_reader.setCursor(startCursor);

    if (nameCursor.isValid())
    {
        while (!m_reader.isAtCursor(endCursor))
        {
            if (m_reader.getCursor() == nameCursor)
            {
                outName = m_reader.advanceToken();
            }
            else
            {
                outToks.add(m_reader.advanceToken());
            }
        }
    }
    else
    {
        while (!m_reader.isAtCursor(endCursor))
        {
            outToks.add(m_reader.advanceToken());
        }
    }

    return SLANG_OK;
}

SlangResult Parser::_parseSpecialMacro()
{
    Token name;
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &name));

    List<Token> params;

    if (m_reader.peekTokenType() == TokenType::LParent)
    {
        // Mark the start
        auto startCursor = m_reader.getCursor();

        // Consume the params
        SLANG_RETURN_ON_FAIL(_consumeBalancedParens());

        auto endCursor = m_reader.getCursor();
        m_reader.setCursor(startCursor);

        while (!m_reader.isAtCursor(endCursor))
        {
            params.add(m_reader.advanceToken());
        }
    }

    // Can do special handling here
    const UnownedStringSlice suffix = name.getContent().tail(m_options->m_markPrefix.getLength());

    if (suffix == "COM_INTERFACE")
    {
        // TODO(JS): It's a com interface. Extact the GUID
    }

    return SLANG_OK;
}

SlangResult Parser::_parseMarker()
{
    SLANG_ASSERT(
        m_reader.peekTokenType() == TokenType::Identifier &&
        _isMarker(m_reader.peekToken().getContent()) && m_currentScope->isClassLike());

    ClassLikeNode* node = as<ClassLikeNode>(m_currentScope);

    if (node->m_marker.type != TokenType::Unknown)
    {
        m_sink->diagnose(
            m_reader.peekToken(),
            CPPDiagnostics::classMarkerAlreadyFound,
            node->m_name.getContent());
        m_sink->diagnose(node->m_marker, CPPDiagnostics::previousLocation);
        return SLANG_FAIL;
    }

    // Set the marker token.
    node->m_marker = m_reader.advanceToken();

    // Looks like it's a marker
    UnownedStringSlice slice(node->m_marker.getContent());

    // Strip the prefix and suffix
    slice = UnownedStringSlice(
        slice.begin() + m_options->m_markPrefix.getLength(),
        slice.end() - m_options->m_markSuffix.getLength());

    // Strip ABSTRACT_ if it's there
    UnownedStringSlice abstractSlice("ABSTRACT_");
    if (slice.startsWith(abstractSlice))
    {
        slice = UnownedStringSlice(slice.begin() + abstractSlice.getLength(), slice.end());
    }

    // TODO: We could strip other stuff or have other heuristics there, but this is
    // probably okay for now

    // Set the typeSet
    node->m_typeSet = m_nodeTree->getOrAddTypeSet(slice);

    // Okay now looking for ( identifier)
    Token typeNameToken;

    SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeNameToken));
    SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));

    if (typeNameToken.getContent() != node->m_name.getContent())
    {
        m_sink->diagnose(
            typeNameToken,
            CPPDiagnostics::typeNameDoesntMatch,
            node->m_name.getContent());
        return SLANG_FAIL;
    }

    // If has the marker it is assumed reflected
    node->m_reflectionType = ReflectionType::Reflected;
    return SLANG_OK;
}

SlangResult Parser::_maybeParseType(UnownedStringSlice& outType, Token& outName)
{
    auto startCursor = m_reader.getCursor();

    Index templateDepth = 0;

    TokenReader::ParsingCursor nameCursor;

    SlangResult res = _maybeParseType(templateDepth, nameCursor);
    if (SLANG_FAILED(res) && m_sink->getErrorCount())
    {
        return res;
    }

    if (templateDepth != 0)
    {
        m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::unexpectedTemplateClose);
        return SLANG_FAIL;
    }

    if (nameCursor.isValid())
    {
        const auto cursor = m_reader.getCursor();
        m_reader.setCursor(nameCursor);
        outName = m_reader.peekToken();
        m_reader.setCursor(cursor);

        // Extract the contents
        List<Token> toks;
        _getTypeTokens(startCursor, nameCursor, toks);
        outType = _concatTokens(toks.getBuffer(), toks.getCount());
    }
    else
    {
        // We can build up the out type, from the tokens we found
        outType = _concatTokens(startCursor);
    }
    return SLANG_OK;
}

static bool _isBalancedOpen(TokenType tokenType)
{
    return tokenType == TokenType::LBrace || tokenType == TokenType::LParent ||
           tokenType == TokenType::LBracket;
}

static bool _isBalancedClose(TokenType tokenType)
{
    return tokenType == TokenType::RBrace || tokenType == TokenType::RParent ||
           tokenType == TokenType::RBracket;
}

static TokenType _getBalancedClose(TokenType tokenType)
{
    SLANG_ASSERT(_isBalancedOpen(tokenType));
    switch (tokenType)
    {
    case TokenType::LBrace:
        return TokenType::RBrace;
    case TokenType::LParent:
        return TokenType::RParent;
    case TokenType::LBracket:
        return TokenType::RBracket;
    default:
        return TokenType::Unknown;
    }
}

SlangResult Parser::_parseBalanced(DiagnosticSink* sink)
{
    const TokenType openTokenType = m_reader.peekTokenType();
    if (!_isBalancedOpen(openTokenType))
    {
        return SLANG_FAIL;
    }

    // Save the start token
    const Token startToken = m_reader.advanceToken();
    // Get the token type that would close the open
    const TokenType closeTokenType = _getBalancedClose(openTokenType);

    while (true)
    {
        const TokenType tokenType = m_reader.peekTokenType();

        // If we hit the closing token, we are done
        if (tokenType == closeTokenType)
        {
            m_reader.advanceToken();
            return SLANG_OK;
        }

        // If we hit a balanced open, recurse
        if (_isBalancedOpen(tokenType))
        {
            SLANG_RETURN_ON_FAIL(_parseBalanced(sink));
            continue;
        }

        // If we hit a close token that doesn't match, then the balancing has gone wrong
        if (_isBalancedClose(tokenType))
        {
            // Only diagnose if required
            if (sink)
            {
                sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedUnbalancedToken);
                sink->diagnose(startToken, CPPDiagnostics::seeOpen);
            }
            return SLANG_FAIL;
        }

        // If we hit the end of the file and have not hit the closing token, then
        // somethings gone wrong
        if (tokenType == TokenType::EndOfFile)
        {
            if (sink)
            {
                sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::unexpectedEndOfFile);
                sink->diagnose(startToken, CPPDiagnostics::seeOpen);
            }

            return SLANG_FAIL;
        }

        // Skip the token
        m_reader.advanceToken();
    }
}

SlangResult Parser::_consumeBalancedParens()
{
    SLANG_ASSERT(m_reader.peekTokenType() == TokenType::LParent);

    Index parenCount = 0;

    while (true)
    {
        const TokenType tokenType = m_reader.peekTokenType();

        switch (tokenType)
        {
        case TokenType::LParent:
            {
                parenCount++;
                break;
            }
        case TokenType::RParent:
            {
                --parenCount;
                // If no more parens then we are done
                if (parenCount == 0)
                {
                    m_reader.advanceToken();
                    return SLANG_OK;
                }
                break;
            }
        case TokenType::EndOfFile:
            {
                // If we hit the end of the file, then not balanced
                return SLANG_FAIL;
            }
        default:
            break;
        }

        m_reader.advanceToken();
    }
}

SlangResult Parser::_parseExpression(List<Token>& outExprTokens)
{
    Index parenCount = 0;
    Index bracketCount = 0;

    // TODO(JS): NOTE! This doesn't handle an expression that contains a template params in
    // Something<Arg1, 3>, because without knowing what Something is, it's not known if < is a
    // comparison or or a 'template' bracket
    //
    // This can be worked around in the originating source by placing in parens

    while (true)
    {
        TokenType tokenType = m_reader.peekTokenType();

        switch (tokenType)
        {
        case TokenType::LParent:
            {
                parenCount++;
                break;
            }
        case TokenType::RParent:
            {
                // If no parens, and nothing else is open then we are done
                if (parenCount == 0)
                {
                    if (bracketCount)
                    {
                        m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
                        return SLANG_FAIL;
                    }

                    return SLANG_OK;
                }
                --parenCount;
                break;
            }
        case TokenType::LBracket:
            {
                bracketCount++;
                break;
            }
        case TokenType::RBracket:
            {
                // If no brackets are open we are done
                if (bracketCount == 0)
                {
                    if (parenCount)
                    {
                        m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
                        return SLANG_FAIL;
                    }
                    return SLANG_OK;
                }
                --bracketCount;
                break;
            }
        case TokenType::EndOfFile:
            {
                if ((bracketCount | parenCount) == 0)
                {
                    return SLANG_OK;
                }
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseExpression);
                return SLANG_FAIL;
            }
        case TokenType::RBrace:
        case TokenType::Semicolon:
        case TokenType::Comma:
            {
                if ((bracketCount | parenCount) == 0)
                {
                    return SLANG_OK;
                }
                break;
            }

        default:
            break;
        }

        outExprTokens.add(m_reader.advanceToken());
    }
}

SlangResult Parser::_parseTypeDef()
{
    if (!m_currentScope->canContainTypes())
    {
        m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotDeclareTypeInScope);
        return SLANG_FAIL;
    }

    // Consume the typedef
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier));

    Token nameToken;
    // Parse the type
    List<Token> toks;
    SLANG_RETURN_ON_FAIL(_maybeParseType(toks, nameToken));

    // Followed by the name
    if (nameToken.type != TokenType::Identifier)
    {
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &nameToken));
    }

    if (Node::lookupNameInScope(m_currentScope, nameToken.getContent()))
    {
        m_sink->diagnose(
            nameToken.loc,
            CPPDiagnostics::identifierAlreadyDefined,
            nameToken.getContent());
        return SLANG_FAIL;
    }

    SLANG_RETURN_ON_FAIL(expect(TokenType::Semicolon));

    RefPtr<TypeDefNode> node = new TypeDefNode;
    node->m_name = nameToken;
    node->m_reflectionType = m_currentScope->getContainedReflectionType();

    // Set what aliases too
    node->m_targetTypeTokens.swapWith(toks);

    m_currentScope->addChild(node);

    return SLANG_OK;
}


bool Parser::_isCtor()
{
    bool isCtor = false;
    // It's a constructor
    if (m_currentScope->isClassLike() && m_reader.peekTokenType() == TokenType::Identifier &&
        m_reader.peekToken().getContent() == m_currentScope->m_name.getContent())
    {
        // We need to check it's followed immediately by ( to be sure it's a ctor

        auto cursor = m_reader.getCursor();
        m_reader.advanceToken();
        isCtor = (m_reader.peekTokenType() == TokenType::LParent);
        m_reader.setCursor(cursor);
    }

    return isCtor;
}

bool isAlphaNumeric(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
}

SlangResult Parser::_maybeParseContained(Node** outNode)
{
    *outNode = nullptr;

    _maybeConsume(IdentifierStyle::CallableMisc);

    bool isStatic = false;
    bool isVirtual = false;

    while (m_reader.peekTokenType() == TokenType::Identifier)
    {
        const IdentifierStyle style =
            m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());

        // Check for virtualness
        if (style == IdentifierStyle::Virtual)
        {
            isVirtual = true;
            m_reader.advanceToken();
            continue;
        }

        // Check if static
        if (style == IdentifierStyle::Static)
        {
            isStatic = true;
            m_reader.advanceToken();
            continue;
        }

        break;
    }

    _maybeConsume(IdentifierStyle::CallableMisc);

    UnownedStringSlice typeName;
    Token nameToken;

    bool isConstructor = false;

    if (m_currentScope->isClassLike())
    {
        // If it's a dtor
        if (advanceIfToken(TokenType::OpBitNot, &nameToken))
        {
            // Dtor
            // For Dtor we don't hold the full name just the ~
            Token tok;
            SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &tok));

            if (tok.getContent() != m_currentScope->m_name.getContent())
            {
                m_sink->diagnose(
                    m_reader.peekLoc(),
                    CPPDiagnostics::destructorNameDoesntMatch,
                    m_currentScope->m_name.getContent());
                return SLANG_FAIL;
            }
        }
        else if (_isCtor())
        {
            nameToken = m_reader.advanceToken();
            isConstructor = true;
        }
    }

    // If don't have a name it's not a dtor or ctor, so see if it's a type
    if (nameToken.type == TokenType::Unknown)
    {
        if (SLANG_FAILED(_maybeParseType(typeName, nameToken)))
        {
            if (m_sink->getErrorCount())
            {
                return SLANG_FAIL;
            }

            _consumeToSync();
            return SLANG_OK;
        }
    }

    if (nameToken.type == TokenType::Unknown)
    {
        // Has a calling convention (must be a function/method)
        Token callingConventionToken;
        advanceIfStyle(IdentifierStyle::CallingConvention, &callingConventionToken);

        // Expecting a name
        if (!advanceIfToken(TokenType::Identifier, &nameToken))
        {
            _consumeToSync();
            return SLANG_OK;
        }
    }

    // Handles other scenarios, but here for catching operator overloading
    if (nameToken.type == TokenType::Identifier)
    {
        const auto style = m_nodeTree->m_identifierLookup->get(nameToken.getContent());
        if (style != IdentifierStyle::None)
        {
            _consumeToSync();
            return SLANG_OK;
        }
    }

    if (m_reader.peekTokenType() == TokenType::LParent)
    {
        if (!m_currentScope->canContainCallable())
        {
            SLANG_RETURN_ON_FAIL(_consumeBalancedParens());
            // Consume everything up to ; or {
            SLANG_RETURN_ON_FAIL(_consumeToSync());

            return SLANG_OK;
        }

        // Looks like it's a callable
        m_reader.advanceToken();

        List<CallableNode::Param> params;

        if (m_reader.peekTokenType() != TokenType::RParent)
        {
            while (true)
            {
                Token paramName;
                UnownedStringSlice type;
                SlangResult res = _maybeParseType(type, paramName);

                if (SLANG_FAILED(res))
                {
                    m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingType);
                    return res;
                }

                if (paramName.type != TokenType::Identifier)
                {
                    if (m_reader.peekTokenType() == TokenType::Identifier)
                    {
                        paramName = m_reader.advanceToken();
                    }
                }

                // If we have a name check for default value
                if (paramName.type == TokenType::Identifier && advanceIfToken(TokenType::OpAssign))
                {
                    // Check if we have a default value
                    List<Token> exprTokens;
                    SLANG_RETURN_ON_FAIL(_parseExpression(exprTokens));
                }

                CallableNode::Param param;
                param.m_name = paramName;
                param.m_type = type;

                params.add(param);

                {
                    const auto peekType = m_reader.peekTokenType();
                    if (peekType == TokenType::RParent)
                    {
                        break;
                    }
                    if (peekType == TokenType::Comma)
                    {
                        m_reader.advanceToken();
                        continue;
                    }
                }

                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, ", or ) or =");
                return SLANG_FAIL;
            }
        }

        // Skip )
        m_reader.advanceToken();

        // Parse suffix
        bool isPure = false;

        // const?
        _maybeConsume(IdentifierStyle::TypeModifier);

        if (isConstructor)
        {
            // Initializer list
            if (advanceIfToken(TokenType::Colon))
            {
                while (true)
                {
                    auto peekType = m_reader.peekTokenType();
                    if (peekType == TokenType::Semicolon || peekType == TokenType::LBrace ||
                        peekType == TokenType::EndOfFile)
                    {
                        break;
                    }
                    // Consume
                    m_reader.advanceToken();
                }
            }
        }

        // = 0 ? or = default
        if (advanceIfToken(TokenType::OpAssign))
        {
            if (m_reader.peekTokenType() == TokenType::IntegerLiteral)
            {
                Int value = -1;
                if (SLANG_SUCCEEDED(
                        StringUtil::parseInt(m_reader.peekToken().getContent(), value)) &&
                    value == 0)
                {
                    isPure = true;
                    m_reader.advanceToken();
                }
                else
                {
                    m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, "0");
                    return SLANG_FAIL;
                }
            }
            else if (advanceIfStyle(IdentifierStyle::Default))
            {
            }
            else
            {
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotParseCallable);
                return SLANG_FAIL;
            }
        }

        if (m_reader.peekTokenType() == TokenType::Semicolon)
        {
            m_reader.advanceToken();
        }
        else if (m_reader.peekTokenType() == TokenType::LBrace)
        {
            SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
        }
        else
        {
            m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::expectingToken, "; or {");
            return SLANG_FAIL;
        }

        RefPtr<CallableNode> callableNode = new CallableNode;

        callableNode->m_returnType = typeName;
        callableNode->m_name = nameToken;
        callableNode->m_reflectionType = m_currentScope->getContainedReflectionType();

        callableNode->m_isVirtual = isVirtual;
        callableNode->m_isPure = isPure;
        callableNode->m_isStatic = isStatic;

        callableNode->m_params.swapWith(params);

        Node* nodeWithName = m_currentScope->findChild(nameToken.getContent());

        if (nodeWithName)
        {
            CallableNode* initialOverload = as<CallableNode>(nodeWithName);
            if (!initialOverload)
            {
                m_sink->diagnose(m_reader.peekLoc(), CPPDiagnostics::cannotOverload);
                m_sink->diagnose(nodeWithName->getSourceLoc(), CPPDiagnostics::seeDeclarationOf);
                return SLANG_FAIL;
            }

            callableNode->m_nextOverload = initialOverload->m_nextOverload;
            initialOverload->m_nextOverload = initialOverload;

            m_currentScope->addChildIgnoringName(callableNode);
        }
        else
        {
            m_currentScope->addChild(callableNode);
        }

        *outNode = callableNode;
        return SLANG_OK;
    }
    else
    {
        // Looks like variable
        if (!m_currentScope->canContainFields() || nameToken.type != TokenType::Identifier)
        {
            _consumeToSync();
            return SLANG_OK;
        }

        // Check if has a default value
        if (advanceIfToken(TokenType::OpAssign))
        {
            List<Token> exprTokens;
            SLANG_RETURN_ON_FAIL(_parseExpression(exprTokens));
        }

        // Hit end of field/variable
        if (m_reader.peekTokenType() == TokenType::Semicolon)
        {
            RefPtr<FieldNode> fieldNode = new FieldNode;

            fieldNode->m_fieldType = typeName;
            fieldNode->m_name = nameToken;
            fieldNode->m_reflectionType = m_currentScope->getContainedReflectionType();
            fieldNode->m_isStatic = isStatic;
            if (fieldNode->m_reflectionType == ReflectionType::Reflected)
            {
                static const char* illegalTypes[] = {
                    "size_t",
                    "Int",
                    "UInt",
                    "Index",
                    "Count",
                    "UIndex",
                    "UCount",
                    "PtrInt",
                    "intptr_t",
                    "uintptr_t"};
                for (const auto& illegalType : illegalTypes)
                {
                    int index = typeName.indexOf(UnownedStringSlice(illegalType));
                    if (index != -1)
                    {
                        index += UnownedStringSlice(illegalType).getLength();
                        if (index >= typeName.getLength() || !isAlphaNumeric(typeName[index]))
                        {
                            // Cannot use this type in a field (as it's arch dependent
                            m_sink->diagnose(
                                nameToken,
                                CPPDiagnostics::cannoseUseArchDependentType,
                                illegalType);
                            return SLANG_FAIL;
                        }
                    }
                }
            }
            m_currentScope->addChild(fieldNode);

            *outNode = fieldNode;
            return SLANG_OK;
        }
    }

    _consumeToSync();
    return SLANG_OK;
}

/* static */ Node::Kind Parser::_toNodeKind(IdentifierStyle style)
{
    switch (style)
    {
    case IdentifierStyle::Class:
        return Node::Kind::ClassType;
    case IdentifierStyle::Struct:
        return Node::Kind::StructType;
    case IdentifierStyle::Namespace:
        return Node::Kind::Namespace;
    case IdentifierStyle::Enum:
        return Node::Kind::Enum;
    case IdentifierStyle::TypeDef:
        return Node::Kind::TypeDef;
    default:
        return Node::Kind::Invalid;
    }
}

static UnownedStringSlice _trimUnderscorePrefix(const UnownedStringSlice& slice)
{
    if (slice.getLength() && slice[0] == '_')
    {
        return UnownedStringSlice(slice.begin() + 1, slice.end());
    }
    else
    {
        return slice;
    }
}

SlangResult Parser::_parsePreDeclare()
{
    // Skip the declare type token
    m_reader.advanceToken();

    SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));

    // Get the typeSet
    Token typeSetToken;
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeSetToken));
    TypeSet* typeSet = m_nodeTree->getOrAddTypeSet(typeSetToken.getContent());

    SLANG_RETURN_ON_FAIL(expect(TokenType::Comma));

    // Get the type of type
    Node::Kind nodeKind;
    {
        Token typeToken;
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeToken));

        const IdentifierStyle style = m_nodeTree->m_identifierLookup->get(typeToken.getContent());

        if (style != IdentifierStyle::Struct && style != IdentifierStyle::Class)
        {
            m_sink->diagnose(
                typeToken,
                CPPDiagnostics::expectingTypeKeyword,
                typeToken.getContent());
            return SLANG_FAIL;
        }
        nodeKind = _toNodeKind(style);
    }

    Token name;
    Token super;

    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &name));

    if (advanceIfToken(TokenType::Colon))
    {
        SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &super));
    }

    SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));

    switch (nodeKind)
    {
    case Node::Kind::ClassType:
    case Node::Kind::StructType:
        {
            RefPtr<ClassLikeNode> node(new ClassLikeNode(nodeKind));

            node->m_name = name;
            node->m_super = super;
            node->m_typeSet = typeSet;

            // Assume it is reflected
            node->m_reflectionType = ReflectionType::Reflected;

            SLANG_RETURN_ON_FAIL(pushScope(node));
            // Pop out of the node
            popScope();
            break;
        }
    default:
        {
            return SLANG_FAIL;
        }
    }


    return SLANG_OK;
}

SlangResult Parser::_parseTypeSet()
{
    // Skip the declare type token
    m_reader.advanceToken();

    SLANG_RETURN_ON_FAIL(expect(TokenType::LParent));

    Token typeSetToken;
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeSetToken));

    TypeSet* typeSet = m_nodeTree->getOrAddTypeSet(typeSetToken.getContent());

    SLANG_RETURN_ON_FAIL(expect(TokenType::Comma));

    // Get the type of type
    Token typeToken;
    SLANG_RETURN_ON_FAIL(expect(TokenType::Identifier, &typeToken));

    SLANG_RETURN_ON_FAIL(expect(TokenType::RParent));

    // Set the typename
    typeSet->m_typeName = typeToken.getContent();

    return SLANG_OK;
}

SlangResult Parser::parse(SourceOrigin* sourceOrigin, const Options* options)
{
    SLANG_ASSERT(options);
    m_options = options;

    // Set the current origin
    m_sourceOrigin = sourceOrigin;

    SourceFile* sourceFile = sourceOrigin->m_sourceFile;

    SourceManager* manager = sourceFile->getSourceManager();

    SourceView* sourceView = manager->createSourceView(sourceFile, nullptr, SourceLoc::fromRaw(0));

    Lexer lexer;

    // Set up the scope stack
    m_scopeStack.clear();

    m_currentScope = m_nodeTree->m_rootNode;
    m_scopeStack.add(m_currentScope);

    if (!options->m_requireMark)
    {
        m_currentScope->m_reflectionOverride = ReflectionType::Reflected;
    }

    lexer.initialize(sourceView, m_sink, m_nodeTree->m_namePool, manager->getMemoryArena());
    m_tokenList = lexer.lexAllSemanticTokens();
    // See if there were any errors
    if (m_sink->getErrorCount())
    {
        return SLANG_FAIL;
    }

    m_reader = TokenReader(m_tokenList);

    while (true)
    {
        switch (m_reader.peekTokenType())
        {
        case TokenType::OpBitNot:
            {
                // Handle dtor
                if (m_currentScope->isClassLike())
                {
                    Node* containedNode = nullptr;
                    SLANG_RETURN_ON_FAIL(_maybeParseContained(&containedNode));
                }
                else
                {
                    // consume
                    m_reader.advanceToken();
                }
                break;
            }
        case TokenType::Identifier:
            {
                const IdentifierStyle style =
                    m_nodeTree->m_identifierLookup->get(m_reader.peekToken().getContent());

                switch (style)
                {
                case IdentifierStyle::Extern:
                    {
                        m_reader.advanceToken();

                        Token externType;
                        SLANG_RETURN_ON_FAIL(expect(TokenType::StringLiteral, &externType));

                        if (advanceIfToken(TokenType::LBrace))
                        {
                            // Push a 'special' scope (which is basically transparent)
                            pushScope(nullptr);
                        }
                        break;
                    }
                case IdentifierStyle::Template:
                    {
                        SLANG_RETURN_ON_FAIL(_consumeTemplate());
                        break;
                    }
                case IdentifierStyle::PreDeclare:
                    {
                        SLANG_RETURN_ON_FAIL(_parsePreDeclare());
                        break;
                    }
                case IdentifierStyle::TypeSet:
                    {
                        SLANG_RETURN_ON_FAIL(_parseTypeSet());
                        break;
                    }
                case IdentifierStyle::Reflected:
                    {
                        m_reader.advanceToken();
                        if (m_currentScope)
                        {
                            m_currentScope->m_reflectionOverride = ReflectionType::Reflected;
                        }
                        break;
                    }
                case IdentifierStyle::Unreflected:
                    {
                        m_reader.advanceToken();
                        if (m_currentScope)
                        {
                            m_currentScope->m_reflectionOverride = ReflectionType::NotReflected;
                        }
                        break;
                    }
                case IdentifierStyle::Access:
                    {
                        m_reader.advanceToken();
                        SLANG_RETURN_ON_FAIL(expect(TokenType::Colon));
                        break;
                    }
                case IdentifierStyle::TypeDef:
                    {
                        if (isTypeEnabled(Node::Kind::TypeDef))
                        {
                            SLANG_RETURN_ON_FAIL(_parseTypeDef());
                        }
                        else
                        {
                            m_reader.advanceToken();
                            SLANG_RETURN_ON_FAIL(_consumeToSync());
                        }
                        break;
                    }
                default:
                    {
                        IdentifierFlags flags = getFlags(style);

                        if (flags & IdentifierFlag::StartScope)
                        {
                            Node::Kind kind = _toNodeKind(style);
                            SLANG_ASSERT(kind != Node::Kind::Invalid);

                            if (isTypeEnabled(kind))
                            {
                                SLANG_RETURN_ON_FAIL(_maybeParseNode(kind));
                            }
                            else
                            {
                                SLANG_RETURN_ON_FAIL(_maybeConsumeScope());
                            }
                        }
                        else
                        {
                            UnownedStringSlice content = m_reader.peekToken().getContent();

                            // If it's a marker handle it
                            if (_isMarker(content))
                            {
                                if (!m_currentScope->isClassLike())
                                {
                                    m_sink->diagnose(
                                        m_reader.peekLoc(),
                                        CPPDiagnostics::classMarkerOutsideOfClass);
                                    return SLANG_FAIL;
                                }

                                SLANG_RETURN_ON_FAIL(_parseMarker());
                                break;
                            }

                            if (m_options->m_markPrefix.getLength() > 0 &&
                                content.startsWith(m_options->m_markPrefix.getUnownedSlice()))
                            {
                                SLANG_RETURN_ON_FAIL(_parseSpecialMacro());
                                break;
                            }


                            // Special case the node that's the root of the hierarchy (as far as
                            // reflection is concerned) This could be a field
                            if (m_currentScope->canContainFields() ||
                                m_currentScope->canContainCallable())
                            {
                                Node* containedNode = nullptr;
                                SLANG_RETURN_ON_FAIL(_maybeParseContained(&containedNode));
                            }
                            else
                            {
                                m_reader.advanceToken();
                            }
                        }
                        break;
                    }
                }
                break;
            }
        case TokenType::LBrace:
            {
                SLANG_RETURN_ON_FAIL(consumeToClosingBrace());
                break;
            }
        case TokenType::RBrace:
            {
                SLANG_RETURN_ON_FAIL(popScope());
                m_reader.advanceToken();
                break;
            }
        case TokenType::EndOfFile:
            {
                // Okay we need to confirm that we are in the root node, and with no open braces
                if (m_currentScope != m_nodeTree->getRootNode())
                {
                    m_sink->diagnose(m_reader.peekToken(), CPPDiagnostics::braceOpenAtEndOfFile);
                    return SLANG_FAIL;
                }
                if (m_sink->getErrorCount())
                    return SLANG_FAIL;
                return SLANG_OK;
            }
        case TokenType::Pound:
            {
                Token token = m_reader.peekToken();
                if (token.flags & TokenFlag::AtStartOfLine)
                {
                    // We are just going to ignore all of these for now....
                    m_reader.advanceToken();
                    for (;;)
                    {
                        auto t = m_reader.peekToken();
                        if (t.type == TokenType::EndOfFile || (t.flags & TokenFlag::AtStartOfLine))
                        {
                            break;
                        }
                        m_reader.advanceToken();
                    }
                    break;
                }
                // Skip it then
                m_reader.advanceToken();
                break;
            }
        default:
            {
                // Skip it then
                m_reader.advanceToken();
                break;
            }
        }
    }
}

} // namespace CppParse
