/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/SyntaxHelpers.vb
248 строк
13 KB
Cyrus Najmabadi
Organize VB imports
27 июн 2025, 21:53
27 июн 2025, 21:53
e9c47fd
Код
Авторство
О чём код?
' 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 System.Collections.ObjectModel Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator Friend Module SyntaxHelpers Friend ReadOnly ParseOptions As VisualBasicParseOptions = VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersionFacts.CurrentVersion) ''' <summary> ''' Parse expression. Returns null if there are any errors. ''' </summary> <Extension> Friend Function ParseExpression(expr As String, diagnostics As DiagnosticBag, allowFormatSpecifiers As Boolean, <Out> ByRef formatSpecifiers As ReadOnlyCollection(Of String)) As ExecutableStatementSyntax Dim syntax = ParseDebuggerExpression(expr, consumeFullText:=Not allowFormatSpecifiers) diagnostics.AddRange(syntax.GetDiagnostics()) formatSpecifiers = Nothing If allowFormatSpecifiers Then Dim builder = ArrayBuilder(Of String).GetInstance() If ParseFormatSpecifiers(builder, expr, syntax.Expression.FullWidth, diagnostics) AndAlso builder.Count > 0 Then formatSpecifiers = New ReadOnlyCollection(Of String)(builder.ToArray()) End If builder.Free() End If Return If(diagnostics.HasAnyErrors(), Nothing, syntax) End Function <Extension> Friend Function ParseAssignment(target As String, expr As String, diagnostics As DiagnosticBag) As AssignmentStatementSyntax Dim text = SourceText.From(expr, encoding:=Nothing, SourceHashAlgorithms.Default) Dim expression = SyntaxHelpers.ParseDebuggerExpressionInternal(text, consumeFullText:=True) ' We're creating a SyntaxTree for just the RHS so that the Diagnostic spans for parse errors ' will be correct (with respect to the original input text). If we ever expose a SemanticModel ' for debugger expressions, we should use this SyntaxTree. Dim syntaxTree = CreateSyntaxTree(expression, text) diagnostics.AddRange(syntaxTree.GetDiagnostics()) If diagnostics.HasAnyErrors Then Return Nothing End If ' Any Diagnostic spans produced in binding will be offset by the length of the "target" expression text. ' If we want to support live squiggles in debugger windows, SemanticModel, etc, we'll want to address this. Dim targetText = SourceText.From(target, encoding:=Nothing, SourceHashAlgorithms.Default) Dim targetSyntax = SyntaxHelpers.ParseDebuggerExpressionInternal(targetText, consumeFullText:=True) Debug.Assert(Not targetSyntax.GetDiagnostics().Any(), "The target of an assignment should never contain Diagnostics if we're being allowed to assign to it in the debugger.") Dim assignment = InternalSyntax.SyntaxFactory.SimpleAssignmentStatement( targetSyntax, New InternalSyntax.PunctuationSyntax(SyntaxKind.EqualsToken, "=", Nothing, Nothing), expression) Dim assignmentText = SourceText.From(assignment.ToString(), encoding:=Nothing, SourceHashAlgorithms.Default) syntaxTree = CreateSyntaxTree(assignment.MakeDebuggerStatementContext(), assignmentText) Return DirectCast(syntaxTree.GetDebuggerStatement(), AssignmentStatementSyntax) End Function ''' <summary> ''' Parse statement. Returns null if there are any errors. ''' </summary> <Extension> Friend Function ParseStatement(statement As String, diagnostics As DiagnosticBag) As StatementSyntax Dim syntax = ParseDebuggerStatement(statement) diagnostics.AddRange(syntax.GetDiagnostics()) Return If(diagnostics.HasAnyErrors(), Nothing, syntax) End Function ''' <summary> ''' Return set of identifier tokens, with leading And ''' trailing spaces And comma separators removed. ''' </summary> ''' <remarks> ''' The native VB EE didn't support format specifiers. ''' </remarks> Private Function ParseFormatSpecifiers( builder As ArrayBuilder(Of String), expr As String, offset As Integer, diagnostics As DiagnosticBag) As Boolean Dim expectingComma = True Dim start = -1 Dim n = expr.Length While offset < n Dim c = expr(offset) If SyntaxFacts.IsWhitespace(c) OrElse c = ","c Then If start >= 0 Then Dim token = expr.Substring(start, offset - start) If expectingComma Then ReportInvalidFormatSpecifier(token, diagnostics) Return False End If builder.Add(token) start = -1 expectingComma = c <> ","c ElseIf c = ","c Then If Not expectingComma Then ReportInvalidFormatSpecifier(",", diagnostics) Return False End If expectingComma = False End If ElseIf start < 0 Then start = offset End If offset = offset + 1 End While If start >= 0 Then Dim token = expr.Substring(start) If expectingComma Then ReportInvalidFormatSpecifier(token, diagnostics) Return False End If builder.Add(token) ElseIf Not expectingComma Then ReportInvalidFormatSpecifier(",", diagnostics) Return False End If ' Verify format specifiers are valid identifiers. For Each token In builder If Not token.All(AddressOf SyntaxFacts.IsIdentifierPartCharacter) Then ReportInvalidFormatSpecifier(token, diagnostics) Return False End If Next Return True End Function Private Sub ReportInvalidFormatSpecifier(token As String, diagnostics As DiagnosticBag) diagnostics.Add(ERRID.ERR_InvalidFormatSpecifier, Location.None, token) End Sub ''' <summary> ''' Parse a debugger expression (e.g. possibly including pseudo-variables). ''' </summary> ''' <param name="source">The input string</param> ''' <remarks> ''' It would be better if this method returned ExpressionStatementSyntax, but this is the best we can do for ''' the time being due to issues in the binder resolving ambiguities between invocations and array access. ''' </remarks> Friend Function ParseDebuggerExpression(source As String, consumeFullText As Boolean) As PrintStatementSyntax Dim text = SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default) Dim expression = ParseDebuggerExpressionInternal(text, consumeFullText) Dim statement = InternalSyntax.SyntaxFactory.PrintStatement( New InternalSyntax.PunctuationSyntax(SyntaxKind.QuestionToken, "?", Nothing, Nothing), expression) Dim syntaxTree = CreateSyntaxTree(statement.MakeDebuggerStatementContext(), text) Return DirectCast(syntaxTree.GetDebuggerStatement(), PrintStatementSyntax) End Function Private Function ParseDebuggerExpressionInternal(source As SourceText, consumeFullText As Boolean) As InternalSyntax.ExpressionSyntax Using scanner As New InternalSyntax.Scanner(source, ParseOptions, isScanningForExpressionCompiler:=True) ' NOTE: Default options should be enough Using p = New InternalSyntax.Parser(scanner) p.GetNextToken() Dim node = p.ParseExpression() If consumeFullText Then node = p.ConsumeUnexpectedTokens(node) Return node End Using End Using End Function Private Function ParseDebuggerStatement(source As String) As StatementSyntax Dim text = SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default) Using scanner As New InternalSyntax.Scanner(text, ParseOptions, isScanningForExpressionCompiler:=True) ' NOTE: Default options should be enough Using p = New InternalSyntax.Parser(scanner) p.GetNextToken() Dim node = p.ParseStatementInMethodBody() node = p.ConsumeUnexpectedTokens(node) Dim syntaxTree = CreateSyntaxTree(node.MakeDebuggerStatementContext(), text) Return syntaxTree.GetDebuggerStatement() End Using End Using End Function Private Function CreateSyntaxTree(root As InternalSyntax.VisualBasicSyntaxNode, text As SourceText) As SyntaxTree Return VisualBasicSyntaxTree.CreateForDebugger(DirectCast(root.CreateRed(Nothing, 0), VisualBasicSyntaxNode), text, ParseOptions) End Function <Extension> Private Function MakeDebuggerStatementContext(statement As InternalSyntax.StatementSyntax) As InternalSyntax.CompilationUnitSyntax Return InternalSyntax.SyntaxFactory.CompilationUnit( options:=Nothing, [imports]:=Nothing, attributes:=Nothing, members:=Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList.List(statement), endOfFileToken:=InternalSyntax.SyntaxFactory.EndOfFileToken) End Function <Extension> Private Function GetDebuggerStatement(syntaxTree As SyntaxTree) As StatementSyntax Return DirectCast(DirectCast(syntaxTree.GetRoot(), CompilationUnitSyntax).Members.Single(), StatementSyntax) End Function ''' <summary> ''' This list is based on the statements found in StatementAnalyzer::IsSupportedStatement ''' (vb\language\debugger\statementanalyzer.cpp). ''' We'll add to that list some additional statements that can easily be supported by the new implementation ''' (include all compound assignments, not just "+=", etc). ''' For now, we'll leave out single line If statements, as the parsing for those would require extra ''' complexity on the EE side (ParseStatementInMethodBody should handle them, but it doesn't...). ''' </summary> Friend Function IsSupportedDebuggerStatement(syntax As StatementSyntax) As Boolean Select Case syntax.Kind Case SyntaxKind.AddAssignmentStatement, SyntaxKind.CallStatement, SyntaxKind.ConcatenateAssignmentStatement, SyntaxKind.DivideAssignmentStatement, SyntaxKind.ExponentiateAssignmentStatement, SyntaxKind.ExpressionStatement, SyntaxKind.IntegerDivideAssignmentStatement, SyntaxKind.LeftShiftAssignmentStatement, SyntaxKind.MultiplyAssignmentStatement, SyntaxKind.PrintStatement, SyntaxKind.ReDimStatement, SyntaxKind.ReDimPreserveStatement, SyntaxKind.RightShiftAssignmentStatement, SyntaxKind.SimpleAssignmentStatement, SyntaxKind.SubtractAssignmentStatement Return True Case Else Return False End Select End Function Friend Function EscapeKeywordIdentifiers(identifier As String) As String If SyntaxFacts.IsKeywordKind(SyntaxFacts.GetKeywordKind(identifier)) Then Dim pooled = PooledStringBuilder.GetInstance() Dim builder = pooled.Builder builder.Append("["c) builder.Append(identifier) builder.Append("]"c) Return pooled.ToStringAndFree() Else Return identifier End If End Function End Module End Namespace