diff --git a/src/Compilers/VisualBasic/Portable/Scanner/CharacterInfo.vb b/src/Compilers/VisualBasic/Portable/Scanner/CharacterInfo.vb index 7c3b0f7b378cc..ad20b587797ab 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/CharacterInfo.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/CharacterInfo.vb @@ -488,4 +488,37 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class + Friend Module CharExts + + + Friend Function IsAnyOf(c As Char, c0 As Char, c1 As Char) As Boolean + Return (c = c0) OrElse (c = c1) + End Function + + + Friend Function IsAnyOf(c As Char, c0 As Char, c1 As Char, c2 As Char) As Boolean + Return (c = c0) OrElse (c = c1) OrElse (c = c2) + End Function + + + Friend Function IsAnyOf(c As Char, c0 As Char, c1 As Char, c2 As Char, c3 As Char) As Boolean + Return (c = c0) OrElse (c = c1) OrElse (c = c2) OrElse (c = c3) + End Function + + + Friend Function IsNoneOf(c As Char, c0 As Char, c1 As Char) As Boolean + Return (c <> c0) AndAlso (c <> c1) + End Function + + + Friend Function IsNoneOf(c As Char, c0 As Char, c1 As Char, c2 As Char) As Boolean + Return (c <> c0) AndAlso (c <> c1) AndAlso (c <> c2) + End Function + + + Friend Function IsNoneOf(c As Char, c0 As Char, c1 As Char, c2 As Char, c3 As Char) As Boolean + Return (c <> c0) AndAlso (c <> c1) AndAlso (c <> c2) AndAlso (c <> c3) + End Function + End Module + End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Scanner/KeywordTable.vb b/src/Compilers/VisualBasic/Portable/Scanner/KeywordTable.vb index 744f74ce0e072..81439b7429357 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/KeywordTable.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/KeywordTable.vb @@ -304,9 +304,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax Private Shared Function EnsureHalfWidth(s As String) As String Dim result As Char() = Nothing - + Dim ch As Char For i As Integer = 0 To s.Length - 1 - Dim ch = s(i) + ch = s(i) If SyntaxFacts.IsFullWidth(ch) Then ch = SyntaxFacts.MakeHalfWidth(ch) diff --git a/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb b/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb index 00af359223d69..9c81053ce6096 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/Scanner.vb @@ -222,13 +222,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Private Function ScanNextCharAsToken(leadingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken Dim token As SyntaxToken - - If Not CanGet() Then + Dim c As Char + If Not TryPeek(c) Then token = MakeEofToken(leadingTrivia) Else ' // Don't break up surrogate pairs - Dim c = Peek() - Dim length = If(IsHighSurrogate(c) AndAlso CanGet(1) AndAlso IsLowSurrogate(Peek(1)), 2, 1) + Dim length = If(IsHighSurrogate(c) AndAlso TryPeek(1,c) AndAlso IsLowSurrogate(c), 2, 1) token = MakeBadToken(leadingTrivia, length, ERRID.ERR_IllegalChar) End If @@ -255,9 +254,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End If Dim condLineStart = _lineBufferOffset - - While (CanGet()) - Dim c As Char = Peek() + Dim c As Char + While TryPeek(c) Select Case (c) @@ -271,12 +269,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax EatWhitespace() Continue While - Case _ - "a"c, "b"c, "c"c, "d"c, "e"c, "f"c, "g"c, "h"c, "i"c, "j"c, "k"c, "l"c, - "m"c, "n"c, "o"c, "p"c, "q"c, "r"c, "s"c, "t"c, "u"c, "v"c, "w"c, "x"c, - "y"c, "z"c, "A"c, "B"c, "C"c, "D"c, "E"c, "F"c, "G"c, "H"c, "I"c, "J"c, - "K"c, "L"c, "M"c, "N"c, "O"c, "P"c, "Q"c, "R"c, "S"c, "T"c, "U"c, "V"c, - "W"c, "X"c, "Y"c, "Z"c, "'"c, "_"c + Case "a"c, "b"c, "c"c, "d"c, "e"c, "f"c, "g"c, "h"c, "i"c, "j"c, "k"c, "l"c, + "m"c, "n"c, "o"c, "p"c, "q"c, "r"c, "s"c, "t"c, "u"c, "v"c, "w"c, "x"c, + "y"c, "z"c, "A"c, "B"c, "C"c, "D"c, "E"c, "F"c, "G"c, "H"c, "I"c, "J"c, + "K"c, "L"c, "M"c, "N"c, "O"c, "P"c, "Q"c, "R"c, "S"c, "T"c, "U"c, "V"c, + "W"c, "X"c, "Y"c, "Z"c, "'"c, "_"c EatThroughLine() condLineStart = _lineBufferOffset @@ -312,9 +309,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Sub EatThroughLine() - While CanGet() - Dim c As Char = Peek() - + Dim c As Char + While TryPeek(c) If IsNewLine(c) Then EatThroughLineBreak(c) Return @@ -471,10 +467,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function LengthOfLineBreak(StartCharacter As Char, Optional here As Integer = 0) As Integer - Debug.Assert(CanGet(here)) + Dim c As Char + Dim res = TryPeek(here,c) + Debug.Assert(res) Debug.Assert(IsNewLine(StartCharacter)) - Debug.Assert(StartCharacter = Peek(here)) + Debug.Assert(StartCharacter = c) If StartCharacter = CARRIAGE_RETURN AndAlso NextIs(here + 1, LINE_FEED) Then Return 2 @@ -517,27 +515,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function ScanLineContinuation(tList As SyntaxListBuilder) As Boolean - If Not CanGet() Then - Return False - End If - - If Not IsAfterWhitespace() Then - Return False - End If - - Dim ch As Char = Peek() - If Not IsUnderscore(ch) Then + Dim ch As Char + If Not TryPeek(ch) OrElse Not IsAfterWhitespace() OrElse Not IsUnderscore(ch) Then Return False End If Dim Here = 1 - While CanGet(Here) - ch = Peek(Here) - If IsWhitespace(ch) Then - Here += 1 - Else - Exit While - End If + While TryPeek(Here,ch) AndAlso IsWhitespace(ch) + Here += 1 End While ' Line continuation is valid at the end of the @@ -562,9 +547,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' do not include the new line character since that would confuse code handling ' implicit line continuations. (See Scanner::EatLineContinuation.) Otherwise, ' include the new line and any additional spaces as trivia. - If startComment = 0 AndAlso - CanGet(Here) AndAlso - Not IsNewLine(Peek(Here)) Then + + If startComment = 0 AndAlso TryPeek(Here, ch) AndAlso Not IsNewLine(ch) Then tList.Add(MakeEndOfLineTrivia(GetText(newLine))) If spaces > 0 Then @@ -585,15 +569,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ''' Consumes all trivia until a nontrivia char is found ''' Friend Function ScanMultilineTrivia() As SyntaxList(Of VisualBasicSyntaxNode) - If Not CanGet() Then + Dim ch As Char + If Not TryPeek(ch) Then Return Nothing End If - Dim ch = Peek() - ' optimization for a common case ' the ASCII range between ': and ~ , with exception of except "'", "_" and R cannot start trivia - If ch > ":"c AndAlso ch <= "~"c AndAlso ch <> "'"c AndAlso ch <> "_"c AndAlso ch <> "R"c AndAlso ch <> "r"c Then + If ch > ":"c AndAlso ch <= "~"c AndAlso ch.IsNoneOf("'"c, "_"c, "R"c, "r"c) Then Return Nothing End If @@ -610,7 +593,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ''' Scans a single piece of trivia ''' Private Function TryScanSinglePieceOfMultilineTrivia(tList As SyntaxListBuilder) As Boolean - If CanGet() Then + Dim ch As Char + If TryPeek(ch) Then Dim atNewLine = IsAtNewLine() @@ -625,7 +609,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End If End If - Dim ch = Peek() If IsWhitespace(ch) Then ' eat until linebreak or nonwhitespace Dim wslen = GetWhitespaceLength(1) @@ -670,11 +653,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' check for # Private Function StartsDirective(Here As Integer) As Boolean - If CanGet(Here) Then - Dim ch = Peek(Here) - Return IsHash(ch) - End If - Return False + Dim ch As Char + Return TryPeek(Here,ch) AndAlso IsHash(ch) End Function Private Function IsAtNewLine() As Boolean @@ -714,8 +694,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Sub Private Sub ScanSingleLineTriviaInXmlDoc(tList As SyntaxListBuilder) - If CanGet() Then - Dim c As Char = Peek() + Dim c As Char + If TryPeek(c) Then Select Case (c) ' // Whitespace ' // S ::= (#x20 | #x9 | #xD | #xA)+ @@ -747,7 +727,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Sub ScanWhitespaceAndLineContinuations(tList As SyntaxListBuilder) - If CanGet() AndAlso IsWhitespace(Peek()) Then + Dim c As Char + If TryPeek(c) AndAlso IsWhitespace(c) Then tList.Add(ScanWhitespace(1)) ' collect { lineCont, ws } While ScanLineContinuation(tList) @@ -816,10 +797,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' Case 3 is required to parse single line if's and numeric labels. ' Case 4 is required to limit explicit line continuations to single new line - - If CanGet() Then - - Dim ch As Char = Peek() + Dim ch As Char + If TryPeek(ch) Then + Dim startOfTerminatorTrivia = _lineBufferOffset If IsNewLine(ch) Then @@ -831,11 +811,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' collect { ws, colon } Do Dim len = GetWhitespaceLength(0) - If Not CanGet(len) Then + If Not TryPeek(len, ch) Then Exit Do End If - ch = Peek(len) If Not IsColonAndNotColonEquals(ch, offset:=len) Then Exit Do End If @@ -871,7 +850,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Private Function GetWhitespaceLength(len As Integer) As Integer ' eat until linebreak or nonwhitespace - While CanGet(len) AndAlso IsWhitespace(Peek(len)) + Dim c As Char + While TryPeek(len, c) AndAlso IsWhitespace(c) len += 1 End While Return len @@ -879,7 +859,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Private Function GetXmlWhitespaceLength(len As Integer) As Integer ' eat until linebreak or nonwhitespace - While CanGet(len) AndAlso IsXmlWhitespace(Peek(len)) + Dim c As Char + While TryPeek(len, c) AndAlso IsXmlWhitespace(c) len += 1 End While Return len @@ -902,32 +883,33 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Sub EatWhitespace() - Debug.Assert(CanGet) - Debug.Assert(IsWhitespace(Peek())) + Dim c As Char + Dim ok = TryPeek(c) + Debug.Assert(ok) + Debug.Assert(IsWhitespace(c)) AdvanceChar() ' eat until linebreak or nonwhitespace - While CanGet() AndAlso IsWhitespace(Peek) + While TryPeek(c) AndAlso IsWhitespace(c) AdvanceChar() End While End Sub Private Function PeekStartComment(i As Integer) As Integer - - If CanGet(i) Then - Dim ch = Peek(i) - + Dim ch As Char + If TryPeek(i, ch) Then + Dim c2, c3 As Char If IsSingleQuote(ch) Then Return 1 ElseIf MatchOneOrAnotherOrFullwidth(ch, "R"c, "r"c) AndAlso - CanGet(i + 2) AndAlso MatchOneOrAnotherOrFullwidth(Peek(i + 1), "E"c, "e"c) AndAlso - MatchOneOrAnotherOrFullwidth(Peek(i + 2), "M"c, "m"c) Then + TryPeek(i + 2, c2) AndAlso MatchOneOrAnotherOrFullwidth(Peek(i + 1), "E"c, "e"c) AndAlso + MatchOneOrAnotherOrFullwidth(c2, "M"c, "m"c) Then - If Not CanGet(i + 3) OrElse IsNewLine(Peek(i + 3)) Then + If Not TryPeek(i + 3, c3) OrElse IsNewLine(c3) Then ' have only 'REM' Return 3 - ElseIf Not IsIdentifierPartCharacter(Peek(i + 3)) Then + ElseIf Not IsIdentifierPartCharacter(c3) Then ' have 'REM ' Return 4 End If @@ -945,9 +927,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim looksLikeDocComment As Boolean = StartsXmlDoc(0) ' eat all chars until EoL - While CanGet(length) AndAlso - Not IsNewLine(Peek(length)) - + Dim c As Char + While TryPeek(length, c) AndAlso Not IsNewLine(c) length += 1 End While @@ -979,450 +960,191 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax #End Region - ' at this point it is very likely that we are located at - ' the beginning of a token - Private Function TryScanToken(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken - - If Not CanGet() Then - Return MakeEofToken(precedingTrivia) - End If - - Dim ch As Char = Peek() + Private Function ScanTokenCommon(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), ch As Char, fullWidth As Boolean) As SyntaxToken + Dim lengthWithMaybeEquals As Integer = 1 + Dim c As Char Select Case ch - Case CARRIAGE_RETURN, LINE_FEED, NEXT_LINE, LINE_SEPARATOR, PARAGRAPH_SEPARATOR + Case CARRIAGE_RETURN, LINE_FEED + Return ScanNewlineAsStatementTerminator(ch, precedingTrivia) + Case NEXT_LINE, LINE_SEPARATOR, PARAGRAPH_SEPARATOR + If fullWidth Then Exit Select Return ScanNewlineAsStatementTerminator(ch, precedingTrivia) - Case " "c, CHARACTER_TABULATION, "'"c Debug.Assert(False, String.Format("Unexpected char: &H{0:x}", AscW(ch))) Return Nothing ' trivia cannot start a token - Case "@"c - Return MakeAtToken(precedingTrivia, False) - + Return MakeAtToken(precedingTrivia, fullWidth) Case "("c - Return MakeOpenParenToken(precedingTrivia, False) - + Return MakeOpenParenToken(precedingTrivia, fullWidth) Case ")"c - Return MakeCloseParenToken(precedingTrivia, False) - + Return MakeCloseParenToken(precedingTrivia, fullWidth) Case "{"c - Return MakeOpenBraceToken(precedingTrivia, False) - + Return MakeOpenBraceToken(precedingTrivia, fullWidth) Case "}"c - Return MakeCloseBraceToken(precedingTrivia, False) - + Return MakeCloseBraceToken(precedingTrivia, fullWidth) Case ","c - Return MakeCommaToken(precedingTrivia, False) - + Return MakeCommaToken(precedingTrivia, fullWidth) Case "#"c Dim dl = ScanDateLiteral(precedingTrivia) - If dl IsNot Nothing Then - Return dl - Else - Return MakeHashToken(precedingTrivia, False) - End If - + Return If(dl, MakeHashToken(precedingTrivia, fullWidth)) Case "&"c - If CanGet(1) AndAlso BeginsBaseLiteral(Peek(1)) Then + If TryPeek(1, c) AndAlso BeginsBaseLiteral(c) Then Return ScanNumericLiteral(precedingTrivia) End If - - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeAmpersandEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeAmpersandToken(precedingTrivia, False) + Return MakeAmpersandToken(precedingTrivia, fullWidth) End If - Case "="c - Return MakeEqualsToken(precedingTrivia, False) - + Return MakeEqualsToken(precedingTrivia, fullWidth) Case "<"c - Return ScanLeftAngleBracket(precedingTrivia, False, _scanSingleLineTriviaFunc) - + Return ScanLeftAngleBracket(precedingTrivia, fullWidth, _scanSingleLineTriviaFunc) Case ">"c - Return ScanRightAngleBracket(precedingTrivia, False) - + Return ScanRightAngleBracket(precedingTrivia, fullWidth) Case ":"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeColonEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return ScanColonAsStatementTerminator(precedingTrivia, False) + Return ScanColonAsStatementTerminator(precedingTrivia, fullWidth) End If - Case "+"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakePlusEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakePlusToken(precedingTrivia, False) + Return MakePlusToken(precedingTrivia, fullWidth) End If - Case "-"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeMinusEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeMinusToken(precedingTrivia, False) + Return MakeMinusToken(precedingTrivia, fullWidth) End If - Case "*"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeAsteriskEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeAsteriskToken(precedingTrivia, False) + Return MakeAsteriskToken(precedingTrivia, fullWidth) End If - Case "/"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeSlashEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeSlashToken(precedingTrivia, False) + Return MakeSlashToken(precedingTrivia, fullWidth) End If - Case "\"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeBackSlashEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeBackslashToken(precedingTrivia, False) + Return MakeBackslashToken(precedingTrivia, fullWidth) End If - Case "^"c - Dim lengthWithMaybeEquals = 1 If TrySkipFollowingEquals(lengthWithMaybeEquals) Then Return MakeCaretEqualsToken(precedingTrivia, lengthWithMaybeEquals) Else - Return MakeCaretToken(precedingTrivia, False) + Return MakeCaretToken(precedingTrivia, fullWidth) End If - Case "!"c - Return MakeExclamationToken(precedingTrivia, False) - + Return MakeExclamationToken(precedingTrivia, fullWidth) Case "."c If CanGet(1) AndAlso IsDecimalDigit(Peek(1)) Then Return ScanNumericLiteral(precedingTrivia) Else - Return MakeDotToken(precedingTrivia, False) + Return MakeDotToken(precedingTrivia, fullWidth) End If - - Case "0"c, - "1"c, - "2"c, - "3"c, - "4"c, - "5"c, - "6"c, - "7"c, - "8"c, - "9"c + Case "0"c, "1"c, "2"c, "3"c, "4"c, + "5"c, "6"c, "7"c, "8"c, "9"c Return ScanNumericLiteral(precedingTrivia) - Case """"c Return ScanStringLiteral(precedingTrivia) - Case "A"c If NextAre(1, "s ") Then - ' TODO: do we allow widechars in keywords? Dim spelling = "As" - AdvanceChar(2) + If fullWidth Then spelling = GetText(2) Else AdvanceChar(2) Return MakeKeyword(SyntaxKind.AsKeyword, spelling, precedingTrivia) Else Return ScanIdentifierOrKeyword(precedingTrivia) End If - Case "E"c If NextAre(1, "nd ") Then - ' TODO: do we allow widechars in keywords? Dim spelling = "End" - AdvanceChar(3) + If fullWidth Then spelling = GetText(3) Else AdvanceChar(3) Return MakeKeyword(SyntaxKind.EndKeyword, spelling, precedingTrivia) Else Return ScanIdentifierOrKeyword(precedingTrivia) End If - Case "I"c If NextAre(1, "f ") Then - ' TODO: do we allow widechars in keywords? Dim spelling = "If" - AdvanceChar(2) + If fullWidth Then spelling = GetText(2) Else AdvanceChar(2) Return MakeKeyword(SyntaxKind.IfKeyword, spelling, precedingTrivia) Else Return ScanIdentifierOrKeyword(precedingTrivia) End If - Case "a"c, "b"c, "c"c, "d"c, "e"c, "f"c, "g"c, "h"c, "i"c, "j"c, "k"c, "l"c, "m"c, "n"c, "o"c, "p"c, "q"c, "r"c, "s"c, "t"c, "u"c, "v"c, "w"c, "x"c, "y"c, "z"c Return ScanIdentifierOrKeyword(precedingTrivia) - Case "B"c, "C"c, "D"c, "F"c, "G"c, "H"c, "J"c, "K"c, "L"c, "M"c, "N"c, "O"c, "P"c, "Q"c, - "R"c, "S"c, "T"c, "U"c, "V"c, "W"c, "X"c, "Y"c, "Z"c + "R"c, "S"c, "T"c, "U"c, "V"c, "W"c, "X"c, "Y"c, "Z"c Return ScanIdentifierOrKeyword(precedingTrivia) - Case "_"c - If CanGet(1) AndAlso IsIdentifierPartCharacter(Peek(1)) Then + If TryPeek(1, c) AndAlso IsIdentifierPartCharacter(c) Then Return ScanIdentifierOrKeyword(precedingTrivia) End If - Dim err As ERRID = ERRID.ERR_ExpectedIdentifier Dim len = GetWhitespaceLength(1) If Not CanGet(len) OrElse IsNewLine(Peek(len)) OrElse PeekStartComment(len) > 0 Then err = ERRID.ERR_LineContWithCommentOrNoPrecSpace End If - ' not a line continuation and cannot start identifier. Return MakeBadToken(precedingTrivia, 1, err) - Case "["c Return ScanBracketedIdentifier(precedingTrivia) - Case "?"c - Return MakeQuestionToken(precedingTrivia, False) - + Return MakeQuestionToken(precedingTrivia, fullWidth) Case "%"c - If CanGet(1) AndAlso - Peek(1) = ">"c Then + If NextIs(1, ">"c) Then Return XmlMakeEndEmbeddedToken(precedingTrivia, _scanSingleLineTriviaFunc) End If - Case "$"c, FULLWIDTH_DOLLAR_SIGN - If CanGet(1) AndAlso IsDoubleQuote(Peek(1)) Then + If fullWidth Then Exit Select + If TryPeek(1, c) AndAlso IsDoubleQuote(c) Then Return MakePunctuationToken(precedingTrivia, 2, SyntaxKind.DollarSignDoubleQuoteToken) End If - End Select - If IsIdentifierStartCharacter(ch) Then Return ScanIdentifierOrKeyword(precedingTrivia) End If - Debug.Assert(Not IsNewLine(ch)) - - If IsDoubleQuote(ch) Then - Return ScanStringLiteral(precedingTrivia) - End If - - If IsFullWidth(ch) Then - ch = MakeHalfWidth(ch) - Return ScanTokenFullWidth(precedingTrivia, ch) + If fullWidth Then + Debug.Assert(Not IsDoubleQuote(ch)) + Else + If IsDoubleQuote(ch) Then + Return ScanStringLiteral(precedingTrivia) + End If + If IsFullWidth(ch) Then + ch = MakeHalfWidth(ch) + Return ScanTokenFullWidth(precedingTrivia, ch) + End If End If - Return Nothing End Function - ' REVIEW: Is there a better way to reuse this logic? - Private Function ScanTokenFullWidth(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), ch As Char) As SyntaxToken - Select Case ch - Case CARRIAGE_RETURN, LINE_FEED - Return ScanNewlineAsStatementTerminator(ch, precedingTrivia) - - Case " "c, CHARACTER_TABULATION, "'"c - Debug.Assert(False, String.Format("Unexpected char: &H{0:x}", AscW(ch))) - Return Nothing ' trivia cannot start a token - - Case "@"c - Return MakeAtToken(precedingTrivia, True) - - Case "("c - Return MakeOpenParenToken(precedingTrivia, True) - - Case ")"c - Return MakeCloseParenToken(precedingTrivia, True) - - Case "{"c - Return MakeOpenBraceToken(precedingTrivia, True) - - Case "}"c - Return MakeCloseBraceToken(precedingTrivia, True) - - Case ","c - Return MakeCommaToken(precedingTrivia, True) - - Case "#"c - Dim dl = ScanDateLiteral(precedingTrivia) - If dl IsNot Nothing Then - Return dl - Else - Return MakeHashToken(precedingTrivia, True) - End If - - Case "&"c - If CanGet(1) AndAlso BeginsBaseLiteral(Peek(1)) Then - Return ScanNumericLiteral(precedingTrivia) - End If - - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeAmpersandEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeAmpersandToken(precedingTrivia, True) - End If - - Case "="c - Return MakeEqualsToken(precedingTrivia, True) - - Case "<"c - Return ScanLeftAngleBracket(precedingTrivia, True, _scanSingleLineTriviaFunc) - - Case ">"c - Return ScanRightAngleBracket(precedingTrivia, True) - - Case ":"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeColonEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return ScanColonAsStatementTerminator(precedingTrivia, True) - End If - - Case "+"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakePlusEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakePlusToken(precedingTrivia, True) - End If - - Case "-"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeMinusEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeMinusToken(precedingTrivia, True) - End If - - Case "*"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeAsteriskEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeAsteriskToken(precedingTrivia, True) - End If - - Case "/"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeSlashEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeSlashToken(precedingTrivia, True) - End If - - Case "\"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeBackSlashEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeBackslashToken(precedingTrivia, True) - End If - - Case "^"c - Dim lengthWithMaybeEquals = 1 - If TrySkipFollowingEquals(lengthWithMaybeEquals) Then - Return MakeCaretEqualsToken(precedingTrivia, lengthWithMaybeEquals) - Else - Return MakeCaretToken(precedingTrivia, True) - End If - - Case "!"c - Return MakeExclamationToken(precedingTrivia, True) - - Case "."c - If CanGet(1) AndAlso IsDecimalDigit(Peek(1)) Then - Return ScanNumericLiteral(precedingTrivia) - Else - Return MakeDotToken(precedingTrivia, True) - End If - - Case "0"c, - "1"c, - "2"c, - "3"c, - "4"c, - "5"c, - "6"c, - "7"c, - "8"c, - "9"c - Return ScanNumericLiteral(precedingTrivia) - - Case """"c - Return ScanStringLiteral(precedingTrivia) - - Case "A"c - If NextAre(1, "s ") Then - Dim spelling = GetText(2) - Return MakeKeyword(SyntaxKind.AsKeyword, spelling, precedingTrivia) - Else - Return ScanIdentifierOrKeyword(precedingTrivia) - End If - - Case "E"c - If NextAre(1, "nd ") Then - Dim spelling = GetText(3) - Return MakeKeyword(SyntaxKind.EndKeyword, spelling, precedingTrivia) - Else - Return ScanIdentifierOrKeyword(precedingTrivia) - End If - - Case "I"c - If NextAre(1, "f ") Then - ' TODO: do we allow widechars in keywords? - Dim spelling = GetText(2) - Return MakeKeyword(SyntaxKind.IfKeyword, spelling, precedingTrivia) - Else - Return ScanIdentifierOrKeyword(precedingTrivia) - End If - - Case "a"c, "b"c, "c"c, "d"c, "e"c, "f"c, "g"c, "h"c, "i"c, "j"c, "k"c, "l"c, "m"c, - "n"c, "o"c, "p"c, "q"c, "r"c, "s"c, "t"c, "u"c, "v"c, "w"c, "x"c, "y"c, "z"c - Return ScanIdentifierOrKeyword(precedingTrivia) - - Case "B"c, "C"c, "D"c, "F"c, "G"c, "H"c, "J"c, "K"c, "L"c, "M"c, "N"c, "O"c, "P"c, "Q"c, - "R"c, "S"c, "T"c, "U"c, "V"c, "W"c, "X"c, "Y"c, "Z"c - Return ScanIdentifierOrKeyword(precedingTrivia) - - Case "_"c - If CanGet(1) AndAlso IsIdentifierPartCharacter(Peek(1)) Then - Return ScanIdentifierOrKeyword(precedingTrivia) - End If - - Dim err As ERRID = ERRID.ERR_ExpectedIdentifier - Dim len = GetWhitespaceLength(1) - If Not CanGet(len) OrElse IsNewLine(Peek(len)) OrElse PeekStartComment(len) > 0 Then - err = ERRID.ERR_LineContWithCommentOrNoPrecSpace - End If - - ' not a line continuation and cannot start identifier. - Return MakeBadToken(precedingTrivia, 1, err) - - Case "["c - Return ScanBracketedIdentifier(precedingTrivia) - - Case "?"c - Return MakeQuestionToken(precedingTrivia, True) - - Case "%"c - If CanGet(1) AndAlso - Peek(1) = ">"c Then - Return XmlMakeEndEmbeddedToken(precedingTrivia, _scanSingleLineTriviaFunc) - End If - - End Select - - If IsIdentifierStartCharacter(ch) Then - Return ScanIdentifierOrKeyword(precedingTrivia) + ' at this point it is very likely that we are located at + ' the beginning of a token + Private Function TryScanToken(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken + Dim ch As Char + If Not TryPeek(ch) Then + Return MakeEofToken(precedingTrivia) End If + Return ScanTokenCommon(precedingTrivia, ch, False) + End Function - Debug.Assert(Not IsNewLine(ch)) - Debug.Assert(Not IsDoubleQuote(ch)) - - Return Nothing + Private Function ScanTokenFullWidth(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), ch As Char) As SyntaxToken + Return ScanTokenCommon(precedingTrivia, ch, True) End Function ' // Allow whitespace between the characters of a two-character token. @@ -1433,11 +1155,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim Here = Index Dim eq As Char - While CanGet(Here) - eq = Peek(Here) + While TryPeek(Here, eq) Here += 1 If Not IsWhitespace(eq) Then - If eq = "="c OrElse eq = FULLWIDTH_EQUALS_SIGN Then + If eq.IsAnyOf("="c, FULLWIDTH_EQUALS_SIGN) Then Index = Here Return True Else @@ -1449,21 +1170,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function ScanRightAngleBracket(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), charIsFullWidth As Boolean) As SyntaxToken - Debug.Assert(CanGet) ' > - Debug.Assert(Peek() = ">"c OrElse Peek() = FULLWIDTH_GREATER_THAN_SIGN) + Dim c As Char + Dim ok = TryPeek(c) + Debug.Assert(ok) ' > + Debug.Assert(c.IsAnyOf(">"c, FULLWIDTH_GREATER_THAN_SIGN)) Dim length As Integer = 1 ' // Allow whitespace between the characters of a two-character token. length = GetWhitespaceLength(length) - If CanGet(length) Then - Dim c As Char = Peek(length) - - If c = "="c OrElse c = FULLWIDTH_EQUALS_SIGN Then + If TryPeek(length, c) Then + If c.IsAnyOf("="c, FULLWIDTH_EQUALS_SIGN) Then length += 1 Return MakeGreaterThanEqualsToken(precedingTrivia, length) - ElseIf c = ">"c OrElse c = FULLWIDTH_GREATER_THAN_SIGN Then + ElseIf c.IsAnyOf(">"c, FULLWIDTH_GREATER_THAN_SIGN) Then length += 1 If TrySkipFollowingEquals(length) Then Return MakeGreaterThanGreaterThanEqualsToken(precedingTrivia, length) @@ -1476,26 +1197,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function ScanLeftAngleBracket(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), charIsFullWidth As Boolean, scanTrailingTrivia As ScanTriviaFunc) As SyntaxToken - Debug.Assert(CanGet) ' < - Debug.Assert(Peek() = "<"c OrElse Peek() = FULLWIDTH_LESS_THAN_SIGN) + Dim c As Char + Dim ok = TryPeek(c) + Debug.Assert(ok) ' < + Debug.Assert(c.IsAnyOf("<"c, FULLWIDTH_LESS_THAN_SIGN)) Dim length As Integer = 1 ' Check for XML tokens - If Not charIsFullWidth AndAlso CanGet(length) Then - Dim c As Char = Peek(length) + If Not charIsFullWidth AndAlso TryPeek(length, c) Then + Select Case c Case "!"c - If CanGet(length + 2) Then + Dim c2 As Char + If TryPeek(length + 2,c2) Then Select Case (Peek(length + 1)) Case "-"c - If CanGet(length + 3) AndAlso Peek(length + 2) = "-"c Then + If CanGet(length + 3) AndAlso c2 = "-"c Then Return XmlMakeBeginCommentToken(precedingTrivia, scanTrailingTrivia) End If - Case "["c + Case "["c If NextAre(length + 2, "CDATA[") Then - Return XmlMakeBeginCDataToken(precedingTrivia, scanTrailingTrivia) End If End Select @@ -1511,23 +1234,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' // Allow whitespace between the characters of a two-character token. length = GetWhitespaceLength(length) - If CanGet(length) Then - Dim c As Char = Peek(length) - If c = "="c OrElse c = FULLWIDTH_EQUALS_SIGN Then + If TryPeek(length, c) Then + If c.IsAnyOf("="c, FULLWIDTH_EQUALS_SIGN) Then length += 1 Return MakeLessThanEqualsToken(precedingTrivia, length) - ElseIf c = ">"c OrElse c = FULLWIDTH_GREATER_THAN_SIGN Then + ElseIf c.IsAnyOf(">"c, FULLWIDTH_GREATER_THAN_SIGN) Then length += 1 Return MakeLessThanGreaterThanToken(precedingTrivia, length) - ElseIf c = "<"c OrElse c = FULLWIDTH_LESS_THAN_SIGN Then + ElseIf c.IsAnyOf("<"c, FULLWIDTH_LESS_THAN_SIGN) Then length += 1 - - If CanGet(length) Then - c = Peek(length) - + If TryPeek(length, c) Then 'if the second "<" is a part of "<%" - like in "<<%" , we do not want to use it. - If c <> "%"c AndAlso c <> FULLWIDTH_PERCENT_SIGN Then + If c.IsNoneOf("%"c, FULLWIDTH_PERCENT_SIGN) Then If TrySkipFollowingEquals(length) Then Return MakeLessThanLessThanEqualsToken(precedingTrivia, length) Else @@ -1571,13 +1290,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function ScanIdentifierOrKeyword(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken - Debug.Assert(CanGet) - Debug.Assert(IsIdentifierStartCharacter(Peek)) + Dim ch As Char + Dim ok = TryPeek(ch) + Debug.Assert(ok) + Debug.Assert(IsIdentifierStartCharacter(ch)) Debug.Assert(PeekStartComment(0) = 0) ' comment should be handled by caller - - Dim ch = Peek() - If CanGet(1) Then - Dim ch1 = Peek(1) + Dim ch1 As Char + If TryPeek(1, ch1) Then If IsConnectorPunctuation(ch) AndAlso Not IsIdentifierPartCharacter(ch1) Then Return MakeBadToken(precedingTrivia, 1, ERRID.ERR_ExpectedIdentifier) End If @@ -1588,8 +1307,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' // The C++ compiler refuses to inline IsIdentifierCharacter, so the ' // < 128 test is inline here. (This loop gets a *lot* of traffic.) ' TODO: make sure we get good perf here - While CanGet(len) - ch = Peek(len) + While TryPeek(len, ch) Dim code = Convert.ToUInt16(ch) If code < 128 AndAlso IsNarrowIdentifierCharacter(code) OrElse @@ -1603,16 +1321,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax 'Check for a type character Dim TypeCharacter As TypeCharacter = TypeCharacter.None - If CanGet(len) Then - ch = Peek(len) + If TryPeek(len, ch) Then FullWidthRepeat: Select Case ch Case "!"c + Dim NextChar As Char ' // If the ! is followed by an identifier it is a dictionary lookup operator, not a type character. - If CanGet(len + 1) Then - Dim NextChar As Char = Peek(len + 1) - + If TryPeek(len + 1, NextChar) Then If IsIdentifierStartCharacter(NextChar) OrElse MatchOneOrAnotherOrFullwidth(NextChar, "["c, "]"c) Then Exit Select @@ -1653,9 +1369,7 @@ FullWidthRepeat: Dim contextualKind As SyntaxKind = SyntaxKind.IdentifierToken Dim spelling = GetText(len) - Dim BaseSpelling = If(TypeCharacter = TypeCharacter.None, - spelling, - Intern(spelling, 0, len - 1)) + Dim BaseSpelling = If(TypeCharacter = TypeCharacter.None, spelling, Intern(spelling, 0, len - 1)) ' this can be keyword only if it has no type character, or if it is Mid$ If TypeCharacter = TypeCharacter.None Then @@ -1689,34 +1403,35 @@ FullWidthRepeat: End Function Private Function ScanBracketedIdentifier(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken - Debug.Assert(CanGet) ' [ - Debug.Assert(Peek() = "["c OrElse Peek() = FULLWIDTH_LEFT_SQUARE_BRACKET) + Dim ch As Char + Dim ok = TryPeek(ch) + Debug.Assert(ok) ' [ + Debug.Assert(ch.IsAnyOf("["c, FULLWIDTH_LEFT_SQUARE_BRACKET)) Dim IdStart As Integer = 1 Dim Here As Integer = IdStart Dim InvalidIdentifier As Boolean = False - If Not CanGet(Here) Then + If Not TryPeek(Here, ch) Then Return MakeBadToken(precedingTrivia, Here, ERRID.ERR_MissingEndBrack) End If - Dim ch = Peek(Here) + Dim c1 As Char ' check if we can start an ident. If Not IsIdentifierStartCharacter(ch) OrElse (IsConnectorPunctuation(ch) AndAlso - Not (CanGet(Here + 1) AndAlso - IsIdentifierPartCharacter(Peek(Here + 1)))) Then + Not (TryPeek(Here + 1, c1) AndAlso IsIdentifierPartCharacter(c1))) Then InvalidIdentifier = True End If ' check ident until ] - While CanGet(Here) - Dim [Next] As Char = Peek(Here) + Dim [Next] As Char + While TryPeek(Here, [Next]) - If [Next] = "]"c OrElse [Next] = FULLWIDTH_RIGHT_SQUARE_BRACKET Then + If [Next].IsAnyOf("]"c, FULLWIDTH_RIGHT_SQUARE_BRACKET) Then Dim IdStringLength As Integer = Here - IdStart If IdStringLength > 0 AndAlso Not InvalidIdentifier Then @@ -1762,7 +1477,9 @@ FullWidthRepeat: End Enum Private Function ScanNumericLiteral(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As SyntaxToken - Debug.Assert(CanGet) + Dim ch As Char + Dim ok = TryPeek(ch) + Debug.Assert(ok) Dim Here As Integer = 0 Dim IntegerLiteralStart As Integer @@ -1776,10 +1493,9 @@ FullWidthRepeat: ' // First read a leading base specifier, if present, followed by a sequence of zero ' // or more digits. - Dim ch = Peek() - If ch = "&"c OrElse ch = FULLWIDTH_AMPERSAND Then + If ch.IsAnyOf("&"c, FULLWIDTH_AMPERSAND) Then Here += 1 - ch = If(CanGet(Here), Peek(Here), ChrW(0)) + If Not TryPeek(Here,ch) Then ch = ChrW(0) FullWidthRepeat: Select Case ch @@ -1788,11 +1504,7 @@ FullWidthRepeat: IntegerLiteralStart = Here Base = LiteralBase.Hexadecimal - While CanGet(Here) - ch = Peek(Here) - If Not IsHexDigit(ch) Then - Exit While - End If + While TryPeek(Here, ch) AndAlso IsHexDigit(ch) Here += 1 End While @@ -1801,11 +1513,7 @@ FullWidthRepeat: IntegerLiteralStart = Here Base = LiteralBase.Octal - While CanGet(Here) - ch = Peek(Here) - If Not IsOctalDigit(ch) Then - Exit While - End If + While TryPeek(Here, ch) AndAlso IsOctalDigit(ch) Here += 1 End While @@ -1820,11 +1528,7 @@ FullWidthRepeat: Else ' no base specifier - just go through decimal digits. IntegerLiteralStart = Here - While CanGet(Here) - ch = Peek(Here) - If Not IsDecimalDigit(ch) Then - Exit While - End If + While TryPeek(Here, ch) AndAlso IsDecimalDigit(ch) Here += 1 End While End If @@ -1834,21 +1538,16 @@ FullWidthRepeat: ' // Unless there was an explicit base specifier (which indicates an integer literal), ' // read the rest of a float literal. - If Base = LiteralBase.Decimal AndAlso CanGet(Here) Then + If Base = LiteralBase.Decimal AndAlso TryPeek(Here, ch) Then ' // First read a '.' followed by a sequence of one or more digits. - ch = Peek(Here) - If (ch = "."c Or ch = FULLWIDTH_FULL_STOP) AndAlso - CanGet(Here + 1) AndAlso - IsDecimalDigit(Peek(Here + 1)) Then + Dim cx As Char + If ch.IsAnyOf("."c, FULLWIDTH_FULL_STOP) AndAlso + (TryPeek(Here + 1, cx) AndAlso IsDecimalDigit(cx)) Then Here += 2 ' skip dot and first digit ' all following decimal digits belong to the literal (fractional part) - While CanGet(Here) - ch = Peek(Here) - If Not IsDecimalDigit(ch) Then - Exit While - End If + While TryPeek(Here, ch) AndAlso IsDecimalDigit(ch) Here += 1 End While literalKind = NumericLiteralKind.Float @@ -1856,24 +1555,16 @@ FullWidthRepeat: ' // Read an exponent symbol followed by an optional sign and a sequence of ' // one or more digits. - If CanGet(Here) AndAlso BeginsExponent(Peek(Here)) Then + If TryPeek(Here, cx) AndAlso BeginsExponent(cx) Then Here += 1 - If CanGet(Here) Then - ch = Peek(Here) - - If MatchOneOrAnotherOrFullwidth(ch, "+"c, "-"c) Then - Here += 1 - End If + If TryPeek(Here, ch) AndAlso MatchOneOrAnotherOrFullwidth(ch, "+"c, "-"c) Then + Here += 1 End If - If CanGet(Here) AndAlso IsDecimalDigit(Peek(Here)) Then + If TryPeek(Here, cx) AndAlso IsDecimalDigit(cx) Then Here += 1 - While CanGet(Here) - ch = Peek(Here) - If Not IsDecimalDigit(ch) Then - Exit While - End If + While TryPeek(Here, ch) AndAlso IsDecimalDigit(ch) Here += 1 End While Else @@ -1892,8 +1583,7 @@ FullWidthRepeat: Dim TypeCharacter As TypeCharacter = TypeCharacter.None - If CanGet(Here) Then - ch = Peek(Here) + If TryPeek(Here, ch) Then FullWidthRepeat2: Select Case ch @@ -1969,9 +1659,7 @@ FullWidthRepeat2: literalKind = NumericLiteralKind.Decimal ' check if this was not attempt to use obsolete exponent - If CanGet(Here + 1) Then - ch = Peek(Here + 1) - + If TryPeek(Here + 1, ch) Then If IsDecimalDigit(ch) OrElse MatchOneOrAnotherOrFullwidth(ch, "+"c, "-"c) Then Return MakeBadToken(precedingTrivia, Here, ERRID.ERR_ObsoleteExponent) End If @@ -1981,9 +1669,8 @@ FullWidthRepeat2: End If Case "U"c, "u"c - If literalKind <> NumericLiteralKind.Float AndAlso CanGet(Here + 1) Then - Dim NextChar As Char = Peek(Here + 1) - + Dim NextChar As Char + If literalKind <> NumericLiteralKind.Float AndAlso TryPeek(Here + 1, NextChar) Then 'unsigned suffixes - US, UL, UI If MatchOneOrAnotherOrFullwidth(NextChar, "S"c, "s"c) Then TypeCharacter = TypeCharacter.UShortLiteral @@ -2154,31 +1841,17 @@ FullWidthRepeat2: Return Decimal.TryParse(text, NumberStyles.AllowDecimalPoint Or NumberStyles.AllowExponent, CultureInfo.InvariantCulture, value) End Function - Private Function ScanIntLiteral( - ByRef ReturnValue As Integer, - ByRef Here As Integer - ) As Boolean + Private Function ScanIntLiteral( ByRef ReturnValue As Integer, ByRef Here As Integer) As Boolean Debug.Assert(Here >= 0) - - If Not CanGet(Here) Then - Return False - End If - - Dim ch = Peek(Here) - If Not IsDecimalDigit(ch) Then + Dim ch As Char + If Not TryPeek(Here, ch) OrElse Not IsDecimalDigit(ch) Then Return False End If Dim IntegralValue As Integer = IntegralLiteralCharacterValue(ch) Here += 1 - While CanGet(Here) - ch = Peek(Here) - - If Not IsDecimalDigit(ch) Then - Exit While - End If - + While TryPeek(Here, ch) AndAlso IsDecimalDigit(ch) Dim nextDigit = IntegralLiteralCharacterValue(ch) If IntegralValue < 214748364 OrElse (IntegralValue = 214748364 AndAlso nextDigit < 8) Then @@ -2227,8 +1900,9 @@ FullWidthRepeat2: End If ' // If we see a /, then it's a date + Dim c As Char - If CanGet(Here) AndAlso IsDateSeparatorCharacter(Peek(Here)) Then + If TryPeek(Here, c) AndAlso IsDateSeparatorCharacter(c) Then Dim FirstDateSeparator As Integer = Here ' // We've got a date @@ -2248,10 +1922,11 @@ FullWidthRepeat2: End If ' Do we have a day value? - If CanGet(Here) AndAlso IsDateSeparatorCharacter(Peek(Here)) Then + Dim c1 As Char + If TryPeek(Here, c1) AndAlso IsDateSeparatorCharacter(c1) Then ' // Check to see they used a consistent separator - If Peek(Here) <> Peek(FirstDateSeparator) Then + If c1 <> Peek(FirstDateSeparator) Then GoTo baddate End If @@ -2273,11 +1948,11 @@ FullWidthRepeat2: End If ' // Do we have a year value? - - If CanGet(Here) AndAlso IsDateSeparatorCharacter(Peek(Here)) Then + Dim c1 As Char + If TryPeek(Here, c1) AndAlso IsDateSeparatorCharacter(c1) Then ' // Check to see they used a consistent separator - If Peek(Here) <> Peek(FirstDateSeparator) Then + If c1 <> Peek(FirstDateSeparator) Then GoTo baddate End If @@ -2316,8 +1991,8 @@ FullWidthRepeat2: If HaveTimeValue Then ' // Do we see a :? - - If CanGet(Here) AndAlso IsColon(Peek(Here)) Then + Dim c1 As Char + If TryPeek(Here, c1) AndAlso IsColon(c1) Then Here += 1 ' // Now let's get the minute value @@ -2329,8 +2004,8 @@ FullWidthRepeat2: HaveMinuteValue = True ' // Do we have a second value? - - If CanGet(Here) AndAlso IsColon(Peek(Here)) Then + Dim c2 As Char + If TryPeek(Here, c2) AndAlso IsColon(c2) Then ' // Yes. HaveSecondValue = True Here += 1 @@ -2344,25 +2019,23 @@ FullWidthRepeat2: Here = GetWhitespaceLength(Here) ' // Check AM/PM - - If CanGet(Here) Then - If Peek(Here) = "A"c OrElse Peek(Here) = FULLWIDTH_LATIN_CAPITAL_LETTER_A OrElse - Peek(Here) = "a"c OrElse Peek(Here) = FULLWIDTH_LATIN_SMALL_LETTER_A Then - + + If TryPeek(Here, c1) Then + If c1.IsAnyOf("A"c, FULLWIDTH_LATIN_CAPITAL_LETTER_A, + "a"c, FULLWIDTH_LATIN_SMALL_LETTER_A) Then HaveAM = True Here += 1 - ElseIf Peek(Here) = "P"c OrElse Peek(Here) = FULLWIDTH_LATIN_CAPITAL_LETTER_P OrElse - Peek(Here) = "p"c OrElse Peek(Here) = FULLWIDTH_LATIN_SMALL_LETTER_P Then - + ElseIf c1.IsAnyOf("P"c, FULLWIDTH_LATIN_CAPITAL_LETTER_P, + "p"c, FULLWIDTH_LATIN_SMALL_LETTER_P) Then HavePM = True Here += 1 End If - - If CanGet(Here) AndAlso (HaveAM OrElse HavePM) Then - If Peek(Here) = "M"c OrElse Peek(Here) = FULLWIDTH_LATIN_CAPITAL_LETTER_M OrElse - Peek(Here) = "m"c OrElse Peek(Here) = FULLWIDTH_LATIN_SMALL_LETTER_M Then + Dim c2 As Char + If TryPeek(Here, c2) AndAlso (HaveAM OrElse HavePM) Then + If c2.IsAnyOf("M"c, FULLWIDTH_LATIN_CAPITAL_LETTER_M, + "m"c, FULLWIDTH_LATIN_SMALL_LETTER_M) Then Here = GetWhitespaceLength(Here + 1) @@ -2379,7 +2052,7 @@ FullWidthRepeat2: End If End If - If Not CanGet(Here) OrElse Not IsHash(Peek(Here)) Then + If Not TryPeek(Here, c) OrElse Not IsHash(c) Then GoTo baddate End If @@ -2484,16 +2157,12 @@ FullWidthRepeat2: baddate: ' // If we can find a closing #, then assume it's a malformed date, ' // otherwise, it's not a date - - While CanGet(Here) - Dim ch As Char = Peek(Here) - If IsHash(ch) OrElse IsNewLine(ch) Then - Exit While - End If + Dim ch As Char + While TryPeek(Here, ch) AndAlso Not (IsHash(ch) OrElse IsNewLine(ch)) Here += 1 End While - If Not CanGet(Here) OrElse IsNewLine(Peek(Here)) Then + If Not TryPeek(Here, ch) OrElse IsNewLine(ch) Then ' // No closing # Return Nothing Else @@ -2508,44 +2177,35 @@ baddate: Debug.Assert(IsDoubleQuote(Peek)) Dim length As Integer = 1 - Dim ch As Char + Dim ch,cx As Char Dim followingTrivia As SyntaxList(Of VisualBasicSyntaxNode) ' // Check for a Char literal, which can be of the form: ' // """"c or ""c - If CanGet(3) AndAlso IsDoubleQuote(Peek(2)) Then + If TryPeek(3,ch) AndAlso IsDoubleQuote(Peek(2)) Then If IsDoubleQuote(Peek(1)) Then - If IsDoubleQuote(Peek(3)) AndAlso - CanGet(4) AndAlso - IsLetterC(Peek(4)) Then - + If IsDoubleQuote(ch) AndAlso TryPeek(4, cx) AndAlso IsLetterC(cx) Then ' // Double-quote Char literal: """"c Return MakeCharacterLiteralToken(precedingTrivia, """"c, 5) End If - ElseIf IsLetterC(Peek(3)) Then + ElseIf IsLetterC(ch) Then ' // Char literal. "x"c Return MakeCharacterLiteralToken(precedingTrivia, Peek(1), 4) End If End If - If CanGet(2) AndAlso - IsDoubleQuote(Peek(1)) AndAlso - IsLetterC(Peek(2)) Then - + If TryPeek(2, ch) AndAlso IsDoubleQuote(Peek(1)) AndAlso IsLetterC(ch) Then ' // Error. ""c is not a legal char constant Return MakeBadToken(precedingTrivia, 3, ERRID.ERR_IllegalCharConstant) End If Dim scratch = GetScratch() - While CanGet(length) - ch = Peek(length) + While TryPeek(length, ch) If IsDoubleQuote(ch) Then - If CanGet(length + 1) Then - ch = Peek(length + 1) - + If TryPeek(length + 1, ch) Then If IsDoubleQuote(ch) Then ' // An escaped double quote scratch.Append(""""c) @@ -2555,7 +2215,6 @@ baddate: ' // The end of the char literal. If IsLetterC(ch) Then ' // Error. "aad"c is not a legal char constant - ' // +2 to include both " and c in the token span scratch.Clear() Return MakeBadToken(precedingTrivia, length + 2, ERRID.ERR_IllegalCharConstant) diff --git a/src/Compilers/VisualBasic/Portable/Scanner/ScannerBuffer.vb b/src/Compilers/VisualBasic/Portable/Scanner/ScannerBuffer.vb index 30e698a182c37..266fd2a9e42d0 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/ScannerBuffer.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/ScannerBuffer.vb @@ -95,6 +95,36 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Return p End Function + Friend Function TryPeek(at As Integer, ByRef ch As Char) As Boolean + ' CanGet(at) + Debug.Assert(_lineBufferOffset + at >= 0) + Debug.Assert(at >= -MaxCharsLookBehind) + If _lineBufferOffset + at >= _bufferLen Then Return False + ' Peek(at) + at += _lineBufferOffset + Dim page = _curPage + ch = page._arr(at And s_PAGE_MASK) + If page._pageStart <> (at And s_NOT_PAGE_MASK) Then + page = GetPage(at) + ch = page._arr(at And s_PAGE_MASK) + End If + Return True + End Function + + Friend Function TryPeek(ByRef ch As Char) As Boolean + ' CanGet() + If _lineBufferOffset >= _bufferLen Then Return False + ' Peek() + Dim page = _curPage + Dim position = _lineBufferOffset + ch = page._arr(position And s_PAGE_MASK) + If page._pageStart <> (position And s_NOT_PAGE_MASK) Then + page = GetPage(position) + ch = page._arr(position And s_PAGE_MASK) + End If + Return True + End Function + ' PERF CRITICAL Private Function Peek(skip As Integer) As Char Debug.Assert(CanGet(skip)) diff --git a/src/Compilers/VisualBasic/Portable/Scanner/ScannerInterpolatedString.vb b/src/Compilers/VisualBasic/Portable/Scanner/ScannerInterpolatedString.vb index 40a522c7dbfdd..24be6bd253ef0 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/ScannerInterpolatedString.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/ScannerInterpolatedString.vb @@ -19,26 +19,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim leadingTriviaLength = GetWhitespaceLength(0) Dim offset = leadingTriviaLength Dim length As Integer - - If Not CanGet(offset) Then + Dim c As Char + If Not TryPeek(offset, c) Then Return MakeEndOfInterpolatedStringToken() End If - Dim c = Peek(offset) - ' This should only ever happen for $" or } - Debug.Assert(leadingTriviaLength = 0 OrElse c = "$"c OrElse c = FULLWIDTH_DOLLAR_SIGN OrElse IsRightCurlyBracket(c)) + Debug.Assert(leadingTriviaLength = 0 OrElse c.IsAnyOf("$"c, FULLWIDTH_DOLLAR_SIGN) OrElse IsRightCurlyBracket(c)) ' Another } may follow the close brace of an interpolation if the interpolation lacked a format clause. ' This is because the normal escaping rules only apply when parsing the format string. Debug.Assert(Not CanGet(1) OrElse Peek(offset + 1) <> c OrElse IsRightCurlyBracket(c), "Escape sequence not detected.") Dim scanTrailingTrivia As Boolean + Dim c1 As Char Select Case c Case "$"c, FULLWIDTH_DOLLAR_SIGN - If CanGet(offset + 1) AndAlso IsDoubleQuote(Peek(offset + 1)) Then + If TryPeek(offset + 1, c1) AndAlso IsDoubleQuote(c1) Then kind = SyntaxKind.DollarSignDoubleQuoteToken length = 2 scanTrailingTrivia = False ' Trailing whitespace should be scanned as interpolated string text. @@ -112,22 +111,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function IsInterpolatedStringPunctuation(Optional offset As Integer = 0) As Boolean - If Not CanGet(offset) Then Return False - - Dim c = Peek(offset) - + Dim c, c1 As Char + If Not TryPeek(offset, c) Then Return False + Dim ok = TryPeek(offset + 1, c1) If IsLeftCurlyBracket(c) Then - Return Not CanGet(offset + 1) OrElse Not IsLeftCurlyBracket(Peek(offset + 1)) + Return Not ok OrElse Not IsLeftCurlyBracket(c1) ElseIf IsRightCurlyBracket(c) Then - Return Not CanGet(offset + 1) OrElse Not IsRightCurlyBracket(Peek(offset + 1)) + Return Not ok OrElse Not IsRightCurlyBracket(c1) ElseIf IsDoubleQuote(c) 'A subtle difference between this case and the one above. ' In both interpolated and literal strings the two quote characters used in an escape sequence don't have to match. ' It's enough that the next character is *a* quote char. It doesn't have to be the same quote. ' If we want to preserve consistency the quotes need to be special cased. - Return Not CanGet(offset + 1) OrElse Not IsDoubleQuote(Peek(offset + 1)) + Return Not ok OrElse Not IsDoubleQuote(c1) Else Return False @@ -140,17 +138,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim offset = 0 Dim pendingWhitespace = 0 Dim valueBuilder = GetScratch() - - Do While CanGet(offset) - - Dim c = Peek(offset) + Dim c, c1 As Char + Do While TryPeek(offset, c) ' Any combination of fullwidth and ASCII curly braces of the same direction is an escaping sequence for the corresponding ASCII curly brace. ' We insert that curly brace doubled and because this is the escaping sequence understood by String.Format, that will be replaced by a single brace. ' This is deliberate design and it aligns with existing rules for double quote escaping in strings. + Dim ok = TryPeek(offset+1, c1) If IsLeftCurlyBracket(c) Then - If CanGet(offset + 1) AndAlso IsLeftCurlyBracket(Peek(offset + 1)) Then + If ok AndAlso IsLeftCurlyBracket(c1) Then ' This is an escape sequence. valueBuilder.Append("{{") @@ -164,7 +161,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ElseIf IsRightCurlyBracket(c) Then - If CanGet(offset + 1) AndAlso IsRightCurlyBracket(Peek(offset + 1)) Then + If ok AndAlso IsRightCurlyBracket(c1) Then ' This is an escape sequence. valueBuilder.Append("}}") @@ -178,7 +175,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ElseIf IsDoubleQuote(c) - If CanGet(offset + 1) AndAlso IsDoubleQuote(Peek(offset + 1)) Then + If ok AndAlso IsDoubleQuote(c1) Then ' This is a VB double quote escape. Oddly enough this logic allows mixing and matching of ' smart and dumb double quotes in any order. Regardless we always emit as a standard double quote. ' This is consistent with their handling in string literals. diff --git a/src/Compilers/VisualBasic/Portable/Scanner/ScannerXml.vb b/src/Compilers/VisualBasic/Portable/Scanner/ScannerXml.vb index 5e6e421df0beb..6a3801b6c917c 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/ScannerXml.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/ScannerXml.vb @@ -16,15 +16,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Private Function ScanXmlTrivia(c As Char) As SyntaxList(Of VisualBasicSyntaxNode) Debug.Assert(Not IsScanningXmlDoc) - Debug.Assert(c = CARRIAGE_RETURN OrElse c = LINE_FEED OrElse c = " "c OrElse c = CHARACTER_TABULATION) + Debug.Assert(c.IsAnyOf( CARRIAGE_RETURN, LINE_FEED, " "c, CHARACTER_TABULATION)) Dim builder = _triviaListPool.Allocate Dim len = 0 Do - If c = " "c OrElse c = CHARACTER_TABULATION Then + If c.IsAnyOf(" "c, CHARACTER_TABULATION) Then len += 1 - ElseIf c = CARRIAGE_RETURN OrElse c = LINE_FEED Then + ElseIf c.IsAnyOf(CARRIAGE_RETURN, LINE_FEED) Then If len > 0 Then builder.Add(MakeWhiteSpaceTrivia(GetText(len))) len = 0 @@ -34,10 +34,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Exit Do End If - If Not CanGet(len) Then + If Not TryPeek(len, c) Then Exit Do End If - c = Peek(len) Loop If len > 0 Then @@ -69,8 +68,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim leadingTrivia As SyntaxList(Of VisualBasicSyntaxNode) = Nothing - While CanGet() - Dim c As Char = Peek() + Dim c As Char + While TryPeek(c) Select Case (c) ' // Whitespace @@ -93,7 +92,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax leadingTrivia = ScanXmlTrivia(c) Case "/"c - If CanGet(1) AndAlso Peek(1) = ">" Then + If NextIs(1,">"c) Then Return XmlMakeEndEmptyElementToken(leadingTrivia) End If Return XmlMakeDivToken(leadingTrivia) @@ -113,12 +112,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Return XmlMakeDoubleQuoteToken(leadingTrivia, c, isOpening:=True) Case "<"c - If CanGet(1) Then - Dim ch As Char = Peek(1) + Dim ch As Char + If TryPeek(1, ch) Then + Dim c2 As Char Select Case ch Case "!"c - If CanGet(2) Then - Select Case (Peek(2)) + If TryPeek(2,c2) Then + Select Case c2 Case "-"c If NextIs(3,"-"c) Then Return XmlMakeBeginCommentToken(leadingTrivia, s_scanNoTriviaFunc) @@ -207,14 +207,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax '// NL WS* ' '// Example ' This is a comment Private Function ScanXmlForPossibleStatement(state As ScannerState) As Boolean - If Not CanGet() Then + Dim c As Char + If Not TryPeek(c) Then Return False End If Dim token As SyntaxToken Dim possibleStatement As Boolean = False Dim offsets = CreateOffsetRestorePoint() - Dim c As Char = Peek() Select Case c Case "#"c, @@ -239,8 +239,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax If name.PossibleKeywordKind <> SyntaxKind.XmlNameToken Then leadingTrivia = ScanSingleLineTrivia() c = Peek() - possibleStatement = - c = "("c OrElse c = FULLWIDTH_LEFT_PARENTHESIS + possibleStatement = c.IsAnyOf("("c, FULLWIDTH_LEFT_PARENTHESIS) End If End If @@ -309,8 +308,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim scratch = GetScratch() - While CanGet(Here) - Dim c As Char = Peek(Here) + Dim c As Char + While TryPeek(Here, c) Select Case (c) Case CARRIAGE_RETURN, LINE_FEED @@ -342,12 +341,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End If Debug.Assert(Here = 0) - If CanGet(1) Then - Dim ch As Char = Peek(1) + Dim ch As Char + If TryPeek(1, ch) Then Select Case ch Case "!"c - If CanGet(2) Then - Select Case (Peek(2)) + Dim c2 As Char + If TryPeek(2, c2) Then + Select Case c2 Case "-"c If NextIs(3,"-"c) Then Return XmlMakeBeginCommentToken(precedingTrivia, s_scanNoTriviaFunc) @@ -492,9 +492,9 @@ ScanChars: End If Dim Here = 0 - While CanGet(Here) - Dim c As Char = Peek(Here) - Select Case (c) + Dim c As Char + While TryPeek(Here, c) + Select Case c Case CARRIAGE_RETURN, LINE_FEED Return XmlMakeCommentToken(precedingTrivia, Here + LengthOfLineBreak(c, Here)) @@ -511,9 +511,7 @@ ScanChars: Return XmlMakeCommentToken(precedingTrivia, Here) End If - If CanGet(Here + 2) Then - - c = Peek(Here + 2) + If TryPeek(Here + 2, c) Then Here += 2 ' // if > is not found then this is an error. Return the -- string @@ -583,11 +581,9 @@ ScanChars: Dim scratch = GetScratch() Dim Here = 0 - - While CanGet(Here) - Dim c As Char = Peek(Here) - Select Case (c) - + Dim c As Char + While TryPeek(Here, c) + Select Case c Case CARRIAGE_RETURN, LINE_FEED Here = SkipLineBreak(c, Here) scratch.Append(LINE_FEED) @@ -647,11 +643,10 @@ ScanChars: Dim precedingTrivia = _triviaListPool.Allocate(Of VisualBasicSyntaxNode)() Dim result As SyntaxToken - - If state = ScannerState.StartProcessingInstruction AndAlso CanGet() Then + Dim c As Char + If state = ScannerState.StartProcessingInstruction AndAlso TryPeek(c) Then ' // Whitespace ' // S ::= (#x20 | #x9 | #xD | #xA)+ - Dim c = Peek() Select Case c Case CARRIAGE_RETURN, LINE_FEED, " "c, CHARACTER_TABULATION Dim wsTrivia = ScanXmlTrivia(c) @@ -660,8 +655,7 @@ ScanChars: End If Dim Here = 0 - While CanGet(Here) - Dim c As Char = Peek(Here) + While TryPeek(Here, c) Select Case (c) Case CARRIAGE_RETURN, LINE_FEED @@ -720,9 +714,8 @@ CleanUp: ' // Misc ::= Comment | PI | S Dim precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode) = Nothing - While CanGet() - Dim c As Char = Peek() - + Dim c As Char + While TryPeek(c) Select Case (c) ' // Whitespace ' // S ::= (#x20 | #x9 | #xD | #xA)+ @@ -732,17 +725,17 @@ CleanUp: precedingTrivia = ScanXmlTrivia(c) Case "<"c - If CanGet(1) Then - Dim ch As Char = Peek(1) + Dim ch As Char + If TryPeek(1, ch) Then Select Case ch Case "!"c - If NextAre(2,"--") Then + If NextAre(2, "--") Then Return XmlMakeBeginCommentToken(precedingTrivia, s_scanNoTriviaFunc) - ElseIf NextAre(2,"DOCTYPE") Then + ElseIf NextAre(2, "DOCTYPE") Then Return XmlMakeBeginDTDToken(precedingTrivia) End If Case "%"c - If NextIs(2,"="c) Then + If NextIs(2, "="c) Then Return XmlMakeBeginEmbeddedToken(precedingTrivia) End If Case "?"c @@ -786,7 +779,8 @@ CleanUp: End Function Friend Function ScanXmlStringUnQuoted() As SyntaxToken - If Not CanGet() Then + Dim c As Char + If Not TryPeek(c) Then Return MakeEofToken() End If @@ -796,11 +790,8 @@ CleanUp: Dim Here = 0 Dim scratch = GetScratch() - While CanGet(Here) - Dim c As Char = Peek(Here) - + While TryPeek(Here,c) Select Case (c) - Case CARRIAGE_RETURN, LINE_FEED, " "c, CHARACTER_TABULATION If Here > 0 Then Return XmlMakeAttributeDataToken(Nothing, Here, scratch) @@ -888,9 +879,8 @@ ScanChars: Dim Here = 0 Dim scratch = GetScratch() - - While CanGet(Here) - Dim c As Char = Peek(Here) + Dim c As Char + While TryPeek(Here, c) If c = terminatingChar Or c = altTerminatingChar Then If Here > 0 Then result = XmlMakeAttributeDataToken(precedingTrivia, Here, scratch) @@ -991,10 +981,8 @@ CleanUp: Debug.Assert(Here >= 0) Debug.Assert(CanGet(Here)) Debug.Assert(Peek(Here) = c1) - - If IsHighSurrogate(c1) AndAlso CanGet(Here + 1) Then - Dim c2 = Peek(Here + 1) - + Dim c2 As Char + If IsHighSurrogate(c1) AndAlso TryPeek(Here + 1, c2) Then If IsLowSurrogate(c2) Then Return New XmlCharResult(c1, c2) End If @@ -1033,9 +1021,9 @@ CleanUp: Private Function ScanXmlChar(Here As Integer) As XmlCharResult Debug.Assert(Here >= 0) - Debug.Assert(CanGet(Here)) - - Dim c = Peek(Here) + Dim c As Char + Dim ok =TryPeek(Here, c) + Debug.Assert(ok) If Not isValidUtf16(c) Then Return Nothing @@ -1066,9 +1054,8 @@ CleanUp: 'TODO - Fix ScanXmlNCName to conform to XML spec instead of old loose scanning. - While CanGet(Here) - Dim c As Char = Peek(Here) - + Dim c As Char + While TryPeek(Here, c) Select Case (c) Case ":"c, " "c, CHARACTER_TABULATION, LINE_FEED, CARRIAGE_RETURN, @@ -1130,14 +1117,11 @@ CreateNCNameToken: End Function Private Function ScanXmlReference(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode)) As XmlTextTokenSyntax - Debug.Assert(CanGet) - Debug.Assert(Peek() = "&"c) - + Debug.Assert( NextIs(0,"&"c)) + Dim c,c1 As Char ' skip 1 char for "&" - If CanGet(1) Then - Dim c As Char = Peek(1) - - Select Case (c) + If TryPeek(1, c) Then + Select Case (c) Case "#"c Dim Here = 2 ' skip "&#" Dim result = ScanXmlCharRef(Here) @@ -1150,7 +1134,7 @@ CreateNCNameToken: value = Intern({result.Char1, result.Char2}) End If - If CanGet(Here) AndAlso Peek(Here) = ";"c Then + If NextIs(Here, ";"c) Then Return XmlMakeEntityLiteralToken(precedingTrivia, Here + 1, value) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, Here, value) @@ -1162,9 +1146,8 @@ CreateNCNameToken: Case "a"c ' // & ' // ' - - If CanGet(4) AndAlso NextAre(2,"mp") Then - If Peek(4) = ";"c Then + If TryPeek(4, c1) AndAlso NextAre(2,"mp") Then + If c1 = ";"c Then Return XmlMakeAmpLiteralToken(precedingTrivia) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, 4, "&") @@ -1172,9 +1155,8 @@ CreateNCNameToken: Return DirectCast(noSemicolon.SetDiagnostics({noSemicolonError}), XmlTextTokenSyntax) End If - ElseIf CanGet(5) AndAlso NextAre(2,"pos") Then - - If Peek(5) = ";"c Then + ElseIf TryPeek(5, c1) AndAlso NextAre(2,"pos") Then + If c1 = ";"c Then Return XmlMakeAposLiteralToken(precedingTrivia) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, 5, "'") @@ -1185,10 +1167,8 @@ CreateNCNameToken: Case "l"c ' // < - - If CanGet(3) AndAlso NextIs(2,"t"c) Then - - If Peek(3) = ";"c Then + If TryPeek(3, c1) AndAlso NextIs(2,"t"c) Then + If c1 = ";"c Then Return XmlMakeLtLiteralToken(precedingTrivia) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, 3, "<") @@ -1199,10 +1179,8 @@ CreateNCNameToken: Case "g"c ' // > - - If CanGet(3) AndAlso NextIs(2,"t"c) Then - - If Peek(3) = ";"c Then + If TryPeek(3, c1) AndAlso NextIs(2,"t"c) Then + If c1 = ";"c Then Return XmlMakeGtLiteralToken(precedingTrivia) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, 3, ">") @@ -1213,10 +1191,8 @@ CreateNCNameToken: Case "q"c ' // " - - If CanGet(5) AndAlso NextAre(2,"uot") Then - - If Peek(5) = ";"c Then + If TryPeek(5, c1) AndAlso NextAre(2,"uot") Then + If c1 = ";"c Then Return XmlMakeQuotLiteralToken(precedingTrivia) Else Dim noSemicolon = XmlMakeEntityLiteralToken(precedingTrivia, 5, """") @@ -1236,21 +1212,18 @@ CreateNCNameToken: Private Function ScanXmlCharRef(ByRef index As Integer) As XmlCharResult Debug.Assert(index >= 0) - - If Not CanGet(index) Then + Dim ch As Char + If Not TryPeek(index, ch) Then Return Nothing End If ' cannot reuse Scratch as this can be used in a nested call. Dim charRefSb As New StringBuilder Dim Here = index - - Dim ch = Peek(Here) If ch = "x"c Then Here += 1 - While CanGet(Here) - ch = Peek(Here) + While TryPeek(Here, ch) If XmlCharType.IsHexDigit(ch) Then charRefSb.Append(ch) Else @@ -1266,13 +1239,9 @@ CreateNCNameToken: Return result End If Else - While CanGet(Here) - ch = Peek(Here) - If XmlCharType.IsDigit(ch) Then - charRefSb.Append(ch) - Else - Exit While - End If + + While TryPeek(Here, ch) AndAlso XmlCharType.IsDigit(ch) + charRefSb.Append(ch) Here += 1 End While If charRefSb.Length > 0 Then diff --git a/src/Compilers/VisualBasic/Portable/Scanner/XmlDocComments.vb b/src/Compilers/VisualBasic/Portable/Scanner/XmlDocComments.vb index 175b5145f172f..a840a753729b1 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/XmlDocComments.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/XmlDocComments.vb @@ -51,7 +51,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Debug.Assert(IsAtNewLine) ' leading whitespace until we see ''' should be regular whitespace - If CanGet() AndAlso IsWhitespace(Peek()) Then + Dim c As Char + If TryPeek(c) AndAlso IsWhitespace(c) Then Dim ws = ScanWhitespace() tList.Add(ws) End If @@ -147,14 +148,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax ' lexes (ws)''' Private Function TrySkipXmlDocMarker(ByRef len As Integer) As Boolean + Dim c As Char Dim Here = len - While CanGet(Here) - Dim c = Peek(Here) - If IsWhitespace(c) Then - Here += 1 - Else - Exit While - End If + While TryPeek(Here, c) AndAlso IsWhitespace(c) + Here += 1 End While If StartsXmlDoc(Here) Then @@ -186,7 +183,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim len = 0 Do - If c = " "c OrElse c = CHARACTER_TABULATION Then + If c.IsAnyOf(" "c, CHARACTER_TABULATION) Then len += 1 ElseIf IsNewLine(c) Then @@ -240,9 +237,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Dim Here As Integer = 0 Dim scratch = GetScratch() - - While CanGet(Here) - Dim c As Char = Peek(Here) + Dim c As Char + While TryPeek(Here, c) Select Case (c) Case CARRIAGE_RETURN, LINE_FEED @@ -282,12 +278,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End If Debug.Assert(Here = 0) - If CanGet(1) Then - Dim ch As Char = Peek(1) + Dim ch As Char + If TryPeek(1, ch) Then Select Case ch Case "!"c - If CanGet(2) Then - Select Case (Peek(2)) + Dim c2 As Char + If TryPeek(2, c2) Then + Select Case c2 Case "-"c If NextIs(3,"-"c) Then Return XmlMakeBeginCommentToken(precedingTrivia, s_scanNoTriviaFunc) @@ -370,10 +367,10 @@ ScanChars: precedingTrivia.Add(xDocTrivia) End If - If state = ScannerState.StartProcessingInstruction AndAlso CanGet() Then + Dim c As Char + If state = ScannerState.StartProcessingInstruction AndAlso TryPeek(c) Then ' // Whitespace ' // S ::= (#x20 | #x9 | #xD | #xA)+ - Dim c = Peek() Select Case c Case CARRIAGE_RETURN, LINE_FEED, " "c, CHARACTER_TABULATION Dim offsets = CreateOffsetRestorePoint() @@ -387,8 +384,7 @@ ScanChars: End If Dim Here = 0 - While CanGet(Here) - Dim c As Char = Peek(Here) + While TryPeek(Here, c) Select Case (c) Case CARRIAGE_RETURN, LINE_FEED @@ -461,7 +457,8 @@ CleanUp: precedingTrivia = New SyntaxList(Of VisualBasicSyntaxNode)(xDocTrivia) End If - While CanGet() + Dim c As Char + While TryPeek(c) If Not precedingTrivia.Any AndAlso IsAtNewLine() AndAlso Not Me._doNotRequireXmlDocCommentPrefix Then ' this would indicate that we looked at Trivia, but did not find ' XmlDoc prefix (or we would not be at the line start) @@ -469,8 +466,6 @@ CleanUp: Return MakeEofToken(precedingTrivia) End If - Dim c As Char = Peek() - Select Case (c) ' // Whitespace ' // S ::= (#x20 | #x9 | #xD | #xA)+ @@ -506,12 +501,12 @@ CleanUp: Return XmlMakeDoubleQuoteToken(precedingTrivia, c, isOpening:=True) Case "<"c - If CanGet(1) Then - Dim ch As Char = Peek(1) + Dim ch,c2 As Char + If TryPeek(1, ch) Then Select Case ch Case "!"c - If CanGet(2) Then - Select Case (Peek(2)) + If TryPeek(2, c2) Then + Select Case c2 Case "-"c If NextIs(3,"-"c) Then Return XmlMakeBeginCommentToken(precedingTrivia, s_scanNoTriviaFunc) diff --git a/src/Compilers/VisualBasic/Portable/Scanner/XmlTokenFactories.vb b/src/Compilers/VisualBasic/Portable/Scanner/XmlTokenFactories.vb index e2d42a6ea3f53..53caae4b5a3bf 100644 --- a/src/Compilers/VisualBasic/Portable/Scanner/XmlTokenFactories.vb +++ b/src/Compilers/VisualBasic/Portable/Scanner/XmlTokenFactories.vb @@ -319,11 +319,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax End Function Private Function XmlMakeEndEmbeddedToken(precedingTrivia As SyntaxList(Of VisualBasicSyntaxNode), scanTrailingTrivia As ScanTriviaFunc) As PunctuationSyntax - Debug.Assert(Peek() = "%"c OrElse Peek() = FULLWIDTH_PERCENT_SIGN) + Dim c = Peek + Debug.Assert(c.IsAnyOf("%"c, FULLWIDTH_PERCENT_SIGN)) Debug.Assert(Peek(1) = ">"c) Dim spelling As String - If Peek() = "%"c Then + If c = "%"c Then AdvanceChar(2) spelling = "%>" Else