// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.VB.Ast; namespace ICSharpCode.NRefactory.VB { /// /// Description of OutputVisitor. /// public class OutputVisitor : IAstVisitor, IPatternAstVisitor { readonly IOutputFormatter formatter; readonly VBFormattingOptions policy; readonly Stack containerStack = new Stack(); readonly Stack positionStack = new Stack(); /// /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written. /// LastWritten lastWritten; enum LastWritten { Whitespace, Other, KeywordOrIdentifier } public OutputVisitor(TextWriter textWriter, VBFormattingOptions formattingPolicy) { if (textWriter == null) throw new ArgumentNullException("textWriter"); if (formattingPolicy == null) throw new ArgumentNullException("formattingPolicy"); this.formatter = new TextWriterOutputFormatter(textWriter); this.policy = formattingPolicy; } public OutputVisitor(IOutputFormatter formatter, VBFormattingOptions formattingPolicy) { if (formatter == null) throw new ArgumentNullException("formatter"); if (formattingPolicy == null) throw new ArgumentNullException("formattingPolicy"); this.formatter = formatter; this.policy = formattingPolicy; } public object VisitCompilationUnit(CompilationUnit compilationUnit, object data) { // don't do node tracking as we visit all children directly foreach (AstNode node in compilationUnit.Children) node.AcceptVisitor(this, data); return null; } public object VisitBlockStatement(BlockStatement blockStatement, object data) { StartNode(blockStatement); foreach (var stmt in blockStatement) { stmt.AcceptVisitor(this, data); NewLine(); } return EndNode(blockStatement); } public object VisitPatternPlaceholder(AstNode placeholder, Pattern pattern, object data) { throw new NotImplementedException(); } public object VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, object data) { StartNode(typeParameterDeclaration); switch (typeParameterDeclaration.Variance) { case ICSharpCode.NRefactory.TypeSystem.VarianceModifier.Invariant: break; case ICSharpCode.NRefactory.TypeSystem.VarianceModifier.Covariant: WriteKeyword("Out"); break; case ICSharpCode.NRefactory.TypeSystem.VarianceModifier.Contravariant: WriteKeyword("In"); break; default: throw new Exception("Invalid value for VarianceModifier"); } WriteIdentifier(typeParameterDeclaration.Name); if (typeParameterDeclaration.Constraints.Any()) { WriteKeyword("As"); if (typeParameterDeclaration.Constraints.Count > 1) WriteToken("{", TypeParameterDeclaration.Roles.LBrace); WriteCommaSeparatedList(typeParameterDeclaration.Constraints); if (typeParameterDeclaration.Constraints.Count > 1) WriteToken("}", TypeParameterDeclaration.Roles.RBrace); } return EndNode(typeParameterDeclaration); } public object VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, object data) { StartNode(parameterDeclaration); WriteAttributes(parameterDeclaration.Attributes); WriteModifiers(parameterDeclaration.ModifierTokens); WriteIdentifier(parameterDeclaration.Name.Name); if (!parameterDeclaration.Type.IsNull) { WriteKeyword("As"); parameterDeclaration.Type.AcceptVisitor(this, data); } if (!parameterDeclaration.OptionalValue.IsNull) { WriteToken("=", ParameterDeclaration.Roles.Assign); parameterDeclaration.OptionalValue.AcceptVisitor(this, data); } return EndNode(parameterDeclaration); } public object VisitVBTokenNode(VBTokenNode vBTokenNode, object data) { var mod = vBTokenNode as VBModifierToken; if (mod != null) { StartNode(vBTokenNode); WriteKeyword(VBModifierToken.GetModifierName(mod.Modifier)); return EndNode(vBTokenNode); } else { throw new NotSupportedException("Should never visit individual tokens"); } } public object VisitAliasImportsClause(AliasImportsClause aliasImportsClause, object data) { throw new NotImplementedException(); } public object VisitAttribute(ICSharpCode.NRefactory.VB.Ast.Attribute attribute, object data) { StartNode(attribute); if (!attribute.Target.IsNull) { attribute.Target.AcceptVisitor(this, data); WriteToken(":", VB.Ast.Attribute.TargetRole); } attribute.Type.AcceptVisitor(this, data); WriteCommaSeparatedListInParenthesis(attribute.Arguments, false); return EndNode(attribute); } public object VisitAttributeBlock(AttributeBlock attributeBlock, object data) { StartNode(attributeBlock); WriteToken("<", AttributeBlock.Roles.LChevron); WriteCommaSeparatedList(attributeBlock.Attributes); WriteToken(">", AttributeBlock.Roles.RChevron); if (attributeBlock.Parent is ParameterDeclaration) Space(); else NewLine(); return EndNode(attributeBlock); } public object VisitImportsStatement(ImportsStatement importsStatement, object data) { StartNode(importsStatement); WriteKeyword("Imports", AstNode.Roles.Keyword); Space(); WriteCommaSeparatedList(importsStatement.ImportsClauses); NewLine(); return EndNode(importsStatement); } public object VisitMemberImportsClause(MemberImportsClause memberImportsClause, object data) { StartNode(memberImportsClause); memberImportsClause.Member.AcceptVisitor(this, data); return EndNode(memberImportsClause); } public object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) { StartNode(namespaceDeclaration); NewLine(); WriteKeyword("Namespace"); bool isFirst = true; foreach (Identifier node in namespaceDeclaration.Identifiers) { if (isFirst) { isFirst = false; } else { WriteToken(".", NamespaceDeclaration.Roles.Dot); } node.AcceptVisitor(this, null); } NewLine(); WriteMembers(namespaceDeclaration.Members); WriteKeyword("End"); WriteKeyword("Namespace"); NewLine(); return EndNode(namespaceDeclaration); } public object VisitOptionStatement(OptionStatement optionStatement, object data) { throw new NotImplementedException(); } public object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) { StartNode(typeDeclaration); WriteAttributes(typeDeclaration.Attributes); WriteModifiers(typeDeclaration.ModifierTokens); WriteClassTypeKeyword(typeDeclaration); WriteIdentifier(typeDeclaration.Name.Name); if (!typeDeclaration.InheritsType.IsNull) { Space(); WriteKeyword("Inherits"); typeDeclaration.InheritsType.AcceptVisitor(this, data); } WriteImplementsClause(typeDeclaration.ImplementsTypes); NewLine(); WriteMembers(typeDeclaration.Members); WriteKeyword("End"); WriteClassTypeKeyword(typeDeclaration); NewLine(); return EndNode(typeDeclaration); } void WriteClassTypeKeyword(TypeDeclaration typeDeclaration) { switch (typeDeclaration.ClassType) { case ICSharpCode.NRefactory.TypeSystem.ClassType.Class: WriteKeyword("Class"); break; case ICSharpCode.NRefactory.TypeSystem.ClassType.Enum: break; case ICSharpCode.NRefactory.TypeSystem.ClassType.Interface: WriteKeyword("Interface"); break; case ICSharpCode.NRefactory.TypeSystem.ClassType.Struct: WriteKeyword("Structure"); break; case ICSharpCode.NRefactory.TypeSystem.ClassType.Delegate: break; case ICSharpCode.NRefactory.TypeSystem.ClassType.Module: WriteKeyword("Module"); break; default: throw new Exception("Invalid value for ClassType"); } } public object VisitXmlNamespaceImportsClause(XmlNamespaceImportsClause xmlNamespaceImportsClause, object data) { throw new NotImplementedException(); } public object VisitEnumDeclaration(EnumDeclaration enumDeclaration, object data) { StartNode(enumDeclaration); WriteAttributes(enumDeclaration.Attributes); WriteModifiers(enumDeclaration.ModifierTokens); WriteKeyword("Enum"); WriteIdentifier(enumDeclaration.Name.Name); if (!enumDeclaration.UnderlyingType.IsNull) { Space(); WriteKeyword("As"); enumDeclaration.UnderlyingType.AcceptVisitor(this, data); } NewLine(); Indent(); foreach (var member in enumDeclaration.Members) { member.AcceptVisitor(this, null); } Unindent(); WriteKeyword("End"); WriteKeyword("Enum"); NewLine(); return EndNode(enumDeclaration); } public object VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, object data) { StartNode(enumMemberDeclaration); WriteAttributes(enumMemberDeclaration.Attributes); WriteIdentifier(enumMemberDeclaration.Name.Name); if (!enumMemberDeclaration.Value.IsNull) { Space(); WriteToken("=", EnumMemberDeclaration.Roles.Assign); Space(); enumMemberDeclaration.Value.AcceptVisitor(this, data); } NewLine(); return EndNode(enumMemberDeclaration); } public object VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, object data) { StartNode(delegateDeclaration); WriteAttributes(delegateDeclaration.Attributes); WriteModifiers(delegateDeclaration.ModifierTokens); WriteKeyword("Delegate"); if (delegateDeclaration.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); WriteIdentifier(delegateDeclaration.Name.Name); WriteTypeParameters(delegateDeclaration.TypeParameters); WriteCommaSeparatedListInParenthesis(delegateDeclaration.Parameters, false); if (!delegateDeclaration.IsSub) { Space(); WriteKeyword("As"); WriteAttributes(delegateDeclaration.ReturnTypeAttributes); delegateDeclaration.ReturnType.AcceptVisitor(this, data); } NewLine(); return EndNode(delegateDeclaration); } public object VisitIdentifier(Identifier identifier, object data) { StartNode(identifier); WriteIdentifier(identifier.Name); WriteTypeCharacter(identifier.TypeCharacter); return EndNode(identifier); } public object VisitXmlIdentifier(XmlIdentifier xmlIdentifier, object data) { throw new NotImplementedException(); } public object VisitXmlLiteralString(XmlLiteralString xmlLiteralString, object data) { throw new NotImplementedException(); } public object VisitSimpleNameExpression(SimpleNameExpression simpleNameExpression, object data) { StartNode(simpleNameExpression); simpleNameExpression.Identifier.AcceptVisitor(this, data); WriteTypeArguments(simpleNameExpression.TypeArguments); return EndNode(simpleNameExpression); } public object VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, object data) { StartNode(primitiveExpression); WritePrimitiveValue(primitiveExpression.Value); return EndNode(primitiveExpression); } public object VisitInstanceExpression(InstanceExpression instanceExpression, object data) { StartNode(instanceExpression); switch (instanceExpression.Type) { case InstanceExpressionType.Me: WriteKeyword("Me"); break; case InstanceExpressionType.MyBase: WriteKeyword("MyBase"); break; case InstanceExpressionType.MyClass: WriteKeyword("MyClass"); break; default: throw new Exception("Invalid value for InstanceExpressionType"); } return EndNode(instanceExpression); } public object VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, object data) { StartNode(parenthesizedExpression); LPar(); parenthesizedExpression.Expression.AcceptVisitor(this, data); RPar(); return EndNode(parenthesizedExpression); } public object VisitGetTypeExpression(GetTypeExpression getTypeExpression, object data) { StartNode(getTypeExpression); WriteKeyword("GetType"); LPar(); getTypeExpression.Type.AcceptVisitor(this, data); RPar(); return EndNode(getTypeExpression); } public object VisitTypeOfIsExpression(TypeOfIsExpression typeOfIsExpression, object data) { StartNode(typeOfIsExpression); WriteKeyword("TypeOf"); typeOfIsExpression.TypeOfExpression.AcceptVisitor(this, data); WriteKeyword("Is"); typeOfIsExpression.Type.AcceptVisitor(this, data); return EndNode(typeOfIsExpression); } public object VisitGetXmlNamespaceExpression(GetXmlNamespaceExpression getXmlNamespaceExpression, object data) { throw new NotImplementedException(); } public object VisitMemberAccessExpression(MemberAccessExpression memberAccessExpression, object data) { StartNode(memberAccessExpression); memberAccessExpression.Target.AcceptVisitor(this, data); WriteToken(".", MemberAccessExpression.Roles.Dot); memberAccessExpression.MemberName.AcceptVisitor(this, data); WriteTypeArguments(memberAccessExpression.TypeArguments); return EndNode(memberAccessExpression); } public object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) { StartNode(typeReferenceExpression); typeReferenceExpression.Type.AcceptVisitor(this, data); return EndNode(typeReferenceExpression); } public object VisitEventMemberSpecifier(EventMemberSpecifier eventMemberSpecifier, object data) { StartNode(eventMemberSpecifier); eventMemberSpecifier.Target.AcceptVisitor(this, data); WriteToken(".", EventMemberSpecifier.Roles.Dot); eventMemberSpecifier.Member.AcceptVisitor(this, data); return EndNode(eventMemberSpecifier); } public object VisitInterfaceMemberSpecifier(InterfaceMemberSpecifier interfaceMemberSpecifier, object data) { StartNode(interfaceMemberSpecifier); interfaceMemberSpecifier.Target.AcceptVisitor(this, data); WriteToken(".", EventMemberSpecifier.Roles.Dot); interfaceMemberSpecifier.Member.AcceptVisitor(this, data); return EndNode(interfaceMemberSpecifier); } #region TypeMembers public object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) { StartNode(constructorDeclaration); WriteAttributes(constructorDeclaration.Attributes); WriteModifiers(constructorDeclaration.ModifierTokens); WriteKeyword("Sub"); WriteKeyword("New"); WriteCommaSeparatedListInParenthesis(constructorDeclaration.Parameters, false); NewLine(); Indent(); WriteBlock(constructorDeclaration.Body); Unindent(); WriteKeyword("End"); WriteKeyword("Sub"); NewLine(); return EndNode(constructorDeclaration); } public object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) { StartNode(methodDeclaration); WriteAttributes(methodDeclaration.Attributes); WriteModifiers(methodDeclaration.ModifierTokens); if (methodDeclaration.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); methodDeclaration.Name.AcceptVisitor(this, data); WriteTypeParameters(methodDeclaration.TypeParameters); WriteCommaSeparatedListInParenthesis(methodDeclaration.Parameters, false); if (!methodDeclaration.IsSub && !methodDeclaration.ReturnType.IsNull) { Space(); WriteKeyword("As"); WriteAttributes(methodDeclaration.ReturnTypeAttributes); methodDeclaration.ReturnType.AcceptVisitor(this, data); } WriteHandlesClause(methodDeclaration.HandlesClause); WriteImplementsClause(methodDeclaration.ImplementsClause); if (!methodDeclaration.Body.IsNull) { NewLine(); Indent(); WriteBlock(methodDeclaration.Body); Unindent(); WriteKeyword("End"); if (methodDeclaration.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); } NewLine(); return EndNode(methodDeclaration); } #endregion #region TypeName public object VisitPrimitiveType(PrimitiveType primitiveType, object data) { StartNode(primitiveType); WriteKeyword(primitiveType.Keyword); return EndNode(primitiveType); } public object VisitQualifiedType(QualifiedType qualifiedType, object data) { StartNode(qualifiedType); qualifiedType.Target.AcceptVisitor(this, data); WriteToken(".", AstNode.Roles.Dot); WriteIdentifier(qualifiedType.Name); WriteTypeArguments(qualifiedType.TypeArguments); return EndNode(qualifiedType); } public object VisitComposedType(ComposedType composedType, object data) { StartNode(composedType); composedType.BaseType.AcceptVisitor(this, data); if (composedType.HasNullableSpecifier) WriteToken("?", ComposedType.Roles.QuestionMark); WriteArraySpecifiers(composedType.ArraySpecifiers); return EndNode(composedType); } public object VisitArraySpecifier(ArraySpecifier arraySpecifier, object data) { StartNode(arraySpecifier); LPar(); for (int i = 0; i < arraySpecifier.Dimensions - 1; i++) { WriteToken(",", ArraySpecifier.Roles.Comma); } RPar(); return EndNode(arraySpecifier); } public object VisitSimpleType(SimpleType simpleType, object data) { StartNode(simpleType); WriteIdentifier(simpleType.Identifier); WriteTypeArguments(simpleType.TypeArguments); return EndNode(simpleType); } #endregion #region Pattern Matching public object VisitAnyNode(AnyNode anyNode, object data) { throw new NotImplementedException(); } public object VisitBackreference(Backreference backreference, object data) { throw new NotImplementedException(); } public object VisitChoice(Choice choice, object data) { throw new NotImplementedException(); } public object VisitNamedNode(NamedNode namedNode, object data) { throw new NotImplementedException(); } public object VisitRepeat(Repeat repeat, object data) { throw new NotImplementedException(); } public object VisitOptionalNode(OptionalNode optionalNode, object data) { throw new NotImplementedException(); } public object VisitIdentifierExpressionBackreference(IdentifierExpressionBackreference identifierExpressionBackreference, object data) { throw new NotImplementedException(); } #endregion #region StartNode/EndNode void StartNode(AstNode node) { // Ensure that nodes are visited in the proper nested order. // Jumps to different subtrees are allowed only for the child of a placeholder node. Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek()); if (positionStack.Count > 0) WriteSpecialsUpToNode(node); containerStack.Push(node); positionStack.Push(node.FirstChild); formatter.StartNode(node); } object EndNode(AstNode node) { Debug.Assert(node == containerStack.Peek()); AstNode pos = positionStack.Pop(); Debug.Assert(pos == null || pos.Parent == node); WriteSpecials(pos, null); containerStack.Pop(); formatter.EndNode(node); return null; } #endregion #region WriteSpecials /// /// Writes all specials from start to end (exclusive). Does not touch the positionStack. /// void WriteSpecials(AstNode start, AstNode end) { for (AstNode pos = start; pos != end; pos = pos.NextSibling) { if (pos.Role == AstNode.Roles.Comment) { pos.AcceptVisitor(this, null); } } } /// /// Writes all specials between the current position (in the positionStack) and the next /// node with the specified role. Advances the current position. /// void WriteSpecialsUpToRole(Role role) { for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { if (pos.Role == role) { WriteSpecials(positionStack.Pop(), pos); positionStack.Push(pos); break; } } } /// /// Writes all specials between the current position (in the positionStack) and the specified node. /// Advances the current position. /// void WriteSpecialsUpToNode(AstNode node) { for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) { if (pos == node) { WriteSpecials(positionStack.Pop(), pos); positionStack.Push(pos); break; } } } void WriteSpecialsUpToRole(Role role, AstNode nextNode) { // Look for the role between the current position and the nextNode. for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) { if (pos.Role == AstNode.Roles.Comma) { WriteSpecials(positionStack.Pop(), pos); positionStack.Push(pos); break; } } } #endregion #region Comma /// /// Writes a comma. /// /// The next node after the comma. /// When set prevents printing a space after comma. void Comma(AstNode nextNode, bool noSpaceAfterComma = false) { WriteSpecialsUpToRole(AstNode.Roles.Comma, nextNode); formatter.WriteToken(","); lastWritten = LastWritten.Other; Space(!noSpaceAfterComma); // TODO: Comma policy has changed. } void WriteCommaSeparatedList(IEnumerable list) { bool isFirst = true; foreach (AstNode node in list) { if (isFirst) { isFirst = false; } else { Comma(node); } node.AcceptVisitor(this, null); } } void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { LPar(); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } RPar(); } #if DOTNET35 void WriteCommaSeparatedList(IEnumerable list) { WriteCommaSeparatedList(list); } void WriteCommaSeparatedList(IEnumerable list) { WriteCommaSeparatedList(list); } void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); } void WriteCommaSeparatedListInParenthesis(IEnumerable list, bool spaceWithin) { WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); } #endif void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { WriteToken("[", AstNode.Roles.LBracket); if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list); Space(spaceWithin); } WriteToken("]", AstNode.Roles.RBracket); } void WriteCommaSeparatedListInBrackets(IEnumerable list) { WriteToken ("[", AstNode.Roles.LBracket); if (list.Any ()) { Space(); WriteCommaSeparatedList(list); Space(); } WriteToken ("]", AstNode.Roles.RBracket); } #endregion #region Write tokens /// /// Writes a keyword, and all specials up to /// void WriteKeyword(string keyword, Role tokenRole = null) { WriteSpecialsUpToRole(tokenRole ?? AstNode.Roles.Keyword); if (lastWritten == LastWritten.KeywordOrIdentifier) formatter.Space(); formatter.WriteKeyword(keyword); lastWritten = LastWritten.KeywordOrIdentifier; } void WriteIdentifier(string identifier, Role identifierRole = null) { WriteSpecialsUpToRole(identifierRole ?? AstNode.Roles.Identifier); if (IsKeyword(identifier, containerStack.Peek())) { if (lastWritten == LastWritten.KeywordOrIdentifier) Space(); // this space is not strictly required, so we call Space() formatter.WriteToken("["); } else if (lastWritten == LastWritten.KeywordOrIdentifier) { formatter.Space(); // this space is strictly required, so we directly call the formatter } formatter.WriteIdentifier(identifier); if (IsKeyword(identifier, containerStack.Peek())) { formatter.WriteToken("]"); } lastWritten = LastWritten.KeywordOrIdentifier; } void WriteToken(string token, Role tokenRole) { WriteSpecialsUpToRole(tokenRole); // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token. // Note that we don't need to handle tokens like = because there's no valid // C# program that contains the single token twice in a row. // (for +, - and &, this can happen with unary operators; // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0"; // and for /, this can happen with "1/ *ptr" or "1/ //comment".) // if (lastWritten == LastWritten.Plus && token[0] == '+' // || lastWritten == LastWritten.Minus && token[0] == '-' // || lastWritten == LastWritten.Ampersand && token[0] == '&' // || lastWritten == LastWritten.QuestionMark && token[0] == '?' // || lastWritten == LastWritten.Division && token[0] == '*') // { // formatter.Space(); // } formatter.WriteToken(token); // if (token == "+") // lastWritten = LastWritten.Plus; // else if (token == "-") // lastWritten = LastWritten.Minus; // else if (token == "&") // lastWritten = LastWritten.Ampersand; // else if (token == "?") // lastWritten = LastWritten.QuestionMark; // else if (token == "/") // lastWritten = LastWritten.Division; // else lastWritten = LastWritten.Other; } void WriteTypeCharacter(TypeCode typeCharacter) { switch (typeCharacter) { case TypeCode.Empty: case TypeCode.Object: case TypeCode.DBNull: case TypeCode.Boolean: case TypeCode.Char: break; case TypeCode.SByte: break; case TypeCode.Byte: break; case TypeCode.Int16: break; case TypeCode.UInt16: break; case TypeCode.Int32: WriteToken("%", null); break; case TypeCode.UInt32: break; case TypeCode.Int64: WriteToken("&", null); break; case TypeCode.UInt64: break; case TypeCode.Single: WriteToken("!", null); break; case TypeCode.Double: WriteToken("#", null); break; case TypeCode.Decimal: WriteToken("@", null); break; case TypeCode.DateTime: break; case TypeCode.String: WriteToken("$", null); break; default: throw new Exception("Invalid value for TypeCode"); } } void LPar() { WriteToken("(", AstNode.Roles.LPar); } void RPar() { WriteToken(")", AstNode.Roles.LPar); } /// /// Writes a space depending on policy. /// void Space(bool addSpace = true) { if (addSpace) { formatter.Space(); lastWritten = LastWritten.Whitespace; } } void NewLine() { formatter.NewLine(); lastWritten = LastWritten.Whitespace; } void Indent() { formatter.Indent(); } void Unindent() { formatter.Unindent(); } #endregion #region IsKeyword Test static readonly HashSet unconditionalKeywords = new HashSet { "AddHandler", "AddressOf", "Alias", "And", "AndAlso", "As", "Boolean", "ByRef", "Byte", "ByVal", "Call", "Case", "Catch", "CBool", "CByte", "CChar", "CInt", "Class", "CLng", "CObj", "Const", "Continue", "CSByte", "CShort", "CSng", "CStr", "CType", "CUInt", "CULng", "CUShort", "Date", "Decimal", "Declare", "Default", "Delegate", "Dim", "DirectCast", "Do", "Double", "Each", "Else", "ElseIf", "End", "EndIf", "Enum", "Erase", "Error", "Event", "Exit", "False", "Finally", "For", "Friend", "Function", "Get", "GetType", "GetXmlNamespace", "Global", "GoSub", "GoTo", "Handles", "If", "Implements", "Imports", "In", "Inherits", "Integer", "Interface", "Is", "IsNot", "Let", "Lib", "Like", "Long", "Loop", "Me", "Mod", "Module", "MustInherit", "MustOverride", "MyBase", "MyClass", "Namespace", "Narrowing", "New", "Next", "Not", "Nothing", "NotInheritable", "NotOverridable", "Object", "Of", "On", "Operator", "Option", "Optional", "Or", "OrElse", "Overloads", "Overridable", "Overrides", "ParamArray", "Partial", "Private", "Property", "Protected", "Public", "RaiseEvent", "ReadOnly", "ReDim", "REM", "RemoveHandler", "Resume", "Return", "SByte", "Select", "Set", "Shadows", "Shared", "Short", "Single", "Static", "Step", "Stop", "String", "Structure", "Sub", "SyncLock", "Then", "Throw", "To", "True", "Try", "TryCast", "TypeOf", "UInteger", "ULong", "UShort", "Using", "Variant", "Wend", "When", "While", "Widening", "With", "WithEvents", "WriteOnly", "Xor" }; static readonly HashSet queryKeywords = new HashSet { }; /// /// Determines whether the specified identifier is a keyword in the given context. /// public static bool IsKeyword(string identifier, AstNode context) { if (unconditionalKeywords.Contains(identifier)) return true; // if (context.Ancestors.Any(a => a is QueryExpression)) { // if (queryKeywords.Contains(identifier)) // return true; // } return false; } #endregion #region Write constructs void WriteTypeArguments(IEnumerable typeArguments) { if (typeArguments.Any()) { LPar(); WriteKeyword("Of"); WriteCommaSeparatedList(typeArguments); RPar(); } } void WriteTypeParameters(IEnumerable typeParameters) { if (typeParameters.Any()) { LPar(); WriteKeyword("Of"); WriteCommaSeparatedList(typeParameters); RPar(); } } void WriteModifiers(IEnumerable modifierTokens) { foreach (VBModifierToken modifier in modifierTokens) { modifier.AcceptVisitor(this, null); } } void WriteArraySpecifiers(IEnumerable arraySpecifiers) { foreach (ArraySpecifier specifier in arraySpecifiers) { specifier.AcceptVisitor(this, null); } } void WriteQualifiedIdentifier(IEnumerable identifiers) { bool first = true; foreach (Identifier ident in identifiers) { if (first) { first = false; if (lastWritten == LastWritten.KeywordOrIdentifier) formatter.Space(); } else { WriteSpecialsUpToRole(AstNode.Roles.Dot, ident); formatter.WriteToken("."); lastWritten = LastWritten.Other; } WriteSpecialsUpToNode(ident); formatter.WriteIdentifier(ident.Name); lastWritten = LastWritten.KeywordOrIdentifier; } } void WriteEmbeddedStatement(Statement embeddedStatement) { if (embeddedStatement.IsNull) return; BlockStatement block = embeddedStatement as BlockStatement; if (block != null) VisitBlockStatement(block, null); else embeddedStatement.AcceptVisitor(this, null); } void WriteBlock(BlockStatement body) { if (body.IsNull) NewLine(); else VisitBlockStatement(body, null); } void WriteMembers(IEnumerable members) { Indent(); bool isFirst = true; foreach (var member in members) { if (isFirst) { isFirst = false; } else { NewLine(); } member.AcceptVisitor(this, null); } Unindent(); } void WriteAttributes(IEnumerable attributes) { foreach (AttributeBlock attr in attributes) { attr.AcceptVisitor(this, null); } } void WritePrivateImplementationType(AstType privateImplementationType) { if (!privateImplementationType.IsNull) { privateImplementationType.AcceptVisitor(this, null); WriteToken(".", AstNode.Roles.Dot); } } void WriteImplementsClause(AstNodeCollection implementsClause) { if (implementsClause.Any()) { Space(); WriteKeyword("Implements"); WriteCommaSeparatedList(implementsClause); } } void WriteImplementsClause(AstNodeCollection implementsClause) { if (implementsClause.Any()) { Space(); WriteKeyword("Implements"); WriteCommaSeparatedList(implementsClause); } } void WriteHandlesClause(AstNodeCollection handlesClause) { if (handlesClause.Any()) { Space(); WriteKeyword("Handles"); WriteCommaSeparatedList(handlesClause); } } void WritePrimitiveValue(object val) { if (val == null) { WriteKeyword("Nothing"); return; } if (val is bool) { if ((bool)val) { WriteKeyword("True"); } else { WriteKeyword("False"); } return; } if (val is string) { formatter.WriteToken("\"" + ConvertString(val.ToString()) + "\""); lastWritten = LastWritten.Other; } else if (val is char) { formatter.WriteToken("\"" + ConvertCharLiteral((char)val) + "\"c"); lastWritten = LastWritten.Other; } else if (val is decimal) { formatter.WriteToken(((decimal)val).ToString(NumberFormatInfo.InvariantInfo) + "D"); lastWritten = LastWritten.Other; } else if (val is float) { float f = (float)val; if (float.IsInfinity(f) || float.IsNaN(f)) { // Strictly speaking, these aren't PrimitiveExpressions; // but we still support writing these to make life easier for code generators. WriteKeyword("Single"); WriteToken(".", AstNode.Roles.Dot); if (float.IsPositiveInfinity(f)) WriteIdentifier("PositiveInfinity"); else if (float.IsNegativeInfinity(f)) WriteIdentifier("NegativeInfinity"); else WriteIdentifier("NaN"); return; } formatter.WriteToken(f.ToString("R", NumberFormatInfo.InvariantInfo) + "F"); lastWritten = LastWritten.Other; } else if (val is double) { double f = (double)val; if (double.IsInfinity(f) || double.IsNaN(f)) { // Strictly speaking, these aren't PrimitiveExpressions; // but we still support writing these to make life easier for code generators. WriteKeyword("Double"); WriteToken(".", AstNode.Roles.Dot); if (double.IsPositiveInfinity(f)) WriteIdentifier("PositiveInfinity"); else if (double.IsNegativeInfinity(f)) WriteIdentifier("NegativeInfinity"); else WriteIdentifier("NaN"); return; } string number = f.ToString("R", NumberFormatInfo.InvariantInfo); if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) number += ".0"; formatter.WriteToken(number); // needs space if identifier follows number; this avoids mistaking the following identifier as type suffix lastWritten = LastWritten.KeywordOrIdentifier; } else if (val is IFormattable) { StringBuilder b = new StringBuilder(); // if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) { // b.Append("0x"); // b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo)); // } else { b.Append(((IFormattable)val).ToString(null, NumberFormatInfo.InvariantInfo)); // } if (val is uint || val is ulong) { b.Append("U"); } if (val is long || val is ulong) { b.Append("L"); } formatter.WriteToken(b.ToString()); // needs space if identifier follows number; this avoids mistaking the following identifier as type suffix lastWritten = LastWritten.KeywordOrIdentifier; } else { formatter.WriteToken(val.ToString()); lastWritten = LastWritten.Other; } } #endregion #region ConvertLiteral static string ConvertCharLiteral(char ch) { if (ch == '"') return "\"\""; return ch.ToString(); } static string ConvertString(string str) { StringBuilder sb = new StringBuilder(); foreach (char ch in str) { sb.Append(ConvertCharLiteral(ch)); } return sb.ToString(); } #endregion public object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) { StartNode(fieldDeclaration); WriteAttributes(fieldDeclaration.Attributes); WriteModifiers(fieldDeclaration.ModifierTokens); WriteCommaSeparatedList(fieldDeclaration.Variables); NewLine(); return EndNode(fieldDeclaration); } public object VisitVariableIdentifier(VariableIdentifier variableIdentifier, object data) { StartNode(variableIdentifier); WriteIdentifier(variableIdentifier.Name.Name); if (variableIdentifier.HasNullableSpecifier) WriteToken("?", VariableIdentifier.Roles.QuestionMark); WriteArraySpecifiers(variableIdentifier.ArraySpecifiers); return EndNode(variableIdentifier); } public object VisitAccessor(Accessor accessor, object data) { StartNode(accessor); WriteAttributes(accessor.Attributes); WriteModifiers(accessor.ModifierTokens); if (accessor.Role == PropertyDeclaration.GetterRole) { WriteKeyword("Get"); } else if (accessor.Role == PropertyDeclaration.SetterRole) { WriteKeyword("Set"); } else if (accessor.Role == EventDeclaration.AddHandlerRole) { WriteKeyword("AddHandler"); } else if (accessor.Role == EventDeclaration.RemoveHandlerRole) { WriteKeyword("RemoveHandler"); } else if (accessor.Role == EventDeclaration.RaiseEventRole) { WriteKeyword("RaiseEvent"); } if (accessor.Parameters.Any()) WriteCommaSeparatedListInParenthesis(accessor.Parameters, false); NewLine(); Indent(); WriteBlock(accessor.Body); Unindent(); WriteKeyword("End"); if (accessor.Role == PropertyDeclaration.GetterRole) { WriteKeyword("Get"); } else if (accessor.Role == PropertyDeclaration.SetterRole) { WriteKeyword("Set"); } else if (accessor.Role == EventDeclaration.AddHandlerRole) { WriteKeyword("AddHandler"); } else if (accessor.Role == EventDeclaration.RemoveHandlerRole) { WriteKeyword("RemoveHandler"); } else if (accessor.Role == EventDeclaration.RaiseEventRole) { WriteKeyword("RaiseEvent"); } NewLine(); return EndNode(accessor); } public object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data) { StartNode(propertyDeclaration); WriteAttributes(propertyDeclaration.Attributes); WriteModifiers(propertyDeclaration.ModifierTokens); WriteKeyword("Property"); WriteIdentifier(propertyDeclaration.Name.Name); WriteCommaSeparatedListInParenthesis(propertyDeclaration.Parameters, false); if (!propertyDeclaration.ReturnType.IsNull) { Space(); WriteKeyword("As"); WriteAttributes(propertyDeclaration.ReturnTypeAttributes); propertyDeclaration.ReturnType.AcceptVisitor(this, data); } bool needsBody = !propertyDeclaration.Getter.Body.IsNull || !propertyDeclaration.Setter.Body.IsNull; if (needsBody) { NewLine(); Indent(); if (!propertyDeclaration.Getter.Body.IsNull) { propertyDeclaration.Getter.AcceptVisitor(this, data); } if (!propertyDeclaration.Setter.Body.IsNull) { propertyDeclaration.Setter.AcceptVisitor(this, data); } Unindent(); WriteKeyword("End"); WriteKeyword("Property"); } NewLine(); return EndNode(propertyDeclaration); } public object VisitLabelDeclarationStatement(LabelDeclarationStatement labelDeclarationStatement, object data) { StartNode(labelDeclarationStatement); labelDeclarationStatement.Label.AcceptVisitor(this, data); WriteToken(":", LabelDeclarationStatement.Roles.Colon); return EndNode(labelDeclarationStatement); } public object VisitLocalDeclarationStatement(LocalDeclarationStatement localDeclarationStatement, object data) { StartNode(localDeclarationStatement); if (localDeclarationStatement.ModifierToken != null && !localDeclarationStatement.ModifierToken.IsNull) WriteModifiers(new [] { localDeclarationStatement.ModifierToken }); WriteCommaSeparatedList(localDeclarationStatement.Variables); return EndNode(localDeclarationStatement); } public object VisitWithStatement(WithStatement withStatement, object data) { StartNode(withStatement); WriteKeyword("With"); withStatement.Expression.AcceptVisitor(this, data); NewLine(); Indent(); withStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); WriteKeyword("With"); return EndNode(withStatement); } public object VisitSyncLockStatement(SyncLockStatement syncLockStatement, object data) { StartNode(syncLockStatement); WriteKeyword("SyncLock"); syncLockStatement.Expression.AcceptVisitor(this, data); NewLine(); Indent(); syncLockStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); WriteKeyword("SyncLock"); return EndNode(syncLockStatement); } public object VisitTryStatement(TryStatement tryStatement, object data) { StartNode(tryStatement); WriteKeyword("Try"); NewLine(); Indent(); tryStatement.Body.AcceptVisitor(this, data); Unindent(); foreach (var clause in tryStatement.CatchBlocks) { clause.AcceptVisitor(this, data); } if (!tryStatement.FinallyBlock.IsNull) { WriteKeyword("Finally"); NewLine(); Indent(); tryStatement.FinallyBlock.AcceptVisitor(this, data); Unindent(); } WriteKeyword("End"); WriteKeyword("Try"); return EndNode(tryStatement); } public object VisitCatchBlock(CatchBlock catchBlock, object data) { StartNode(catchBlock); WriteKeyword("Catch"); catchBlock.ExceptionVariable.AcceptVisitor(this, data); if (!catchBlock.ExceptionType.IsNull) { WriteKeyword("As"); catchBlock.ExceptionType.AcceptVisitor(this, data); } NewLine(); Indent(); foreach (var stmt in catchBlock) { stmt.AcceptVisitor(this, data); NewLine(); } Unindent(); return EndNode(catchBlock); } public object VisitExpressionStatement(ExpressionStatement expressionStatement, object data) { StartNode(expressionStatement); expressionStatement.Expression.AcceptVisitor(this, data); return EndNode(expressionStatement); } public object VisitThrowStatement(ThrowStatement throwStatement, object data) { StartNode(throwStatement); WriteKeyword("Throw"); throwStatement.Expression.AcceptVisitor(this, data); return EndNode(throwStatement); } public object VisitIfElseStatement(IfElseStatement ifElseStatement, object data) { StartNode(ifElseStatement); WriteKeyword("If"); ifElseStatement.Condition.AcceptVisitor(this, data); Space(); WriteKeyword("Then"); NewLine(); Indent(); ifElseStatement.Body.AcceptVisitor(this, data); Unindent(); if (!ifElseStatement.ElseBlock.IsNull) { WriteKeyword("Else"); NewLine(); Indent(); ifElseStatement.ElseBlock.AcceptVisitor(this, data); Unindent(); } WriteKeyword("End"); WriteKeyword("If"); return EndNode(ifElseStatement); } public object VisitReturnStatement(ReturnStatement returnStatement, object data) { StartNode(returnStatement); WriteKeyword("Return"); returnStatement.Expression.AcceptVisitor(this, data); return EndNode(returnStatement); } public object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) { StartNode(binaryOperatorExpression); binaryOperatorExpression.Left.AcceptVisitor(this, data); Space(); switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.BitwiseAnd: WriteKeyword("And"); break; case BinaryOperatorType.BitwiseOr: WriteKeyword("Or"); break; case BinaryOperatorType.LogicalAnd: WriteKeyword("AndAlso"); break; case BinaryOperatorType.LogicalOr: WriteKeyword("OrElse"); break; case BinaryOperatorType.ExclusiveOr: WriteKeyword("Xor"); break; case BinaryOperatorType.GreaterThan: WriteToken(">", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.GreaterThanOrEqual: WriteToken(">=", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Equality: WriteToken("=", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.InEquality: WriteToken("<>", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.LessThan: WriteToken("<", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.LessThanOrEqual: WriteToken("<=", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Add: WriteToken("+", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Subtract: WriteToken("-", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Multiply: WriteToken("*", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Divide: WriteToken("/", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Modulus: WriteKeyword("Mod"); break; case BinaryOperatorType.DivideInteger: WriteToken("\\", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Power: WriteToken("*", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.Concat: WriteToken("&", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.ShiftLeft: WriteToken("<<", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.ShiftRight: WriteToken(">>", BinaryOperatorExpression.OperatorRole); break; case BinaryOperatorType.ReferenceEquality: WriteKeyword("Is"); break; case BinaryOperatorType.ReferenceInequality: WriteKeyword("IsNot"); break; case BinaryOperatorType.Like: WriteKeyword("Like"); break; case BinaryOperatorType.DictionaryAccess: WriteToken("!", BinaryOperatorExpression.OperatorRole); break; default: throw new Exception("Invalid value for BinaryOperatorType: " + binaryOperatorExpression.Operator); } Space(); binaryOperatorExpression.Right.AcceptVisitor(this, data); return EndNode(binaryOperatorExpression); } public object VisitIdentifierExpression(IdentifierExpression identifierExpression, object data) { StartNode(identifierExpression); identifierExpression.Identifier.AcceptVisitor(this, data); WriteTypeArguments(identifierExpression.TypeArguments); return EndNode(identifierExpression); } public object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data) { StartNode(assignmentExpression); assignmentExpression.Left.AcceptVisitor(this, data); Space(); switch (assignmentExpression.Operator) { case AssignmentOperatorType.Assign: WriteToken("=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.Add: WriteToken("+=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.Subtract: WriteToken("-=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.Multiply: WriteToken("*=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.Divide: WriteToken("/=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.Power: WriteToken("^=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.DivideInteger: WriteToken("\\=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.ConcatString: WriteToken("&=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.ShiftLeft: WriteToken("<<=", AssignmentExpression.OperatorRole); break; case AssignmentOperatorType.ShiftRight: WriteToken(">>=", AssignmentExpression.OperatorRole); break; default: throw new Exception("Invalid value for AssignmentOperatorType: " + assignmentExpression.Operator); } Space(); assignmentExpression.Right.AcceptVisitor(this, data); return EndNode(assignmentExpression); } public object VisitInvocationExpression(InvocationExpression invocationExpression, object data) { StartNode(invocationExpression); invocationExpression.Target.AcceptVisitor(this, data); WriteCommaSeparatedListInParenthesis(invocationExpression.Arguments, false); return EndNode(invocationExpression); } public object VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, object data) { StartNode(arrayInitializerExpression); WriteToken("{", ArrayInitializerExpression.Roles.LBrace); Space(); WriteCommaSeparatedList(arrayInitializerExpression.Elements); Space(); WriteToken("}", ArrayInitializerExpression.Roles.RBrace); return EndNode(arrayInitializerExpression); } public object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data) { StartNode(arrayCreateExpression); WriteKeyword("New"); Space(); arrayCreateExpression.Type.AcceptVisitor(this, data); WriteCommaSeparatedListInParenthesis(arrayCreateExpression.Arguments, false); foreach (var specifier in arrayCreateExpression.AdditionalArraySpecifiers) { specifier.AcceptVisitor(this, data); } if (!arrayCreateExpression.Initializer.IsNull) { Space(); WriteToken("=", ArrayCreateExpression.Roles.Assign); Space(); arrayCreateExpression.Initializer.AcceptVisitor(this, data); } return EndNode(arrayCreateExpression); } public object VisitObjectCreationExpression(ObjectCreationExpression objectCreationExpression, object data) { StartNode(objectCreationExpression); WriteKeyword("New"); objectCreationExpression.Type.AcceptVisitor(this, data); WriteCommaSeparatedListInParenthesis(objectCreationExpression.Arguments, false); if (!objectCreationExpression.Initializer.IsNull) { Space(); objectCreationExpression.Initializer.AcceptVisitor(this, data); } return EndNode(objectCreationExpression); } public object VisitCastExpression(CastExpression castExpression, object data) { StartNode(castExpression); switch (castExpression.CastType) { case CastType.DirectCast: WriteKeyword("DirectCast"); break; case CastType.TryCast: WriteKeyword("TryCast"); break; case CastType.CType: WriteKeyword("CType"); break; case CastType.CBool: WriteKeyword("CBool"); break; case CastType.CByte: WriteKeyword("CByte"); break; case CastType.CChar: WriteKeyword("CChar"); break; case CastType.CDate: WriteKeyword("CDate"); break; case CastType.CDec: WriteKeyword("CType"); break; case CastType.CDbl: WriteKeyword("CDec"); break; case CastType.CInt: WriteKeyword("CInt"); break; case CastType.CLng: WriteKeyword("CLng"); break; case CastType.CObj: WriteKeyword("CObj"); break; case CastType.CSByte: WriteKeyword("CSByte"); break; case CastType.CShort: WriteKeyword("CShort"); break; case CastType.CSng: WriteKeyword("CSng"); break; case CastType.CStr: WriteKeyword("CStr"); break; case CastType.CUInt: WriteKeyword("CUInt"); break; case CastType.CULng: WriteKeyword("CULng"); break; case CastType.CUShort: WriteKeyword("CUShort"); break; default: throw new Exception("Invalid value for CastType"); } WriteToken("(", CastExpression.Roles.LPar); castExpression.Expression.AcceptVisitor(this, data); if (castExpression.CastType == CastType.CType || castExpression.CastType == CastType.DirectCast || castExpression.CastType == CastType.TryCast) { WriteToken(",", CastExpression.Roles.Comma); Space(); castExpression.Type.AcceptVisitor(this, data); } WriteToken(")", CastExpression.Roles.RPar); return EndNode(castExpression); } public object VisitComment(Comment comment, object data) { formatter.WriteComment(comment.IsDocumentationComment, comment.Content); return null; } public object VisitEventDeclaration(EventDeclaration eventDeclaration, object data) { StartNode(eventDeclaration); WriteAttributes(eventDeclaration.Attributes); WriteModifiers(eventDeclaration.ModifierTokens); if (eventDeclaration.IsCustom) WriteKeyword("Custom"); WriteKeyword("Event"); WriteIdentifier(eventDeclaration.Name.Name); if (!eventDeclaration.IsCustom && eventDeclaration.ReturnType.IsNull) WriteCommaSeparatedListInParenthesis(eventDeclaration.Parameters, false); if (!eventDeclaration.ReturnType.IsNull) { Space(); WriteKeyword("As"); eventDeclaration.ReturnType.AcceptVisitor(this, data); } WriteImplementsClause(eventDeclaration.ImplementsClause); if (eventDeclaration.IsCustom) { NewLine(); Indent(); eventDeclaration.AddHandlerBlock.AcceptVisitor(this, data); eventDeclaration.RemoveHandlerBlock.AcceptVisitor(this, data); eventDeclaration.RaiseEventBlock.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); WriteKeyword("Event"); } NewLine(); return EndNode(eventDeclaration); } public object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) { StartNode(unaryOperatorExpression); switch (unaryOperatorExpression.Operator) { case UnaryOperatorType.Not: WriteKeyword("Not"); break; case UnaryOperatorType.Minus: WriteToken("-", UnaryOperatorExpression.OperatorRole); break; case UnaryOperatorType.Plus: WriteToken("+", UnaryOperatorExpression.OperatorRole); break; case UnaryOperatorType.AddressOf: WriteKeyword("AddressOf"); break; case UnaryOperatorType.Await: WriteKeyword("Await"); break; default: throw new Exception("Invalid value for UnaryOperatorType"); } unaryOperatorExpression.Expression.AcceptVisitor(this, data); return EndNode(unaryOperatorExpression); } public object VisitFieldInitializerExpression(FieldInitializerExpression fieldInitializerExpression, object data) { StartNode(fieldInitializerExpression); if (fieldInitializerExpression.IsKey) { WriteKeyword("Key"); Space(); } WriteToken(".", FieldInitializerExpression.Roles.Dot); fieldInitializerExpression.Identifier.AcceptVisitor(this, data); Space(); WriteToken("=", FieldInitializerExpression.Roles.Assign); Space(); fieldInitializerExpression.Expression.AcceptVisitor(this, data); return EndNode(fieldInitializerExpression); } public object VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, object data) { throw new NotImplementedException(); } public object VisitConditionalExpression(ConditionalExpression conditionalExpression, object data) { StartNode(conditionalExpression); WriteKeyword("If"); WriteToken("(", ConditionalExpression.Roles.LPar); conditionalExpression.ConditionExpression.AcceptVisitor(this, data); WriteToken(",", ConditionalExpression.Roles.Comma); Space(); if (!conditionalExpression.TrueExpression.IsNull) { conditionalExpression.TrueExpression.AcceptVisitor(this, data); WriteToken(",", ConditionalExpression.Roles.Comma); Space(); } conditionalExpression.FalseExpression.AcceptVisitor(this, data); WriteToken(")", ConditionalExpression.Roles.RPar); return EndNode(conditionalExpression); } public object VisitWhileStatement(WhileStatement whileStatement, object data) { StartNode(whileStatement); WriteKeyword("While"); Space(); whileStatement.Condition.AcceptVisitor(this, data); NewLine(); Indent(); whileStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); WriteKeyword("While"); return EndNode(whileStatement); } public object VisitExitStatement(ExitStatement exitStatement, object data) { StartNode(exitStatement); WriteKeyword("Exit"); switch (exitStatement.ExitKind) { case ExitKind.Sub: WriteKeyword("Sub"); break; case ExitKind.Function: WriteKeyword("Function"); break; case ExitKind.Property: WriteKeyword("Property"); break; case ExitKind.Do: WriteKeyword("Do"); break; case ExitKind.For: WriteKeyword("For"); break; case ExitKind.While: WriteKeyword("While"); break; case ExitKind.Select: WriteKeyword("Select"); break; case ExitKind.Try: WriteKeyword("Try"); break; default: throw new Exception("Invalid value for ExitKind"); } return EndNode(exitStatement); } public object VisitForStatement(ForStatement forStatement, object data) { StartNode(forStatement); WriteKeyword("For"); forStatement.Variable.AcceptVisitor(this, data); WriteKeyword("To"); forStatement.ToExpression.AcceptVisitor(this, data); if (!forStatement.StepExpression.IsNull) { WriteKeyword("Step"); Space(); forStatement.StepExpression.AcceptVisitor(this, data); } NewLine(); Indent(); forStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("Next"); return EndNode(forStatement); } public object VisitForEachStatement(ForEachStatement forEachStatement, object data) { StartNode(forEachStatement); WriteKeyword("For"); WriteKeyword("Each"); forEachStatement.Variable.AcceptVisitor(this, data); WriteKeyword("In"); forEachStatement.InExpression.AcceptVisitor(this, data); NewLine(); Indent(); forEachStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("Next"); return EndNode(forEachStatement); } public object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data) { StartNode(operatorDeclaration); WriteAttributes(operatorDeclaration.Attributes); WriteModifiers(operatorDeclaration.ModifierTokens); WriteKeyword("Operator"); switch (operatorDeclaration.Operator) { case OverloadableOperatorType.Add: case OverloadableOperatorType.UnaryPlus: WriteToken("+", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Subtract: case OverloadableOperatorType.UnaryMinus: WriteToken("-", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Multiply: WriteToken("*", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Divide: WriteToken("/", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Modulus: WriteKeyword("Mod"); break; case OverloadableOperatorType.Concat: WriteToken("&", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Not: WriteKeyword("Not"); break; case OverloadableOperatorType.BitwiseAnd: WriteKeyword("And"); break; case OverloadableOperatorType.BitwiseOr: WriteKeyword("Or"); break; case OverloadableOperatorType.ExclusiveOr: WriteKeyword("Xor"); break; case OverloadableOperatorType.ShiftLeft: WriteToken("<<", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.ShiftRight: WriteToken(">>", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.GreaterThan: WriteToken(">", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.GreaterThanOrEqual: WriteToken(">=", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.Equality: WriteToken("=", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.InEquality: WriteToken("<>", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.LessThan: WriteToken("<", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.LessThanOrEqual: WriteToken("<=", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.IsTrue: WriteKeyword("IsTrue"); break; case OverloadableOperatorType.IsFalse: WriteKeyword("IsFalse"); break; case OverloadableOperatorType.Like: WriteKeyword("Like"); break; case OverloadableOperatorType.Power: WriteToken("^", OperatorDeclaration.Roles.Keyword); break; case OverloadableOperatorType.CType: WriteKeyword("CType"); break; case OverloadableOperatorType.DivideInteger: WriteToken("\\", OperatorDeclaration.Roles.Keyword); break; default: throw new Exception("Invalid value for OverloadableOperatorType"); } WriteCommaSeparatedListInParenthesis(operatorDeclaration.Parameters, false); if (!operatorDeclaration.ReturnType.IsNull) { Space(); WriteKeyword("As"); WriteAttributes(operatorDeclaration.ReturnTypeAttributes); operatorDeclaration.ReturnType.AcceptVisitor(this, data); } if (!operatorDeclaration.Body.IsNull) { NewLine(); Indent(); WriteBlock(operatorDeclaration.Body); Unindent(); WriteKeyword("End"); WriteKeyword("Operator"); } NewLine(); return EndNode(operatorDeclaration); } public object VisitSelectStatement(SelectStatement selectStatement, object data) { StartNode(selectStatement); WriteKeyword("Select"); WriteKeyword("Case"); selectStatement.Expression.AcceptVisitor(this, data); NewLine(); Indent(); foreach (CaseStatement stmt in selectStatement.Cases) { stmt.AcceptVisitor(this, data); } Unindent(); WriteKeyword("End"); WriteKeyword("Select"); return EndNode(selectStatement); } public object VisitCaseStatement(CaseStatement caseStatement, object data) { StartNode(caseStatement); WriteKeyword("Case"); if (caseStatement.Clauses.Count == 1 && caseStatement.Clauses.First().Expression.IsNull) WriteKeyword("Else"); else WriteCommaSeparatedList(caseStatement.Clauses); NewLine(); Indent(); caseStatement.Body.AcceptVisitor(this, data); Unindent(); return EndNode(caseStatement); } public object VisitSimpleCaseClause(SimpleCaseClause simpleCaseClause, object data) { StartNode(simpleCaseClause); simpleCaseClause.Expression.AcceptVisitor(this, data); return EndNode(simpleCaseClause); } public object VisitRangeCaseClause(RangeCaseClause rangeCaseClause, object data) { StartNode(rangeCaseClause); rangeCaseClause.Expression.AcceptVisitor(this, data); WriteKeyword("To"); rangeCaseClause.ToExpression.AcceptVisitor(this, data); return EndNode(rangeCaseClause); } public object VisitComparisonCaseClause(ComparisonCaseClause comparisonCaseClause, object data) { StartNode(comparisonCaseClause); switch (comparisonCaseClause.Operator) { case ComparisonOperator.Equality: WriteToken("=", ComparisonCaseClause.OperatorRole); break; case ComparisonOperator.InEquality: WriteToken("<>", ComparisonCaseClause.OperatorRole); break; case ComparisonOperator.LessThan: WriteToken("<", ComparisonCaseClause.OperatorRole); break; case ComparisonOperator.GreaterThan: WriteToken(">", ComparisonCaseClause.OperatorRole); break; case ComparisonOperator.LessThanOrEqual: WriteToken("<=", ComparisonCaseClause.OperatorRole); break; case ComparisonOperator.GreaterThanOrEqual: WriteToken(">=", ComparisonCaseClause.OperatorRole); break; default: throw new Exception("Invalid value for ComparisonOperator"); } Space(); comparisonCaseClause.Expression.AcceptVisitor(this, data); return EndNode(comparisonCaseClause); } public object VisitYieldStatement(YieldStatement yieldStatement, object data) { StartNode(yieldStatement); WriteKeyword("Yield"); yieldStatement.Expression.AcceptVisitor(this, data); return EndNode(yieldStatement); } public object VisitVariableInitializer(VariableInitializer variableInitializer, object data) { StartNode(variableInitializer); variableInitializer.Identifier.AcceptVisitor(this, data); if (!variableInitializer.Type.IsNull) { WriteKeyword("As"); variableInitializer.Type.AcceptVisitor(this, data); } if (!variableInitializer.Expression.IsNull) { Space(); WriteToken("=", VariableInitializer.Roles.Assign); Space(); variableInitializer.Expression.AcceptVisitor(this, data); } return EndNode(variableInitializer); } public object VisitVariableDeclaratorWithTypeAndInitializer(VariableDeclaratorWithTypeAndInitializer variableDeclaratorWithTypeAndInitializer, object data) { StartNode(variableDeclaratorWithTypeAndInitializer); WriteCommaSeparatedList(variableDeclaratorWithTypeAndInitializer.Identifiers); WriteKeyword("As"); variableDeclaratorWithTypeAndInitializer.Type.AcceptVisitor(this, data); if (!variableDeclaratorWithTypeAndInitializer.Initializer.IsNull) { Space(); WriteToken("=", VariableDeclarator.Roles.Assign); Space(); variableDeclaratorWithTypeAndInitializer.Initializer.AcceptVisitor(this, data); } return EndNode(variableDeclaratorWithTypeAndInitializer); } public object VisitVariableDeclaratorWithObjectCreation(VariableDeclaratorWithObjectCreation variableDeclaratorWithObjectCreation, object data) { StartNode(variableDeclaratorWithObjectCreation); WriteCommaSeparatedList(variableDeclaratorWithObjectCreation.Identifiers); WriteKeyword("As"); variableDeclaratorWithObjectCreation.Initializer.AcceptVisitor(this, data); return EndNode(variableDeclaratorWithObjectCreation); } public object VisitDoLoopStatement(DoLoopStatement doLoopStatement, object data) { StartNode(doLoopStatement); WriteKeyword("Do"); if (doLoopStatement.ConditionType == ConditionType.DoUntil) { WriteKeyword("Until"); doLoopStatement.Expression.AcceptVisitor(this, data); } if (doLoopStatement.ConditionType == ConditionType.DoWhile) { WriteKeyword("While"); doLoopStatement.Expression.AcceptVisitor(this, data); } NewLine(); Indent(); doLoopStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("Loop"); if (doLoopStatement.ConditionType == ConditionType.LoopUntil) { WriteKeyword("Until"); doLoopStatement.Expression.AcceptVisitor(this, data); } if (doLoopStatement.ConditionType == ConditionType.LoopWhile) { WriteKeyword("While"); doLoopStatement.Expression.AcceptVisitor(this, data); } return EndNode(doLoopStatement); } public object VisitUsingStatement(UsingStatement usingStatement, object data) { StartNode(usingStatement); WriteKeyword("Using"); WriteCommaSeparatedList(usingStatement.Resources); NewLine(); Indent(); usingStatement.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); WriteKeyword("Using"); return EndNode(usingStatement); } public object VisitGoToStatement(GoToStatement goToStatement, object data) { StartNode(goToStatement); WriteKeyword("GoTo"); goToStatement.Label.AcceptVisitor(this, data); return EndNode(goToStatement); } public object VisitSingleLineSubLambdaExpression(SingleLineSubLambdaExpression singleLineSubLambdaExpression, object data) { StartNode(singleLineSubLambdaExpression); WriteModifiers(singleLineSubLambdaExpression.ModifierTokens); WriteKeyword("Sub"); WriteCommaSeparatedListInParenthesis(singleLineSubLambdaExpression.Parameters, false); Space(); singleLineSubLambdaExpression.EmbeddedStatement.AcceptVisitor(this, data); return EndNode(singleLineSubLambdaExpression); } public object VisitSingleLineFunctionLambdaExpression(SingleLineFunctionLambdaExpression singleLineFunctionLambdaExpression, object data) { StartNode(singleLineFunctionLambdaExpression); WriteModifiers(singleLineFunctionLambdaExpression.ModifierTokens); WriteKeyword("Function"); WriteCommaSeparatedListInParenthesis(singleLineFunctionLambdaExpression.Parameters, false); Space(); singleLineFunctionLambdaExpression.EmbeddedExpression.AcceptVisitor(this, data); return EndNode(singleLineFunctionLambdaExpression); } public object VisitMultiLineLambdaExpression(MultiLineLambdaExpression multiLineLambdaExpression, object data) { StartNode(multiLineLambdaExpression); WriteModifiers(multiLineLambdaExpression.ModifierTokens); if (multiLineLambdaExpression.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); WriteCommaSeparatedListInParenthesis(multiLineLambdaExpression.Parameters, false); NewLine(); Indent(); multiLineLambdaExpression.Body.AcceptVisitor(this, data); Unindent(); WriteKeyword("End"); if (multiLineLambdaExpression.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); return EndNode(multiLineLambdaExpression); } public object VisitQueryExpression(QueryExpression queryExpression, object data) { StartNode(queryExpression); foreach (var op in queryExpression.QueryOperators) { op.AcceptVisitor(this, data); } return EndNode(queryExpression); } public object VisitContinueStatement(ContinueStatement continueStatement, object data) { StartNode(continueStatement); WriteKeyword("Continue"); switch (continueStatement.ContinueKind) { case ContinueKind.Do: WriteKeyword("Do"); break; case ContinueKind.For: WriteKeyword("For"); break; case ContinueKind.While: WriteKeyword("While"); break; default: throw new Exception("Invalid value for ContinueKind"); } return EndNode(continueStatement); } public object VisitExternalMethodDeclaration(ExternalMethodDeclaration externalMethodDeclaration, object data) { StartNode(externalMethodDeclaration); WriteAttributes(externalMethodDeclaration.Attributes); WriteModifiers(externalMethodDeclaration.ModifierTokens); WriteKeyword("Declare"); switch (externalMethodDeclaration.CharsetModifier) { case CharsetModifier.None: break; case CharsetModifier.Auto: WriteKeyword("Auto"); break; case CharsetModifier.Unicode: WriteKeyword("Unicode"); break; case CharsetModifier.Ansi: WriteKeyword("Ansi"); break; default: throw new Exception("Invalid value for CharsetModifier"); } if (externalMethodDeclaration.IsSub) WriteKeyword("Sub"); else WriteKeyword("Function"); externalMethodDeclaration.Name.AcceptVisitor(this, data); WriteKeyword("Lib"); Space(); WritePrimitiveValue(externalMethodDeclaration.Library); Space(); if (externalMethodDeclaration.Alias != null) { WriteKeyword("Alias"); Space(); WritePrimitiveValue(externalMethodDeclaration.Alias); Space(); } WriteCommaSeparatedListInParenthesis(externalMethodDeclaration.Parameters, false); if (!externalMethodDeclaration.IsSub && !externalMethodDeclaration.ReturnType.IsNull) { Space(); WriteKeyword("As"); WriteAttributes(externalMethodDeclaration.ReturnTypeAttributes); externalMethodDeclaration.ReturnType.AcceptVisitor(this, data); } NewLine(); return EndNode(externalMethodDeclaration); } public static string ToVBNetString(PrimitiveExpression primitiveExpression) { var writer = new StringWriter(); new OutputVisitor(writer, new VBFormattingOptions()).WritePrimitiveValue(primitiveExpression.Value); return writer.ToString(); } public object VisitEmptyExpression(EmptyExpression emptyExpression, object data) { StartNode(emptyExpression); return EndNode(emptyExpression); } } }