File: Syntax\InternalSyntax\SyntaxToken.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports Microsoft.CodeAnalysis.Syntax.InternalSyntax
Imports CoreInternalSyntax = Microsoft.CodeAnalysis.Syntax.InternalSyntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
    Partial Friend Class SyntaxToken
        Inherits VisualBasicSyntaxNode
 
        Private ReadOnly _text As String
        Private ReadOnly _trailingTriviaOrTriviaInfo As Object
 
        Friend Class TriviaInfo
            Private Sub New(leadingTrivia As GreenNode, trailingTrivia As GreenNode)
                Me._leadingTrivia = leadingTrivia
                Me._trailingTrivia = trailingTrivia
            End Sub
 
            Private Const s_maximumCachedTriviaWidth As Integer = 40
            Private Const s_triviaInfoCacheSize As Integer = 64
 
            Private Shared ReadOnly s_triviaKeyHasher As Func(Of GreenNode, Integer) =
            Function(key) Hash.Combine(key.ToFullString(), CShort(key.RawKind))
 
            Private Shared ReadOnly s_triviaKeyEquality As Func(Of GreenNode, TriviaInfo, Boolean) =
            Function(key, value) (key Is value._leadingTrivia) OrElse ((key.RawKind = value._leadingTrivia.RawKind) AndAlso (key.FullWidth = value._leadingTrivia.FullWidth) AndAlso (key.ToFullString() = value._leadingTrivia.ToFullString()))
 
            Private Shared ReadOnly s_triviaInfoCache As CachingFactory(Of GreenNode, TriviaInfo) = New CachingFactory(Of GreenNode, TriviaInfo)(s_triviaInfoCacheSize, Nothing, s_triviaKeyHasher, s_triviaKeyEquality)
 
            Private Shared Function ShouldCacheTriviaInfo(leadingTrivia As GreenNode, trailingTrivia As GreenNode) As Boolean
                Debug.Assert(leadingTrivia IsNot Nothing)
 
                If trailingTrivia Is Nothing Then
 
                    ' Leading doc comment (which may include whitespace). No trailing trivia.
                    Return leadingTrivia.RawKind = SyntaxKind.DocumentationCommentExteriorTrivia AndAlso leadingTrivia.Flags = NodeFlags.IsNotMissing AndAlso
                        leadingTrivia.FullWidth <= s_maximumCachedTriviaWidth
 
                Else
 
                    ' Leading whitespace and a trailing single space.
                    Return leadingTrivia.RawKind = SyntaxKind.WhitespaceTrivia AndAlso leadingTrivia.Flags = NodeFlags.IsNotMissing AndAlso
                         trailingTrivia.RawKind = SyntaxKind.WhitespaceTrivia AndAlso trailingTrivia.Flags = NodeFlags.IsNotMissing AndAlso
                         trailingTrivia.FullWidth = 1 AndAlso trailingTrivia.ToFullString() = " " AndAlso
                         leadingTrivia.FullWidth <= s_maximumCachedTriviaWidth
 
                End If
 
            End Function
 
            Public Shared Function Create(leadingTrivia As GreenNode, trailingTrivia As GreenNode) As TriviaInfo
                Debug.Assert(leadingTrivia IsNot Nothing)
 
                ' PERF: Cache common kinds of TriviaInfo
                If ShouldCacheTriviaInfo(leadingTrivia, trailingTrivia) Then
                    Dim retVal As TriviaInfo = Nothing
                    SyncLock s_triviaInfoCache
                        ' Note: Only the leading trivia is considered as a key into the cache. That works because
                        ' we cache only a couple of different cases. In one case, the trailing trivia is Nothing
                        ' and in the other it's a single space, and the two cases can be distinguished just by
                        ' examining the Kind on the leading trivia. If we ever decide to cache more kinds of trivia
                        ' this may have to be revisited.
                        If s_triviaInfoCache.TryGetValue(leadingTrivia, retVal) Then
                            Debug.Assert(trailingTrivia Is Nothing OrElse retVal._trailingTrivia.IsEquivalentTo(trailingTrivia))
                            Debug.Assert(retVal._leadingTrivia.IsEquivalentTo(leadingTrivia))
                        Else
                            retVal = New TriviaInfo(leadingTrivia, trailingTrivia)
                            s_triviaInfoCache.Add(leadingTrivia, retVal)
                        End If
                    End SyncLock
 
                    Return retVal
                End If
 
                Return New TriviaInfo(leadingTrivia, trailingTrivia)
            End Function
 
            Public _leadingTrivia As GreenNode
            Public _trailingTrivia As GreenNode
        End Class
 
        Protected Sub New(kind As SyntaxKind, text As String, precedingTrivia As GreenNode, followingTrivia As GreenNode)
            MyBase.New(kind, text.Length)
            Me.SetFlags(NodeFlags.IsNotMissing)
 
            Me._text = text
            If followingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(followingTrivia)
                Me._trailingTriviaOrTriviaInfo = followingTrivia
            End If
 
            If precedingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(precedingTrivia)
                Me._trailingTriviaOrTriviaInfo = TriviaInfo.Create(precedingTrivia, DirectCast(Me._trailingTriviaOrTriviaInfo, GreenNode))
            End If
 
            ClearFlagIfMissing()
        End Sub
 
        Protected Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), text As String, precedingTrivia As GreenNode, followingTrivia As GreenNode)
            MyBase.New(kind, errors, text.Length)
            Me.SetFlags(NodeFlags.IsNotMissing)
 
            Me._text = text
            If followingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(followingTrivia)
                Me._trailingTriviaOrTriviaInfo = followingTrivia
            End If
 
            If precedingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(precedingTrivia)
                Me._trailingTriviaOrTriviaInfo = TriviaInfo.Create(precedingTrivia, DirectCast(Me._trailingTriviaOrTriviaInfo, GreenNode))
            End If
 
            ClearFlagIfMissing()
        End Sub
 
        Protected Sub New(kind As SyntaxKind, errors As DiagnosticInfo(), annotations As SyntaxAnnotation(), text As String, precedingTrivia As GreenNode, followingTrivia As GreenNode)
            MyBase.New(kind, errors, annotations, text.Length)
            Me.SetFlags(NodeFlags.IsNotMissing)
 
            Me._text = text
            If followingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(followingTrivia)
                Me._trailingTriviaOrTriviaInfo = followingTrivia
            End If
 
            If precedingTrivia IsNot Nothing Then
                ' Don't propagate NoMissing in Init
                AdjustFlagsAndWidth(precedingTrivia)
                Me._trailingTriviaOrTriviaInfo = TriviaInfo.Create(precedingTrivia, DirectCast(Me._trailingTriviaOrTriviaInfo, GreenNode))
            End If
 
            ClearFlagIfMissing()
        End Sub
 
        Private Sub ClearFlagIfMissing()
            If Text.Length = 0 AndAlso Kind <> SyntaxKind.EndOfFileToken AndAlso Kind <> SyntaxKind.EmptyToken Then
                ' If a token has text then it is not missing.  The only 0 length tokens that are not considered missing are the end of file token because no text exists for this token 
                ' and the empty token which exists solely so that the empty statement has a token.
                Me.ClearFlags(NodeFlags.IsNotMissing)
            End If
        End Sub
 
        Friend ReadOnly Property Text As String
            Get
                Return Me._text
            End Get
        End Property
 
        Friend NotOverridable Overrides Function GetSlot(index As Integer) As GreenNode
            Throw ExceptionUtilities.Unreachable
        End Function
 
        ' Get the leading trivia as GreenNode array.
        Friend NotOverridable Overrides Function GetLeadingTrivia() As GreenNode
            Dim t = TryCast(_trailingTriviaOrTriviaInfo, TriviaInfo)
            If t IsNot Nothing Then
                Return t._leadingTrivia
            End If
            Return Nothing
        End Function
 
        Private ReadOnly Property _leadingTriviaWidth() As Integer
            Get
                Dim t = TryCast(_trailingTriviaOrTriviaInfo, TriviaInfo)
                If t IsNot Nothing Then
                    Return t._leadingTrivia.FullWidth
                End If
                Return 0
            End Get
        End Property
 
        ' Get the width of the leading trivia
        Public NotOverridable Overrides Function GetLeadingTriviaWidth() As Integer
            Return _leadingTriviaWidth()
        End Function
 
        ' Get the following trivia as GreenNode array.
        Friend NotOverridable Overrides Function GetTrailingTrivia() As GreenNode
            Dim arr = TryCast(_trailingTriviaOrTriviaInfo, GreenNode)
            If arr IsNot Nothing Then
                Return arr
            End If
            Dim t = TryCast(_trailingTriviaOrTriviaInfo, TriviaInfo)
            If t IsNot Nothing Then
                Return t._trailingTrivia
            End If
            Return Nothing
        End Function
 
        ' Get the width of the following trivia
        Public NotOverridable Overrides Function GetTrailingTriviaWidth() As Integer
            Return FullWidth - _text.Length - _leadingTriviaWidth()
        End Function
 
        Friend NotOverridable Overrides Sub AddSyntaxErrors(accumulatedErrors As List(Of DiagnosticInfo))
            If Me.GetDiagnostics IsNot Nothing Then
                accumulatedErrors.AddRange(Me.GetDiagnostics)
            End If
 
            Dim leadingTrivia = GetLeadingTrivia()
            If leadingTrivia IsNot Nothing Then
                Dim triviaList = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(leadingTrivia)
                For i = 0 To triviaList.Count - 1
                    DirectCast(triviaList.ItemUntyped(i), VisualBasicSyntaxNode).AddSyntaxErrors(accumulatedErrors)
                Next
            End If
            Dim trailingTrivia = GetTrailingTrivia()
            If trailingTrivia IsNot Nothing Then
                Dim triviaList = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(trailingTrivia)
                For i = 0 To triviaList.Count - 1
                    DirectCast(triviaList.ItemUntyped(i), VisualBasicSyntaxNode).AddSyntaxErrors(accumulatedErrors)
                Next
            End If
        End Sub
 
        Protected Overrides Sub WriteTokenTo(writer As System.IO.TextWriter, leading As Boolean, trailing As Boolean)
            If leading Then
                Dim leadingTrivia = GetLeadingTrivia()
                If leadingTrivia IsNot Nothing Then
                    leadingTrivia.WriteTo(writer, True, True) 'Append leading trivia
                End If
            End If
 
            writer.Write(Me.Text) 'Append text of token itself
 
            If trailing Then
                Dim trailingTrivia = GetTrailingTrivia()
                If trailingTrivia IsNot Nothing Then
                    trailingTrivia.WriteTo(writer, True, True) ' Append trailing trivia
                End If
            End If
        End Sub
 
        Public NotOverridable Overrides Function Accept(visitor As VisualBasicSyntaxVisitor) As VisualBasicSyntaxNode
            Return visitor.VisitSyntaxToken(Me)
        End Function
 
        Public Overrides Function ToString() As String
            Return Me._text
        End Function
 
        Public NotOverridable Overrides ReadOnly Property IsToken As Boolean
            Get
                Return True
            End Get
        End Property
 
        ''' <summary>
        ''' Helper to check whether the token is a keyword
        ''' </summary>
        Friend Overridable ReadOnly Property IsKeyword As Boolean
            Get
                Return False
            End Get
        End Property
 
        Friend Overridable ReadOnly Property ObjectValue As Object
            Get
                Return ValueText
            End Get
        End Property
 
        Public Overrides Function GetValue() As Object
            Return Me.ObjectValue
        End Function
 
        Friend Overridable ReadOnly Property ValueText As String
            Get
                Return Text
            End Get
        End Property
 
        Public Overrides Function GetValueText() As String
            Return Me.ValueText
        End Function
 
        ''' <summary>
        ''' Helpers to check whether the token is a binary operator
        ''' </summary>
        ''' <returns>True if it is a binary operator</returns>
        Public Function IsBinaryOperator() As Boolean
            Select Case Kind
                Case SyntaxKind.MinusToken,
                    SyntaxKind.PlusToken,
                    SyntaxKind.AsteriskToken,
                    SyntaxKind.SlashToken,
                    SyntaxKind.BackslashToken,
                    SyntaxKind.CaretToken,
                    SyntaxKind.AmpersandToken,
                    SyntaxKind.LessThanLessThanToken,
                    SyntaxKind.GreaterThanGreaterThanToken,
                    SyntaxKind.ModKeyword,
                    SyntaxKind.OrKeyword,
                    SyntaxKind.OrElseKeyword,
                    SyntaxKind.XorKeyword,
                    SyntaxKind.AndKeyword,
                    SyntaxKind.AndAlsoKeyword,
                    SyntaxKind.LikeKeyword,
                    SyntaxKind.EqualsToken,
                    SyntaxKind.LessThanGreaterThanToken,
                    SyntaxKind.LessThanToken,
                    SyntaxKind.LessThanEqualsToken,
                    SyntaxKind.GreaterThanToken,
                    SyntaxKind.GreaterThanEqualsToken,
                    SyntaxKind.IsKeyword,
                    SyntaxKind.IsNotKeyword
                    Return True
            End Select
 
            Return False
        End Function
 
        ''' <summary>
        ''' Check whether the token is a statement terminator
        ''' </summary>
        ''' <returns>True if it is statement terminator</returns>
        Friend ReadOnly Property IsEndOfLine As Boolean
            Get
                Return Kind = SyntaxKind.StatementTerminatorToken OrElse Kind = SyntaxKind.EndOfFileToken
            End Get
        End Property
 
        ''' <summary>
        ''' Check whether token is end of text
        ''' </summary>
        Friend ReadOnly Property IsEndOfParse As Boolean
            Get
                Return Kind = SyntaxKind.EndOfFileToken
            End Get
        End Property
 
        ''' <summary>
        ''' Create a new token with the trivia prepended to the existing preceding trivia
        ''' </summary>
        Public Shared Function AddLeadingTrivia(Of T As SyntaxToken)(token As T, newTrivia As CoreInternalSyntax.SyntaxList(Of GreenNode)) As T
            Debug.Assert(token IsNot Nothing)
 
            If newTrivia.Node Is Nothing Then
                Return token
            End If
 
            Dim oldTrivia = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(token.GetLeadingTrivia())
            Dim leadingTrivia As GreenNode
 
            If oldTrivia.Node Is Nothing Then
                leadingTrivia = newTrivia.Node
            Else
                Dim leadingTriviaBuilder = SyntaxListBuilder(Of VisualBasicSyntaxNode).Create()
                leadingTriviaBuilder.AddRange(newTrivia)
 
                leadingTriviaBuilder.AddRange(oldTrivia)
                leadingTrivia = leadingTriviaBuilder.ToList.Node
            End If
 
            Return DirectCast(token.WithLeadingTrivia(leadingTrivia), T)
        End Function
 
        ''' <summary>
        ''' Create a new token with the trivia appended to the existing following trivia
        ''' </summary>
        Public Shared Function AddTrailingTrivia(Of T As SyntaxToken)(token As T, newTrivia As CoreInternalSyntax.SyntaxList(Of GreenNode)) As T
            Debug.Assert(token IsNot Nothing)
 
            If newTrivia.Node Is Nothing Then
                Return token
            End If
 
            Dim oldTrivia = New CoreInternalSyntax.SyntaxList(Of VisualBasicSyntaxNode)(token.GetTrailingTrivia())
            Dim trailingTrivia As GreenNode
 
            If oldTrivia.Node Is Nothing Then
                trailingTrivia = newTrivia.Node
            Else
                Dim trailingTriviaBuilder = SyntaxListBuilder(Of VisualBasicSyntaxNode).Create()
                trailingTriviaBuilder.AddRange(oldTrivia)
 
                trailingTriviaBuilder.AddRange(newTrivia)
                trailingTrivia = trailingTriviaBuilder.ToList.Node
            End If
 
            Return DirectCast(token.WithTrailingTrivia(trailingTrivia), T)
        End Function
 
        Friend Shared Function Create(kind As SyntaxKind, Optional leading As GreenNode = Nothing, Optional trailing As GreenNode = Nothing, Optional text As String = Nothing) As SyntaxToken
 
            ' use default token text if text is nothing. If it's empty or anything else, use the given one
            Dim tokenText = If(text Is Nothing, SyntaxFacts.GetText(kind), text)
 
            If CInt(kind) >= SyntaxKind.AddHandlerKeyword Then
 
                If CInt(kind) <= SyntaxKind.YieldKeyword OrElse kind = SyntaxKind.NameOfKeyword Then
                    Return New KeywordSyntax(kind, tokenText, leading, trailing)
                ElseIf CInt(kind) <= SyntaxKind.EndOfXmlToken OrElse
                       kind = SyntaxKind.EndOfInterpolatedStringToken OrElse
                       kind = SyntaxKind.DollarSignDoubleQuoteToken _
                Then
                    Return New PunctuationSyntax(kind, tokenText, leading, trailing)
                End If
            End If
 
            Throw ExceptionUtilities.UnexpectedValue(kind)
        End Function
 
        Public Shared Narrowing Operator CType(token As SyntaxToken) As Microsoft.CodeAnalysis.SyntaxToken
            Return New Microsoft.CodeAnalysis.SyntaxToken(Nothing, token, position:=0, index:=0)
        End Operator
 
        Public Overrides Function IsEquivalentTo(other As GreenNode) As Boolean
            If Not MyBase.IsEquivalentTo(other) Then
                Return False
            End If
 
            Dim otherToken = DirectCast(other, SyntaxToken)
 
            If Not String.Equals(Me.Text, otherToken.Text, StringComparison.Ordinal) Then
                Return False
            End If
 
            If Me.HasLeadingTrivia <> otherToken.HasLeadingTrivia OrElse
               Me.HasTrailingTrivia <> otherToken.HasTrailingTrivia Then
                Return False
            End If
 
            If Me.HasLeadingTrivia AndAlso Not Me.GetLeadingTrivia().IsEquivalentTo(otherToken.GetLeadingTrivia()) Then
                Return False
            End If
 
            If Me.HasTrailingTrivia AndAlso Not Me.GetTrailingTrivia().IsEquivalentTo(otherToken.GetTrailingTrivia()) Then
                Return False
            End If
 
            Return True
        End Function
 
        Friend Overrides Function CreateRed(parent As SyntaxNode, position As Integer) As SyntaxNode
            Throw ExceptionUtilities.Unreachable
        End Function
    End Class
 
    Partial Friend Class XmlTextTokenSyntax
        Friend NotOverridable Overrides ReadOnly Property ValueText As String
            Get
                Return Me.Value
            End Get
        End Property
    End Class
 
    Partial Friend Class InterpolatedStringTextTokenSyntax
        Friend NotOverridable Overrides ReadOnly Property ValueText As String
            Get
                Return Me.Value
            End Get
        End Property
    End Class
 
    Partial Friend Class KeywordSyntax
 
        Friend NotOverridable Overrides ReadOnly Property ObjectValue As Object
            Get
                Select Case MyBase.Kind
                    Case SyntaxKind.NothingKeyword
                        Return Nothing
                    Case SyntaxKind.TrueKeyword
                        Return Boxes.BoxedTrue
                    Case SyntaxKind.FalseKeyword
                        Return Boxes.BoxedFalse
                    Case Else
                        Return Me.Text
                End Select
            End Get
        End Property
 
        Friend NotOverridable Overrides ReadOnly Property IsKeyword As Boolean
            Get
                Return True
            End Get
        End Property
    End Class
End Namespace