|
' 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.
'-----------------------------------------------------------------------------------------------------------
' This is the code that actually outputs the VB code that defines the tree. It is passed a read and validated
' ParseTree, and outputs the code to defined the node classes for that tree, and also additional data
' structures like the kinds, visitor, etc.
'-----------------------------------------------------------------------------------------------------------
Imports System.IO
Public Class TestWriter
Inherits WriteUtils
Private ReadOnly _checksum As String
Private _writer As TextWriter 'output is sent here.
Private Const s_externalSourceDirectiveString As String = "ExternalSourceDirective"
' Initialize the class with the parse tree to write.
Public Sub New(parseTree As ParseTree, checksum As String)
MyBase.New(parseTree)
_checksum = checksum
End Sub
' Write out the code defining the tree to the give file.
Public Sub WriteTestCode(writer As TextWriter)
_writer = writer
GenerateFile()
End Sub
Private Sub GenerateFile()
_writer.WriteLine("' Tests for parse trees.")
_writer.WriteLine("' DO NOT HAND EDIT")
_writer.WriteLine()
GenerateNamespace()
End Sub
Private Sub GenerateNamespace()
_writer.WriteLine()
_writer.WriteLine("Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests")
_writer.WriteLine()
_writer.WriteLine("Partial Public Class GeneratedTests")
_writer.WriteLine()
_writer.WriteLine("#region ""Green Factory Calls""")
GenerateFactoryCalls(True)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Green Factory Tests""")
GenerateFactoryCallTests(True)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Green Rewriter Tests""")
GenerateRewriterTests(True)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Green Visitor Tests""")
GenerateVisitorTests(True)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Red Factory Calls""")
GenerateFactoryCalls(False)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Red Factory Tests""")
GenerateFactoryCallTests(False)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("#region ""Red Rewriter Tests""")
GenerateRewriterTests(False)
_writer.WriteLine("#end region")
_writer.WriteLine()
_writer.WriteLine("End Class")
_writer.WriteLine("End Namespace")
End Sub
Private Sub GenerateFactoryCalls(isGreen As Boolean)
For Each nodeStructure In _parseTree.NodeStructures.Values
If Not nodeStructure.Abstract AndAlso Not nodeStructure.NoFactory Then
If Not nodeStructure.Name = "KeywordSyntax" AndAlso Not nodeStructure.Name = "PunctuationSyntax" Then
GenerateFactoryCall(isGreen, nodeStructure)
End If
End If
Next
End Sub
Private Sub GenerateFactoryCall(isGreen As Boolean, nodeStructure As ParseNodeStructure)
For Each kind In nodeStructure.NodeKinds
GenerateFactoryCall(isGreen, nodeStructure, kind)
Next
'If nodeStructure.NodeKinds.Count > 1 Then
' GenerateFactoryCall(isGreen, nodeStructure, Nothing)
'End If
End Sub
Private Sub GenerateFactoryCall(isGreen As Boolean, nodeStructure As ParseNodeStructure, nodeKind As ParseNodeKind)
If nodeKind.Name = "AttributeTarget" AndAlso Not isGreen Then
Dim x = 0
End If
If nodeKind.Name.Contains(s_externalSourceDirectiveString) Then
Return ' check for fix
End If
Dim namespacePrefix As String = If(isGreen, "InternalSyntax.", String.Empty)
_writer.Write(" Private Shared Function ")
Dim functionName As String = If(nodeKind Is Nothing, FactoryName(nodeStructure), FactoryName(nodeKind))
If isGreen Then
_writer.Write("GenerateGreen" + functionName)
Else
_writer.Write("GenerateRed" + functionName)
End If
If isGreen Then
_writer.WriteLine("() As " + namespacePrefix + nodeStructure.Name)
Else
If nodeStructure.IsToken Then
_writer.WriteLine("() As SyntaxToken")
ElseIf nodeStructure.IsTrivia Then
_writer.WriteLine("() As SyntaxTrivia")
Else
_writer.WriteLine("() As {0}", StructureTypeName(nodeStructure))
End If
End If
Dim first As Boolean = True
Dim callTokens As List(Of String) = New List(Of String)()
Dim anePositions As List(Of Integer) = New List(Of Integer)()
Dim aePositions As List(Of Integer) = New List(Of Integer)()
Dim KindNonePositions As List(Of Integer) = New List(Of Integer)()
Dim currentLine = 1
If nodeKind Is Nothing Then
callTokens.Add(namespacePrefix + "SyntaxFactory." + nodeStructure.Name + "(")
Else
callTokens.Add(namespacePrefix + "SyntaxFactory." + nodeKind.Name + "(")
End If
If nodeStructure.IsToken Then
If isGreen Then
If nodeStructure.IsTerminal Then
callTokens.Add("String.Empty")
first = False
End If
Else
If Not first Then callTokens.Add(", ")
first = False
callTokens.Add(namespacePrefix + "SyntaxFactory.TriviaList(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""))")
'If nodeStructure.Name.Contains("Xml") Then
' callTokens.Add("String.Empty")
' first = False
'End If
If nodeKind.Name.EndsWith("LiteralToken", StringComparison.Ordinal) OrElse
nodeKind.Name.EndsWith("XmlNameToken", StringComparison.Ordinal) OrElse
nodeKind.Name.EndsWith("DocumentationCommentLineBreakToken", StringComparison.Ordinal) OrElse
nodeKind.Name = "InterpolatedStringTextToken" _
Then
If Not first Then callTokens.Add(", ")
callTokens.Add("String.Empty")
first = False
End If
End If
Dim fields = GetAllFieldsOfStructure(nodeStructure)
For Each field In fields
If Not first Then callTokens.Add(", ")
first = False
Dim fieldType = FieldTypeRef(field)
callTokens.Add(GetInitValueForType(fieldType))
Next
If Not first Then callTokens.Add(", ")
first = False
If isGreen Then
callTokens.Add(namespacePrefix + "SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""), ")
callTokens.Add(namespacePrefix + "SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""))")
Else
callTokens.Add(namespacePrefix + "SyntaxFactory.TriviaList(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" "")))")
End If
If isGreen Then
_writer.Write(" return ")
callTokens.ForEach(AddressOf _writer.Write)
_writer.WriteLine()
Else
_writer.WriteLine(" Dim exceptionTest as boolean = false")
For exceptionChecks = 0 To anePositions.Count - 1
_writer.WriteLine(" Try")
For i = 0 To callTokens.Count - 1
If (i <> anePositions(exceptionChecks)) Then
_writer.Write(callTokens(i))
Else
_writer.Write("Nothing")
End If
Next
_writer.WriteLine(" catch e as ArgumentNullException")
_writer.WriteLine(" exceptionTest = true")
_writer.WriteLine(" End Try")
_writer.WriteLine(" Debug.Assert(exceptionTest)")
_writer.WriteLine(" exceptionTest = false")
_writer.WriteLine()
Next
' quick hack to cover more code in keyword factories ...
If nodeStructure.IsTerminal AndAlso Not nodeStructure.IsTrivia AndAlso nodeStructure.Name = "KeywordSyntax" Then
_writer.Write(" Dim node1 = ")
For i = 0 To callTokens.Count - 1
_writer.Write(callTokens(i))
If i = 0 Then _writer.Write("String.Empty, ")
Next i
_writer.WriteLine()
_writer.Write(" dim node2 = ")
callTokens.ForEach(AddressOf _writer.Write)
_writer.WriteLine()
_writer.WriteLine(" Debug.Assert(node1.GetText() = String.Empty)")
_writer.WriteLine(" Debug.Assert(node2.GetText() <> String.Empty)")
_writer.WriteLine()
' make parameter = nothing to cause exceptions
_writer.WriteLine(" Try")
_writer.WriteLine(" exceptionTest = false")
For i = 0 To callTokens.Count - 1
_writer.Write(callTokens(i))
If i = 0 Then _writer.Write("Nothing, ")
Next i
_writer.WriteLine()
_writer.WriteLine(" catch e as ArgumentNullException")
_writer.WriteLine(" exceptionTest = true")
_writer.WriteLine(" End Try")
_writer.WriteLine(" Debug.Assert(exceptionTest)")
_writer.WriteLine()
_writer.WriteLine(" return node2")
Else
_writer.Write(" return ")
callTokens.ForEach(AddressOf _writer.Write)
_writer.WriteLine()
End If
End If
Else
Dim children = GetAllFactoryChildrenOfStructure(nodeStructure)
If nodeStructure.IsTrivia Then
callTokens.Add("String.Empty")
anePositions.Add(callTokens.Count - 1)
first = False
End If
For Each child In children
If Not first Then
callTokens.Add(", ")
End If
If child.IsOptional Then
' Hack: remove when the factory methods have been fixed to not contain overloads.
If nodeStructure.Name = "MemberAccessExpressionSyntax" Then
If first Then
callTokens.Add(String.Format("CType(Nothing, {0}{1})", namespacePrefix, ChildFieldTypeRef(child)))
End If
Else
callTokens.Add("Nothing")
End If
' TODO: remove
first = False
Continue For
End If
' TODO: move up.
first = False
Dim childNodeKind As ParseNodeKind = If(Not child Is Nothing, TryCast(child.ChildKind, ParseNodeKind), Nothing)
If TypeOf child.ChildKind Is List(Of ParseNodeKind) Then
childNodeKind = child.ChildKind(nodeKind.Name)
End If
If childNodeKind Is Nothing Then
childNodeKind = DirectCast(child.ChildKind, List(Of ParseNodeKind)).Item(0)
End If
If child.IsList AndAlso child.IsSeparated Then
Dim childKindStructure = child.ParseTree.NodeStructures(childNodeKind.StructureId)
childKindStructure = If(Not childKindStructure.ParentStructure Is Nothing, childKindStructure.ParentStructure, childKindStructure)
callTokens.Add("New " + ChildFactoryTypeRef(nodeStructure, child, isGreen, True) + "()")
Else
Dim structureOfchild = child.ParseTree.NodeStructures(childNodeKind.StructureId)
If structureOfchild.Name = "PunctuationSyntax" OrElse structureOfchild.Name = "KeywordSyntax" Then
If isGreen Then
callTokens.Add("new InternalSyntax." + structureOfchild.Name + "(")
callTokens.Add("SyntaxKind." + childNodeKind.Name + ", String.Empty, Nothing, Nothing)")
Else
Dim token = "SyntaxFactory.Token(SyntaxKind." + childNodeKind.Name + ")"
If child.IsList Then
token = "SyntaxTokenList.Create(" & token & ")"
End If
callTokens.Add(token)
' add none kind here
If Not TypeOf child.ChildKind Is List(Of ParseNodeKind) Then
If child.IsOptional Then
KindNonePositions.Add(callTokens.Count - 1)
End If
Else
If Not child.IsList Then
aePositions.Add(callTokens.Count - 1)
End If
End If
End If
Else
If isGreen Then
callTokens.Add("GenerateGreen" + FactoryName(childNodeKind) + "()")
Else
Dim result = "GenerateRed" + FactoryName(childNodeKind) + "()"
If structureOfchild.IsToken AndAlso child.IsList Then
result = "SyntaxTokenList.Create(" & result & ")"
ElseIf child.IsSeparated Then
result = String.Format("SyntaxFactory.SingletonSeparatedList(Of {0}({1})", BaseTypeReference(child), result)
ElseIf child.IsList Then
result = String.Format("SyntaxFactory.SingletonList(Of {0})({1})", BaseTypeReference(child), result)
End If
callTokens.Add(result)
End If
End If
If Not KindTypeStructure(child.ChildKind).IsToken AndAlso Not child.IsList Then
anePositions.Add(callTokens.Count - 1)
ElseIf KindTypeStructure(child.ChildKind).IsToken And Not child.IsList Then
aePositions.Add(callTokens.Count - 1)
End If
End If
Next
callTokens.Add(")")
' TODO: remove extra conditions
If isGreen OrElse nodeStructure.Name = "CaseBlockSyntax" OrElse nodeStructure.Name = "IfPartSyntax" OrElse nodeStructure.Name = "MultiLineIfBlockSyntax" Then
_writer.Write(" return ")
callTokens.ForEach(AddressOf _writer.Write)
_writer.WriteLine()
Else
_writer.WriteLine(" Dim exceptionTest as boolean = false")
For exceptionChecks = 0 To anePositions.Count - 1
_writer.WriteLine(" Try")
_writer.Write(" ")
For i = 0 To callTokens.Count - 1
If (i <> anePositions(exceptionChecks)) Then
_writer.Write(callTokens(i))
Else
_writer.Write("Nothing")
End If
Next
_writer.WriteLine()
_writer.WriteLine(" catch e as ArgumentNullException")
_writer.WriteLine(" exceptionTest = true")
_writer.WriteLine(" End Try")
_writer.WriteLine(" Debug.Assert(exceptionTest)")
_writer.WriteLine(" exceptionTest = false")
_writer.WriteLine()
Next
For exceptionChecks = 0 To aePositions.Count - 1
_writer.WriteLine(" Try")
_writer.Write(" ")
For i = 0 To callTokens.Count - 1
If (i <> aePositions(exceptionChecks)) Then
_writer.Write(callTokens(i))
Else
_writer.Write("SyntaxFactory.Token(SyntaxKind.ExternalSourceKeyword)") ' this syntaxtoken should not be legal anywhere in the tests
End If
Next
_writer.WriteLine()
_writer.WriteLine(" catch e as ArgumentException")
_writer.WriteLine(" exceptionTest = true")
_writer.WriteLine(" End Try")
_writer.WriteLine(" Debug.Assert(exceptionTest)")
_writer.WriteLine(" exceptionTest = false")
_writer.WriteLine()
Next
For exceptionChecks = 0 To KindNonePositions.Count - 1
_writer.Write(" ")
For i = 0 To callTokens.Count - 1
If (i <> KindNonePositions(exceptionChecks)) Then
_writer.Write(callTokens(i))
Else
_writer.Write("New SyntaxToken(Nothing, New InternalSyntax.KeywordSyntax(SyntaxKind.None, Nothing, Nothing, """", Nothing, Nothing), 0, 0)")
End If
Next
_writer.WriteLine()
_writer.WriteLine()
Next
_writer.Write(" return ")
callTokens.ForEach(AddressOf _writer.Write)
_writer.WriteLine()
End If
End If
_writer.WriteLine(" End Function")
_writer.WriteLine()
End Sub
Private Sub GenerateFactoryCallTests(isGreen As Boolean)
For Each nodeStructure In _parseTree.NodeStructures.Values
If Not nodeStructure.Abstract AndAlso Not nodeStructure.NoFactory Then
If Not nodeStructure.Name = "KeywordSyntax" AndAlso Not nodeStructure.Name = "PunctuationSyntax" Then
GenerateFactoryCallTest(isGreen, nodeStructure)
End If
End If
Next
End Sub
Private Sub GenerateFactoryCallTest(isGreen As Boolean, nodeStructure As ParseNodeStructure)
For Each kind In nodeStructure.NodeKinds
GenerateFactoryCallTest(isGreen, nodeStructure, kind)
Next
End Sub
Private Sub GenerateFactoryCallTest(isGreen As Boolean, nodeStructure As ParseNodeStructure, nodeKind As ParseNodeKind)
If nodeKind.Name.Contains(s_externalSourceDirectiveString) Then
Return ' check for fix
End If
Dim funcNamePart = If(isGreen, "Green", "Red")
_writer.WriteLine(" <Fact>")
_writer.Write(" Public Sub ")
_writer.Write("Test{0}{1}", funcNamePart, FactoryName(nodeKind))
_writer.WriteLine("()")
_writer.WriteLine(" dim objectUnderTest = Generate{0}{1}()", funcNamePart, FactoryName(nodeKind))
'Dim children = GetAllChildrenOfStructure(nodeStructure)
If isGreen Then
_writer.WriteLine(" AttachAndCheckDiagnostics(objectUnderTest)")
Else
Dim withStat As String = Nothing
For Each child In GetAllChildrenOfStructure(nodeStructure)
If Not child.IsOptional Then
_writer.WriteLine(" Assert.NotNull(objectUnderTest.{0})", LowerFirstCharacter(child.Name))
End If
withStat += String.Format(".With{0}(objectUnderTest.{0})", child.Name)
Next
If (withStat IsNot Nothing) Then
_writer.WriteLine(" Dim withObj = objectUnderTest{0}", withStat)
_writer.WriteLine(" Assert.Equal(withobj, objectUnderTest)")
End If
End If
_writer.WriteLine(" End Sub")
_writer.WriteLine()
End Sub
Private Sub GenerateRewriterTests(isGreen As Boolean)
For Each nodeStructure In _parseTree.NodeStructures.Values
If Not nodeStructure.Abstract AndAlso Not nodeStructure.NoFactory AndAlso Not nodeStructure.IsToken AndAlso Not nodeStructure.IsTrivia Then
GenerateRewriterTest(isGreen, nodeStructure)
End If
Next
End Sub
Private Sub GenerateRewriterTest(isGreen As Boolean, nodeStructure As ParseNodeStructure)
For Each kind In nodeStructure.NodeKinds
GenerateRewriterTest(isGreen, nodeStructure, kind)
Next
End Sub
Private Sub GenerateRewriterTest(isGreen As Boolean, nodeStructure As ParseNodeStructure, nodeKind As ParseNodeKind)
If nodeKind.Name.Contains(s_externalSourceDirectiveString) Then
Return ' check for fix
End If
Dim funcNamePart = If(isGreen, "Green", "Red")
_writer.WriteLine(" <Fact>")
_writer.Write(" Public Sub ")
_writer.Write("Test{0}{1}Rewriter", funcNamePart, FactoryName(nodeKind))
_writer.WriteLine("()")
_writer.WriteLine(" dim oldNode = Generate{0}{1}()", funcNamePart, FactoryName(nodeKind))
_writer.WriteLine(" Dim rewriter = New {0}IdentityRewriter()", funcNamePart)
_writer.WriteLine(" Dim newNode = rewriter.Visit(oldNode)")
_writer.WriteLine(" Assert.Equal(oldNode, newNode)")
_writer.WriteLine(" End Sub")
_writer.WriteLine()
End Sub
Private Sub GenerateVisitorTests(isGreen As Boolean)
For Each nodeStructure In _parseTree.NodeStructures.Values
If Not nodeStructure.Abstract AndAlso Not nodeStructure.NoFactory AndAlso Not nodeStructure.IsToken AndAlso Not nodeStructure.IsTrivia Then
GenerateVisitorTest(isGreen, nodeStructure)
End If
Next
End Sub
Private Sub GenerateVisitorTest(isGreen As Boolean, nodeStructure As ParseNodeStructure)
For Each kind In nodeStructure.NodeKinds
GenerateVisitorTest(isGreen, nodeStructure, kind)
Next
End Sub
Private Sub GenerateVisitorTest(isGreen As Boolean, nodeStructure As ParseNodeStructure, nodeKind As ParseNodeKind)
If nodeKind.Name.Contains(s_externalSourceDirectiveString) Then
Return ' check for fix
End If
Dim funcNamePart = If(isGreen, "Green", "Red")
_writer.WriteLine(" <Fact>")
_writer.Write(" Public Sub ")
_writer.Write("Test" + funcNamePart + FactoryName(nodeKind) + "Visitor")
_writer.WriteLine("()")
_writer.WriteLine(" Dim oldNode = Generate" + funcNamePart + FactoryName(nodeKind) + "()")
_writer.WriteLine(" Dim visitor = New " + funcNamePart + "NodeVisitor()")
_writer.WriteLine(" visitor.Visit(oldNode)")
_writer.WriteLine(" End Sub")
_writer.WriteLine()
End Sub
Public Function GetInitValueForType(fieldType As String) As String
Select Case fieldType
Case "Integer"
Return "23"
Case "String"
Return """Bar"""
Case "Char"
Return """E""C"
Case "DateTime"
Return "New DateTime(2008,11,04)"
Case "System.Decimal"
Return "42"
Case "TypeCharacter"
Return "TypeCharacter.DecimalLiteral"
Case "SyntaxKind"
Return "SyntaxKind.IdentifierName"
Case Else
Return "Unknown Type"
End Select
End Function
Public Sub AddHandwrittenFactoryCall(baseType As String)
Select Case baseType
Case "IdentifierTokenSyntax"
_writer.Write("new InternalSyntax.SimpleIdentifierSyntax(SyntaxKind.IdentifierToken, Nothing, Nothing, ""text"",")
_writer.Write("InternalSyntax.SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""), ")
_writer.Write("InternalSyntax.SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""))")
Case "IntegerLiteralTokenSyntax"
_writer.Write("new InternalSyntax.IntegerLiteralToken(""42"", LiteralBase.Decimal, TypeCharacter.None, 42,")
_writer.Write("InternalSyntax.SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""), ")
_writer.Write("InternalSyntax.SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "" ""))")
End Select
End Sub
End Class
|