File: GreenNodes\GreenNodeWriter.vb
Web Access
Project: src\src\Tools\Source\CompilerGeneratorTools\Source\VisualBasicSyntaxGenerator\VisualBasicSyntaxGenerator.vbproj (VisualBasicSyntaxGenerator)
' 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
 
' Class to write out the code for the code tree.
Friend Class GreenNodeWriter
    Inherits WriteUtils
 
    Private _writer As TextWriter    'output is sent here.
    Private ReadOnly _nonterminalsWithOneChild As List(Of String) = New List(Of String)
    Private ReadOnly _nonterminalsWithTwoChildren As List(Of String) = New List(Of String)
 
    ' Initialize the class with the parse tree to write.
    Public Sub New(parseTree As ParseTree)
        MyBase.New(parseTree)
    End Sub
 
    ' Write out the code defining the tree to the give file.
    Public Sub WriteTreeAsCode(writer As TextWriter)
        _writer = writer
 
        GenerateFile()
    End Sub
 
    Private Sub GenerateFile()
        GenerateNamespace()
    End Sub
 
    Private Sub GenerateNamespace()
        _writer.WriteLine()
        If Not String.IsNullOrEmpty(_parseTree.NamespaceName) Then
            _writer.WriteLine("Namespace {0}", Ident(_parseTree.NamespaceName) + ".Syntax.InternalSyntax")
            _writer.WriteLine()
        End If
 
        GenerateNodeStructures()
 
        If Not String.IsNullOrEmpty(_parseTree.VisitorName) Then
            GenerateVisitorClass()
        End If
 
        If Not String.IsNullOrEmpty(_parseTree.RewriteVisitorName) Then
            GenerateRewriteVisitorClass()
        End If
 
        If Not String.IsNullOrEmpty(_parseTree.NamespaceName) Then
            _writer.WriteLine("End Namespace")
        End If
 
        'DumpNames("Nodes with One Child", _nonterminalsWithOneChild)
        'DumpNames("Nodes with Two Children", _nonterminalsWithTwoChildren)
    End Sub
 
#Disable Warning IDE0051 ' Remove unused private members
    Private Sub DumpNames(title As String, names As List(Of String))
#Enable Warning IDE0051 ' Remove unused private members
        Console.WriteLine(title)
        Console.WriteLine("=======================================")
        Dim sortedNames = From n In names Order By n
        For Each name In sortedNames
            Console.WriteLine(name)
        Next
        Console.WriteLine()
    End Sub
 
    Private Sub GenerateNodeStructures()
        For Each nodeStructure In _parseTree.NodeStructures.Values
            If Not nodeStructure.NoFactory Then
                GenerateNodeStructureClass(nodeStructure)
            End If
        Next
    End Sub
 
    ' Generate a class declaration for a node structure.
    Private Sub GenerateNodeStructureClass(nodeStructure As ParseNodeStructure)
        ' XML comment
        GenerateXmlComment(_writer, nodeStructure, 4, includeRemarks:=False)
 
        ' Class name
        _writer.Write("    ")
        If (nodeStructure.PartialClass) Then
            _writer.Write("Partial ")
        End If
        Dim visibility As String = "Friend"
 
        If _parseTree.IsAbstract(nodeStructure) Then
            _writer.WriteLine("{0} MustInherit Class {1}", visibility, StructureTypeName(nodeStructure))
        ElseIf Not nodeStructure.HasDerivedStructure Then
            _writer.WriteLine("{0} NotInheritable Class {1}", visibility, StructureTypeName(nodeStructure))
        Else
            _writer.WriteLine("{0} Class {1}", visibility, StructureTypeName(nodeStructure))
        End If
 
        ' Base class
        If Not IsRoot(nodeStructure) Then
            _writer.WriteLine("        Inherits {0}", StructureTypeName(nodeStructure.ParentStructure))
        End If
        _writer.WriteLine()
 
        'Create members
        GenerateNodeStructureMembers(nodeStructure)
 
        ' Create the constructor.
        GenerateNodeStructureConstructor(nodeStructure, noExtra:=True)
        GenerateNodeStructureConstructor(nodeStructure, noExtra:=True, contextual:=True)
        GenerateNodeStructureConstructor(nodeStructure)
 
        GenerateCreateRed(nodeStructure)
 
        ' Create the property accessor for each of the fields
        Dim fields = nodeStructure.Fields
        For i = 0 To fields.Count - 1
            GenerateNodeFieldProperty(fields(i), i, fields(i).ContainingStructure IsNot nodeStructure)
        Next
 
        ' Create the property accessor for each of the children
        Dim children = nodeStructure.Children
        For i = 0 To children.Count - 1
            GenerateNodeChildProperty(nodeStructure, children(i), i)
            GenerateNodeWithChildProperty(children(i), i, nodeStructure)
        Next
 
        If Not (_parseTree.IsAbstract(nodeStructure) OrElse nodeStructure.IsToken) Then
            If children.Count = 1 Then
                If Not children(0).IsList AndAlso Not KindTypeStructure(children(0).ChildKind).Name = "ExpressionSyntax" Then
                    _nonterminalsWithOneChild.Add(nodeStructure.Name)
                End If
            ElseIf children.Count = 2 Then
                If Not children(0).IsList AndAlso Not KindTypeStructure(children(0).ChildKind).Name = "ExpressionSyntax" AndAlso Not children(1).IsList AndAlso Not KindTypeStructure(children(1).ChildKind).Name = "ExpressionSyntax" Then
                    _nonterminalsWithTwoChildren.Add(nodeStructure.Name)
                End If
            End If
        End If
        'Create GetChild
        GenerateGetChild(nodeStructure)
 
        GenerateWithTrivia(nodeStructure)
 
        GenerateSetDiagnostics(nodeStructure)
 
        GenerateSetAnnotations(nodeStructure)
 
        ' Visitor accept method
        If Not String.IsNullOrEmpty(_parseTree.VisitorName) Then
            GenerateAccept(nodeStructure)
        End If
 
        ' Special methods for the root node.
        If IsRoot(nodeStructure) Then
            GenerateRootNodeSpecialMethods(nodeStructure)
        End If
 
        ' End the class
        _writer.WriteLine("    End Class")
        _writer.WriteLine()
    End Sub
 
    ' Generate CreateRed method 
    Private Sub GenerateCreateRed(nodeStructure As ParseNodeStructure)
 
        If _parseTree.IsAbstract(nodeStructure) OrElse nodeStructure.IsToken OrElse nodeStructure.IsTrivia Then
            Return
        End If
        _writer.WriteLine("        Friend Overrides Function CreateRed(ByVal parent As SyntaxNode, ByVal startLocation As Integer) As SyntaxNode")
        _writer.WriteLine("            Return new {0}.Syntax.{1}(Me, parent, startLocation)", _parseTree.NamespaceName, StructureTypeName(nodeStructure))
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
 
    End Sub
 
    ' Generate SetDiagnostics method 
    Private Sub GenerateSetDiagnostics(nodeStructure As ParseNodeStructure)
 
        If _parseTree.IsAbstract(nodeStructure) Then
            Return
        End If
 
        _writer.WriteLine("        Friend Overrides Function SetDiagnostics(ByVal newErrors As DiagnosticInfo()) As GreenNode")
        _writer.Write("            Return new {0}", StructureTypeName(nodeStructure))
        GenerateNodeStructureConstructorParameters(nodeStructure, "newErrors", "GetAnnotations", "GetLeadingTrivia", "GetTrailingTrivia")
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
 
    End Sub
 
    ' Generate SetAnnotations method 
    Private Sub GenerateSetAnnotations(nodeStructure As ParseNodeStructure)
 
        If _parseTree.IsAbstract(nodeStructure) Then
            Return
        End If
 
        _writer.WriteLine("        Friend Overrides Function SetAnnotations(ByVal annotations As SyntaxAnnotation()) As GreenNode")
        _writer.Write("            Return new {0}", StructureTypeName(nodeStructure))
        GenerateNodeStructureConstructorParameters(nodeStructure, "GetDiagnostics", "annotations", "GetLeadingTrivia", "GetTrailingTrivia")
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
 
    End Sub
 
    ' Generate WithTrivia method s
    Private Sub GenerateWithTrivia(nodeStructure As ParseNodeStructure)
 
        If _parseTree.IsAbstract(nodeStructure) OrElse Not nodeStructure.IsToken Then
            Return
        End If
 
        _writer.WriteLine("        Public Overrides Function WithLeadingTrivia(ByVal trivia As GreenNode) As GreenNode")
        _writer.Write("            Return new {0}", StructureTypeName(nodeStructure))
        GenerateNodeStructureConstructorParameters(nodeStructure, "GetDiagnostics", "GetAnnotations", "trivia", "GetTrailingTrivia")
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
 
        _writer.WriteLine("        Public Overrides Function WithTrailingTrivia(ByVal trivia As GreenNode) As GreenNode")
        _writer.Write("            Return new {0}", StructureTypeName(nodeStructure))
        GenerateNodeStructureConstructorParameters(nodeStructure, "GetDiagnostics", "GetAnnotations", "GetLeadingTrivia", "trivia")
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
    End Sub
 
    ' Generate GetChild, GetChildrenCount so members can be accessed by index
    Private Sub GenerateGetChild(nodeStructure As ParseNodeStructure)
        If _parseTree.IsAbstract(nodeStructure) OrElse nodeStructure.IsToken Then
            Return
        End If
 
        Dim allChildren = GetAllChildrenOfStructure(nodeStructure)
        Dim childrenCount = allChildren.Count
        If childrenCount = 0 Then
            Return
        End If
 
        _writer.WriteLine("        Friend Overrides Function GetSlot(i as Integer) as GreenNode")
 
        ' Create the property accessor for each of the children
        Dim children = allChildren
 
        If childrenCount <> 1 Then
            _writer.WriteLine("            Select case i")
 
            For i = 0 To childrenCount - 1
                _writer.WriteLine("                Case {0}", i)
                _writer.WriteLine("                    Return Me.{0}", ChildVarName(children(i)))
            Next
            _writer.WriteLine("                Case Else")
            _writer.WriteLine("                    Debug.Assert(false, ""child index out of range"")")
            _writer.WriteLine("                    Return Nothing")
            _writer.WriteLine("            End Select")
        Else
            _writer.WriteLine("            If i = 0 Then")
            _writer.WriteLine("                Return Me.{0}", ChildVarName(children(0)))
 
            _writer.WriteLine("            Else")
            _writer.WriteLine("                Debug.Assert(false, ""child index out of range"")")
            _writer.WriteLine("                Return Nothing")
            _writer.WriteLine("            End If")
        End If
 
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
 
        '_writer.WriteLine("        Friend Overrides ReadOnly Property SlotCount() As Integer")
        '_writer.WriteLine("            Get")
        '_writer.WriteLine("                Return {0}", childrenCount)
        '_writer.WriteLine("            End Get")
        '_writer.WriteLine("        End Property")
 
        _writer.WriteLine()
 
    End Sub
 
    Private Sub GenerateNodeStructureMembers(nodeStructure As ParseNodeStructure)
        Dim fields = nodeStructure.Fields
        For Each field In fields
            _writer.WriteLine("        Friend ReadOnly {0} as {1}", FieldVarName(field), FieldTypeRef(field))
        Next
 
        Dim children = nodeStructure.Children
        For Each child In children
            _writer.WriteLine("        Friend ReadOnly {0} as {1}", ChildVarName(child), ChildFieldTypeRef(child, True))
        Next
 
        _writer.WriteLine()
    End Sub
 
    ' Generate constructor for a node structure
    Private Sub GenerateNodeStructureConstructor(nodeStructure As ParseNodeStructure,
                                                 Optional noExtra As Boolean = False,
                                                 Optional contextual As Boolean = False)
 
        ' these constructors are hardcoded
        If nodeStructure.IsTokenRoot OrElse nodeStructure.IsTriviaRoot OrElse nodeStructure.Name = "StructuredTriviaSyntax" Then
            Return
        End If
 
        If nodeStructure.ParentStructure Is Nothing Then
            Return
        End If
 
        Dim allFields = GetAllFieldsOfStructure(nodeStructure)
 
        _writer.Write("        Friend Sub New(")
 
        ' Generate each of the field parameters
        _writer.Write("ByVal kind As {0}", NodeKindType())
        If Not noExtra Then
            _writer.Write(", ByVal errors as DiagnosticInfo(), ByVal annotations as SyntaxAnnotation()", NodeKindType())
        End If
 
        If nodeStructure.IsTerminal Then
            ' terminals have a text
            _writer.Write(", text as String")
        End If
 
        If nodeStructure.IsToken Then
            ' tokens have trivia
 
            _writer.Write(", leadingTrivia As GreenNode, trailingTrivia As GreenNode", StructureTypeName(_parseTree.RootStructure))
        End If
 
        For Each field In allFields
            _writer.Write(", ")
            GenerateNodeStructureFieldParameter(field)
        Next
 
        For Each child In GetAllChildrenOfStructure(nodeStructure)
            _writer.Write(", ")
            GenerateNodeStructureChildParameter(child, Nothing, True)
        Next
 
        If contextual Then
            _writer.Write(", context As ISyntaxFactoryContext")
        End If
 
        _writer.WriteLine(")")
 
        ' Generate each of the field parameters
        _writer.Write("            MyBase.New(kind", NodeKindType())
 
        If Not noExtra Then
            _writer.Write(", errors, annotations")
        End If
 
        If nodeStructure.IsToken AndAlso Not nodeStructure.IsTokenRoot Then
            ' nonterminals have text.
            _writer.Write(", text")
 
            If Not nodeStructure.IsTrivia Then
                ' tokens have trivia, but only if they are not trivia.
                _writer.Write(", leadingTrivia, trailingTrivia")
            End If
        End If
 
        Dim baseClass = nodeStructure.ParentStructure
        If baseClass IsNot Nothing Then
            For Each child In GetAllChildrenOfStructure(baseClass)
                _writer.Write(", {0}", ChildParamName(child))
            Next
        End If
 
        _writer.WriteLine(")")
 
        If Not nodeStructure.Abstract Then
            Dim allChildren = GetAllChildrenOfStructure(nodeStructure)
            Dim childrenCount = allChildren.Count
            If childrenCount <> 0 Then
                _writer.WriteLine("            Me.SlotCount = {0}", childrenCount)
            End If
        End If
 
        ' Generate code to initialize this class
 
        If contextual Then
            _writer.WriteLine("            Me.SetFactoryContext(context)")
        End If
 
        If allFields.Count > 0 Then
            For i = 0 To allFields.Count - 1
                _writer.WriteLine("            Me.{0} = {1}", FieldVarName(allFields(i)), FieldParamName(allFields(i)))
            Next
        End If
 
        If nodeStructure.Children.Count > 0 Then
            '_writer.WriteLine("            Dim fullWidth as integer")
            _writer.WriteLine()
 
            For Each child In nodeStructure.Children
                Dim indent = ""
                If child.IsOptional OrElse child.IsList Then
                    'If endKeyword IsNot Nothing Then
                    _writer.WriteLine("            If {0} IsNot Nothing Then", ChildParamName(child))
                    indent = "    "
                End If
 
                '_writer.WriteLine("{0}            fullWidth  += {1}.FullWidth", indent, ChildParamName(child))
                _writer.WriteLine("{0}            AdjustFlagsAndWidth({1})", indent, ChildParamName(child))
                _writer.WriteLine("{0}            Me.{1} = {2}", indent, ChildVarName(child), ChildParamName(child))
 
                If child.IsOptional OrElse child.IsList Then
                    'If endKeyword IsNot Nothing Then
                    _writer.WriteLine("            End If", ChildParamName(child))
                End If
            Next
 
            '_writer.WriteLine("            Me._fullWidth += fullWidth")
            _writer.WriteLine()
        End If
 
        'TODO: BLUE
        If StructureTypeName(nodeStructure) = "DirectiveTriviaSyntax" Then
            _writer.WriteLine("            SetFlags(NodeFlags.ContainsDirectives)")
        End If
 
        If StructureTypeName(nodeStructure) = "AttributeSyntax" Then
            _writer.WriteLine("            SetFlags(NodeFlags.ContainsAttributes)")
        End If
 
        ' Generate End Sub
        _writer.WriteLine("        End Sub")
        _writer.WriteLine()
 
    End Sub
 
    Private Sub GenerateNodeStructureConstructorParameters(nodeStructure As ParseNodeStructure, errorParam As String, annotationParam As String, precedingTriviaParam As String, followingTriviaParam As String)
        ' Generate each of the field parameters
        _writer.Write("(Me.Kind")
 
        _writer.Write(", {0}", errorParam)
        _writer.Write(", {0}", annotationParam)
 
        If nodeStructure.IsToken Then
            ' nonterminals have text.
            _writer.Write(", text")
 
            If Not nodeStructure.IsTrivia Then
                ' tokens have trivia, but only if they are not trivia.
                _writer.Write(", {0}, {1}", precedingTriviaParam, followingTriviaParam)
            End If
        ElseIf nodeStructure.IsTrivia AndAlso nodeStructure.IsTriviaRoot Then
            _writer.Write(", Me.Text")
        End If
 
        For Each field In GetAllFieldsOfStructure(nodeStructure)
            _writer.Write(", {0}", FieldVarName(field))
        Next
 
        For Each child In GetAllChildrenOfStructure(nodeStructure)
            _writer.Write(", {0}", ChildVarName(child))
        Next
 
        _writer.WriteLine(")")
    End Sub
 
    ' Generate a parameter corresponding to a node structure field
    Private Sub GenerateNodeStructureFieldParameter(field As ParseNodeField, Optional conflictName As String = Nothing)
        _writer.Write("{0} As {1}", FieldParamName(field, conflictName), FieldTypeRef(field))
    End Sub
 
    ' Generate a parameter corresponding to a node structure child
    Private Sub GenerateNodeStructureChildParameter(child As ParseNodeChild, Optional conflictName As String = Nothing, Optional isGreen As Boolean = False)
        _writer.Write("{0} As {1}", ChildParamName(child, conflictName), ChildConstructorTypeRef(child, isGreen))
    End Sub
 
    ' Get modifiers
    Private Function GetModifiers(containingStructure As ParseNodeStructure, isOverride As Boolean, name As String) As String
        ' Is this overridable or an override?
        Dim modifiers = ""
        'If isOverride Then
        '    modifiers = "Overrides "
        'ElseIf containingStructure.HasDerivedStructure Then
        '    modifiers = "Overridable "
        'End If
 
        ' Put Shadows modifier on if useful.
        ' Object has Equals and GetType
        ' root name has members for every kind and structure (factory methods)
        If (name = "Equals" OrElse name = "GetType") Then 'OrElse _parseTree.NodeKinds.ContainsKey(name) OrElse _parseTree.NodeStructures.ContainsKey(name)) Then
            modifiers = "Shadows " + modifiers
        End If
        Return modifiers
    End Function
 
    ' Generate a public property for a node field
    Private Sub GenerateNodeFieldProperty(field As ParseNodeField, fieldIndex As Integer, isOverride As Boolean)
        ' XML comment
        GenerateXmlComment(_writer, field, 8)
 
        _writer.WriteLine("        Friend {2}ReadOnly Property {0} As {1}", FieldPropertyName(field), FieldTypeRef(field), GetModifiers(field.ContainingStructure, isOverride, field.Name))
        _writer.WriteLine("            Get")
        _writer.WriteLine("                Return Me.{0}", FieldVarName(field))
        _writer.WriteLine("            End Get")
        _writer.WriteLine("        End Property")
        _writer.WriteLine("")
    End Sub
 
    ' Generate a public property for a child
    Private Sub GenerateNodeChildProperty(node As ParseNodeStructure, child As ParseNodeChild, childIndex As Integer)
        ' XML comment
        GenerateXmlComment(_writer, child, 8)
 
        Dim isToken = KindTypeStructure(child.ChildKind).IsToken
 
        _writer.WriteLine("        Friend {2}ReadOnly Property {0} As {1}", ChildPropertyName(child), ChildPropertyTypeRef(node, child, True), GetModifiers(child.ContainingStructure, False, child.Name))
        _writer.WriteLine("            Get")
        If Not child.IsList Then
            _writer.WriteLine("                Return Me.{0}", ChildVarName(child))
 
        ElseIf child.IsSeparated Then
            _writer.WriteLine("                Return new {0}(New Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(of {1})(Me.{2}))", ChildPropertyTypeRef(node, child, True), BaseTypeReference(child), ChildVarName(child))
 
        ElseIf KindTypeStructure(child.ChildKind).IsToken Then
            _writer.WriteLine("                Return New Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(of GreenNode)(Me.{1})", BaseTypeReference(child), ChildVarName(child))
 
        Else
            _writer.WriteLine("                Return new {0}(Me.{1})", ChildPropertyTypeRef(node, child, True), ChildVarName(child))
        End If
        _writer.WriteLine("            End Get")
        _writer.WriteLine("        End Property")
        _writer.WriteLine("")
 
    End Sub
 
    ' Generate a public property for a child
    Private Sub GenerateNodeWithChildProperty(withChild As ParseNodeChild, childIndex As Integer, nodeStructure As ParseNodeStructure)
        Dim isOverride As Boolean = withChild.ContainingStructure IsNot nodeStructure
        If withChild.GenerateWith Then
 
            Dim isAbstract As Boolean = _parseTree.IsAbstract(nodeStructure)
 
            If Not isAbstract Then
                ' XML comment
                GenerateWithXmlComment(_writer, withChild, 8)
                _writer.WriteLine("        Friend {2}Function {0}({3} as {4}) As {1}", ChildWithFunctionName(withChild), StructureTypeName(withChild.ContainingStructure), GetModifiers(withChild.ContainingStructure, isOverride, withChild.Name), Ident(UpperFirstCharacter(withChild.Name)), ChildConstructorTypeRef(withChild))
                _writer.WriteLine("            Ensures(Result(Of {0}) IsNot Nothing)", StructureTypeName(withChild.ContainingStructure))
                _writer.Write("            return New {0}(", StructureTypeName(nodeStructure))
 
                _writer.Write("Kind, Green.Errors")
 
                Dim allFields = GetAllFieldsOfStructure(nodeStructure)
                If allFields.Count > 0 Then
                    For i = 0 To allFields.Count - 1
                        _writer.Write(", {0}", FieldParamName(allFields(i)))
                    Next
                End If
 
                For Each child In nodeStructure.Children
                    If child IsNot withChild Then
                        _writer.Write(", {0}", ChildParamName(child))
                    Else
                        _writer.Write(", {0}", Ident(UpperFirstCharacter(child.Name)))
                    End If
                Next
 
                _writer.WriteLine(")")
                _writer.WriteLine("        End Function")
            ElseIf nodeStructure.Children.Contains(withChild) Then
                ' XML comment
                GenerateWithXmlComment(_writer, withChild, 8)
                _writer.WriteLine("        Friend {2} Function {0}({3} as {4}) As {1}", ChildWithFunctionName(withChild), StructureTypeName(withChild.ContainingStructure), "MustOverride", Ident(UpperFirstCharacter(withChild.Name)), ChildConstructorTypeRef(withChild))
            End If
            _writer.WriteLine("")
        End If
    End Sub
 
    ' Generate public properties for a child that is a separated list
 
    Private Sub GenerateAccept(nodeStructure As ParseNodeStructure)
        If nodeStructure.ParentStructure IsNot Nothing AndAlso (_parseTree.IsAbstract(nodeStructure) OrElse nodeStructure.IsToken OrElse nodeStructure.IsTrivia) Then
            Return
        End If
        _writer.WriteLine("        Public {0} Function Accept(ByVal visitor As {1}) As VisualBasicSyntaxNode", If(IsRoot(nodeStructure), "Overridable", "Overrides"), _parseTree.VisitorName)
        _writer.WriteLine("            Return visitor.{0}(Me)", VisitorMethodName(nodeStructure))
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
    End Sub
 
    ' Generate special methods and properties for the root node. These only appear in the root node.
    Private Sub GenerateRootNodeSpecialMethods(nodeStructure As ParseNodeStructure)
 
        _writer.WriteLine()
    End Sub
 
    ' Generate the Visitor class definition
    Private Sub GenerateVisitorClass()
        _writer.WriteLine("    Friend MustInherit Class {0}", Ident(_parseTree.VisitorName))
 
        ' Basic Visit method that dispatches.
        _writer.WriteLine("        Public Overridable Function Visit(ByVal node As {0}) As VisualBasicSyntaxNode", StructureTypeName(_parseTree.RootStructure))
        _writer.WriteLine("            If node IsNot Nothing")
        _writer.WriteLine("                Return node.Accept(Me)")
        _writer.WriteLine("            Else")
        _writer.WriteLine("                Return Nothing")
        _writer.WriteLine("            End If")
        _writer.WriteLine("        End Function")
 
        For Each nodeStructure In _parseTree.NodeStructures.Values
 
            GenerateVisitorMethod(nodeStructure)
 
        Next
 
        _writer.WriteLine("    End Class")
        _writer.WriteLine()
    End Sub
 
    ' Generate a method in the Visitor class
    Private Sub GenerateVisitorMethod(nodeStructure As ParseNodeStructure)
        If nodeStructure.IsToken OrElse nodeStructure.IsTrivia Then
            Return
        End If
        Dim methodName = VisitorMethodName(nodeStructure)
        Dim structureName = StructureTypeName(nodeStructure)
 
        _writer.WriteLine("        Public Overridable Function {0}(ByVal node As {1}) As VisualBasicSyntaxNode",
                          methodName,
                          structureName)
 
        _writer.WriteLine("            Debug.Assert(node IsNot Nothing)")
 
        If Not IsRoot(nodeStructure) Then
            _writer.WriteLine("            Return {0}(node)", VisitorMethodName(nodeStructure.ParentStructure))
        Else
            _writer.WriteLine("            Return node")
        End If
        _writer.WriteLine("        End Function")
    End Sub
 
    ' Generate the RewriteVisitor class definition
    Private Sub GenerateRewriteVisitorClass()
        _writer.WriteLine("    Friend MustInherit Class {0}", Ident(_parseTree.RewriteVisitorName))
        _writer.WriteLine("        Inherits {0}", Ident(_parseTree.VisitorName), StructureTypeName(_parseTree.RootStructure))
        _writer.WriteLine()
 
        For Each nodeStructure In _parseTree.NodeStructures.Values
            GenerateRewriteVisitorMethod(nodeStructure)
        Next
 
        _writer.WriteLine("    End Class")
        _writer.WriteLine()
    End Sub
 
    ' Generate a method in the RewriteVisitor class
    Private Sub GenerateRewriteVisitorMethod(nodeStructure As ParseNodeStructure)
        If nodeStructure.IsToken OrElse nodeStructure.IsTrivia Then
            Return
        End If
 
        If nodeStructure.Abstract Then
            ' do nothing for abstract nodes
            Return
        End If
 
        Dim methodName = VisitorMethodName(nodeStructure)
        Dim structureName = StructureTypeName(nodeStructure)
 
        _writer.WriteLine("        Public Overrides Function {0}(ByVal node As {1}) As {2}",
                  methodName,
                  structureName,
                  StructureTypeName(_parseTree.RootStructure))
 
        ' non-abstract non-terminals need to rewrite their children and recreate as needed.
        Dim allFields = GetAllFieldsOfStructure(nodeStructure)
        Dim allChildren = GetAllChildrenOfStructure(nodeStructure)
 
        ' create anyChanges variable
        _writer.WriteLine("            Dim anyChanges As Boolean = False")
        _writer.WriteLine()
 
        ' visit all children
        For i = 0 To allChildren.Count - 1
            If allChildren(i).IsList Then
                _writer.WriteLine("            Dim {0} = VisitList(node.{1})" + Environment.NewLine +
                                  "            If node.{2} IsNot {0}.Node Then anyChanges = True",
                                  ChildNewVarName(allChildren(i)), ChildPropertyName(allChildren(i)), ChildVarName(allChildren(i)))
            ElseIf KindTypeStructure(allChildren(i).ChildKind).IsToken Then
                _writer.WriteLine("            Dim {0} = DirectCast(Visit(node.{2}), {1})" + Environment.NewLine +
                                  "            If node.{3} IsNot {0} Then anyChanges = True",
                                  ChildNewVarName(allChildren(i)), BaseTypeReference(allChildren(i)), ChildPropertyName(allChildren(i)), ChildVarName(allChildren(i)))
            Else
                _writer.WriteLine("            Dim {0} = DirectCast(Visit(node.{2}), {1})" + Environment.NewLine +
                                  "            If node.{2} IsNot {0} Then anyChanges = True",
                                  ChildNewVarName(allChildren(i)), ChildPropertyTypeRef(nodeStructure, allChildren(i)), ChildVarName(allChildren(i)))
            End If
        Next
        _writer.WriteLine()
 
        ' check if any changes.
        _writer.WriteLine("            If anyChanges Then")
 
        _writer.Write("                Return New {0}(node.Kind", StructureTypeName(nodeStructure))
 
        _writer.Write(", node.GetDiagnostics, node.GetAnnotations")
 
        For Each field In allFields
            _writer.Write(", node.{0}", FieldPropertyName(field))
        Next
        For Each child In allChildren
            If child.IsList Then
                _writer.Write(", {0}.Node", ChildNewVarName(child))
            ElseIf KindTypeStructure(child.ChildKind).IsToken Then
                _writer.Write(", {0}", ChildNewVarName(child))
            Else
                _writer.Write(", {0}", ChildNewVarName(child))
            End If
        Next
        _writer.WriteLine(")")
 
        _writer.WriteLine("            Else")
        _writer.WriteLine("                Return node")
        _writer.WriteLine("            End If")
 
        _writer.WriteLine("        End Function")
        _writer.WriteLine()
    End Sub
 
End Class