public virtual void VisitLifetimeName(ReferenceLifetimeSyntax referenceLifetime, A args)
 {
     VisitExpression(referenceLifetime.ReferentTypeExpression, args);
 }
        /// <summary>
        /// For expressions, we switch to a precedence climbing parser.
        /// </summary>
        public ExpressionSyntax ParseExpression(OperatorPrecedence minPrecedence)
        {
            var expression = ParseAtom();

            for (; ;)
            {
                IOperatorToken     @operator  = null;
                OperatorPrecedence?precedence = null;
                var leftAssociative           = true;
                switch (Tokens.Current)
                {
                case IEqualsToken _:
                case IPlusEqualsToken _:
                case IMinusEqualsToken _:
                case IAsteriskEqualsToken _:
                case ISlashEqualsToken _:
                    if (minPrecedence <= OperatorPrecedence.Assignment)
                    {
                        precedence      = OperatorPrecedence.Assignment;
                        leftAssociative = false;
                        @operator       = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IQuestionQuestionToken _:
                    if (minPrecedence <= OperatorPrecedence.Coalesce)
                    {
                        precedence = OperatorPrecedence.Coalesce;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IOrKeywordToken _:
                    if (minPrecedence <= OperatorPrecedence.LogicalOr)
                    {
                        precedence = OperatorPrecedence.LogicalOr;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IAndKeywordToken _:
                    if (minPrecedence <= OperatorPrecedence.LogicalAnd)
                    {
                        precedence = OperatorPrecedence.LogicalAnd;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IEqualsEqualsToken _:
                case INotEqualToken _:
                    if (minPrecedence <= OperatorPrecedence.Equality)
                    {
                        precedence = OperatorPrecedence.Equality;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case ILessThanToken _:
                case ILessThanOrEqualToken _:
                case IGreaterThanToken _:
                case IGreaterThanOrEqualToken _:
                case ILessThanColonToken _:     // Subtype operator
                case IAsKeywordToken _:
                    if (minPrecedence <= OperatorPrecedence.Relational)
                    {
                        precedence = OperatorPrecedence.Relational;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IColonToken _:     // type kind
                    if (minPrecedence <= OperatorPrecedence.Relational)
                    {
                        var      colon = Tokens.Expect <IColonToken>();
                        TypeKind typeKind;
                        switch (Tokens.Current)
                        {
                        case IClassKeywordToken _:
                            typeKind = TypeKind.Class;
                            break;

                        case IStructKeywordToken _:
                            typeKind = TypeKind.Struct;
                            break;

                        default:
                            Tokens.Expect <ITypeKindKeywordToken>();
                            // We saw a colon without what we expected after, just assume it is missing
                            continue;
                        }
                        var typeKindSpan = Tokens.Expect <ITypeKindKeywordToken>();
                        var span         = TextSpan.Covering(colon, typeKindSpan);
                        expression = new TypeKindExpressionSyntax(span, typeKind);
                        continue;
                    }
                    break;

                case IDotDotToken _:
                case ILessThanDotDotToken _:
                case IDotDotLessThanToken _:
                case ILessThanDotDotLessThanToken _:
                    if (minPrecedence <= OperatorPrecedence.Range)
                    {
                        precedence = OperatorPrecedence.Range;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IPlusToken _:
                case IMinusToken _:
                    if (minPrecedence <= OperatorPrecedence.Additive)
                    {
                        precedence = OperatorPrecedence.Additive;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IAsteriskToken _:
                case ISlashToken _:
                    if (minPrecedence <= OperatorPrecedence.Multiplicative)
                    {
                        precedence = OperatorPrecedence.Multiplicative;
                        @operator  = Tokens.RequiredToken <IOperatorToken>();
                    }
                    break;

                case IDollarToken _:
                    if (minPrecedence <= OperatorPrecedence.Lifetime)
                    {
                        Tokens.Expect <IDollarToken>();
                        var(nameSpan, lifetime) = ParseLifetimeName();
                        expression = new ReferenceLifetimeSyntax(expression, nameSpan, lifetime);
                        continue;
                    }
                    break;

                case IQuestionToken _:
                    if (minPrecedence <= OperatorPrecedence.Unary)
                    {
                        var question = Tokens.Required <IQuestionToken>();
                        var span     = TextSpan.Covering(expression.Span, question);
                        expression = new UnaryExpressionSyntax(span, UnaryOperatorFixity.Postfix, UnaryOperator.Question, expression);
                        continue;
                    }
                    break;

                case IOpenParenToken _:
                    if (minPrecedence <= OperatorPrecedence.Primary)
                    {
                        var callee = expression;
                        Tokens.Expect <IOpenParenToken>();
                        var arguments      = ParseArguments();
                        var closeParenSpan = Tokens.Expect <ICloseParenToken>();
                        var span           = TextSpan.Covering(callee.Span, closeParenSpan);
                        expression = new InvocationSyntax(span, callee, arguments);
                        continue;
                    }
                    break;

                case IDotToken _:
                case ICaretDotToken _:
                case IQuestionDotToken _:
                    if (minPrecedence <= OperatorPrecedence.Primary)
                    {
                        // Member Access
                        var accessOperator = BuildAccessOperator(Tokens.RequiredToken <IAccessOperatorToken>());
                        var member         = ParseSimpleName();
                        var span           = TextSpan.Covering(expression.Span, member.Span);
                        expression = new MemberAccessExpressionSyntax(span, expression, accessOperator, member);
                        continue;
                    }
                    break;

                default:
                    return(expression);
                }

                if (@operator is IOperatorToken operatorToken &&
                    precedence is OperatorPrecedence operatorPrecedence)
                {
                    if (leftAssociative)
                    {
                        operatorPrecedence += 1;
                    }

                    var rightOperand = ParseExpression(operatorPrecedence);
                    expression = BuildOperatorExpression(expression, operatorToken, rightOperand);
                }