Coding for fun: Write your code in comments with Roslyn

Sometimes, you have some very basic methods and you could find useless to comment them but sometimes you have to comment them in order to generate help file.

Some tools allow you to generate comments using the method signature.

For fun, I will use another way: I will write the method code in the comment.

I have a ConsoleApplication:

class Program
{
    
static void Main(string
[] args)
     {
        
Console.WriteLine("a ?"
);
        
int a = int.Parse(Console
.ReadLine());
        
Console.WriteLine("b ?"
);
        
int b = int.Parse(Console
.ReadLine());
        
Console.WriteLine("{0} + {1} = {2}"
, a, b, Add(a, b));
        
Console
.ReadLine();
     }



/// <summary> /// return a + b; /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static int Add(int a, int
b)
{
        
throw new NotImplementedException();
} }

The code of the Add method (return a + b;) is commented.

Now I will write my own C# compiler using Roslyn:

class Program
{
    
static void Main(string
[] args)
     {
        
string
solutionPath = args[0];
        
string
binaryDirectory = args[1];
        
var solution = Solution
.Load(solutionPath);
        
List<IProject
> notSortedProjects = solution.Projects.ToList();
        
List<IProject> projects = new List<IProject
>();
        
do
         {
            
foreach (IProject project in
notSortedProjects.ToList())
             {
                
if
(project.ProjectReferences.All(prId => projects.Any(p => p.Id == prId)))
                 {
                     projects.Add(project);
                     notSortedProjects.Remove(project);
                 }
             }
         }
while
(notSortedProjects.Count != 0);


        
foreach (var project in
projects)
         {
            
foreach (var document in
project.Documents)
             {
                
var
documentTree = document.GetSyntaxTree();
                
var newDocumentTree = new CodeInCommentsRewriter().Visit((SyntaxNode
)documentTree.Root);
                
var newDocumentTreeText = new StringText
(newDocumentTree.ToString());
                 solution = solution.UpdateDocument(document.Id, newDocumentTreeText);
             }
            
using (var stream = new FileStream(string.Format("{0}.{1}"
,
                 
Path
.Combine(binaryDirectory, project.AssemblyName),
                  project.CompilationOptions.AssemblyKind ==
AssemblyKind.DynamicallyLinkedLibrary ? "dll" : "exe"), FileMode
.Create))
             {
                
var
emitResult = solution.Projects.First(p => p.Id == project.Id).GetCompilation().Emit(stream);
                
if
(!emitResult.Success)
                    
throw new InvalidOperationException();
             }
         }
     } }
public class CodeInCommentsRewriter : SyntaxRewriter
{
    
protected override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax
node)
     {
        
IEnumerator<SyntaxNode
> bodySyntaxNodesEnumerator = node.BodyOpt.ChildNodes().GetEnumerator();
        
if
(bodySyntaxNodesEnumerator.MoveNext())
         {
            
ThrowStatementSyntax childNode = bodySyntaxNodesEnumerator.Current as ThrowStatementSyntax
;
            
if (!bodySyntaxNodesEnumerator.MoveNext()// only one child node                 && childNode != null
)
             {
                
var comment = node.GetLeadingTrivia().Select(t => Regex.Match(t.GetText(), "return (.?)*").Value).FirstOrDefault(v => ! string
.IsNullOrEmpty(v));
                
if (comment != null
)
                 {
                    
return Syntax
.MethodDeclaration(
                         node.Attributes,
                         node.Modifiers,
                         node.ReturnType,
                         node.ExplicitInterfaceSpecifierOpt,
                         node.Identifier,
                         node.TypeParameterListOpt,
                         node.ParameterList,
                         node.ConstraintClauses,
                        
Syntax
.Block(
                             node.BodyOpt.OpenBraceToken,
                            
Syntax
.List(
                                
Syntax
.ParseStatement(comment)),
                             node.BodyOpt.CloseBraceToken),
                         node.SemicolonTokenOpt);
                 }
             }
         }
        
return base.VisitMethodDeclaration(node);
     } }

And here we are! // I use some shortcut (the code is a return in one line)

If I execute my compiler and then the generated exe, I can see in the Console that 1 + 2 = 3.

Published Thu, Jan 5 2012 14:08 by Matthieu MEZIL
Filed under: , ,

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: