Share via


Code Generation in multiple languages

I'm currently working on a personal project that needs to spit out code after parsing some XML file. I had previously used the .NET frameworks CodeDom to do on the fly compilation and hence tried digging it up to see if I could use it for code generation. In a small time I was completely blown over by the feature set and what I could achieve in a relatively small time.

I had initially expected to get little support from the framework and had thought I'd manipulate text to generate the C# code. I now figured out that I could use the CodeDom to build the code structure hierarchy and just pass on a language parameter and if that language is supported generate code using that language. Suddenly my application was not limited to C# but I could use VB.NET or VJ# for my output code as well. To demonstrate this I'd skip the XML parsing (serialization and logic) part. The following code generates a hello world program in any of the supported .NET language

 private void BtnGenerate_Click(object sender, EventArgs e)
{

    TextCode.Text  = GenerateCode("C#");
    TextCode.Text += GenerateCode("VJ#");
    TextCode.Text += GenerateCode("VB");
}
public string GenerateCode(string language)
{
    // get CodeDom provider from the language name
    CodeDomProvider provider =
            CSharpCodeProvider.CreateProvider(language);
    // generate the code 
    return GenerateCode(provider);
}


public string GenerateCode(CodeDomProvider provider)
{
    // open string based in memory streams 
    using(StringWriter writer = new StringWriter())
    using (IndentedTextWriter tw = 
        new IndentedTextWriter(writer, " "))
    {
        // create top level namespace
        CodeNamespace myNamespace = 
            new CodeNamespace("AbhinabaNameSpace");
        // create file level comment
        CodeComment comment = new CodeComment(
            string.Format("Generated on {0}",
                DateTime.Now.ToLocalTime().ToShortDateString()),
            false);
        CodeCommentStatement commentStatement =
            new CodeCommentStatement(comment);
        myNamespace.Comments.Add(commentStatement);
        // add using statements for the required namespaces
        myNamespace.Imports.Add(
            new CodeNamespaceImport("System"));
        // define the one and only class
        CodeTypeDeclaration mainClass = 
            new CodeTypeDeclaration();
        mainClass.IsClass = true;
        mainClass.Name = "HelloWorldMainClass";
        mainClass.Attributes = MemberAttributes.Public;
        myNamespace.Types.Add(mainClass);
        //define the entry point which'd be 
        //Main method in C#
        CodeEntryPointMethod mainMethod = 
            new CodeEntryPointMethod();
        mainMethod.Comments.Add(
            new CodeCommentStatement("<summary>", true));
        mainMethod.Comments.Add(
            new CodeCommentStatement("Entry point", true));
        mainMethod.Comments.Add(
            new CodeCommentStatement("</summary>", true));
        mainClass.Members.Add(mainMethod);
        //define the string variable message
        CodeVariableDeclarationStatement strDecl = 
            new CodeVariableDeclarationStatement(
                new CodeTypeReference(typeof(string)),
                "message");
        mainMethod.Statements.Add(strDecl);
        //create the message = "hello world" statement
        CodeAssignStatement ptxAssign = 
            new CodeAssignStatement(
                new CodeVariableReferenceExpression("message"),
                new CodeSnippetExpression("\"hello world\""));
        mainMethod.Statements.Add(ptxAssign);
        //call console.writeline to print the statement
        CodeMethodInvokeExpression invokeConsoleWriteLine =
            new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(typeof(Console)),                 "WriteLine",
                new CodeExpression[] 
                {
                    new CodeArgumentReferenceExpression(
                                                "message"),
                }
            );
        mainMethod.Statements.Add(invokeConsoleWriteLine);
        // code generation options
        CodeGeneratorOptions opt = new CodeGeneratorOptions();
        opt.BracingStyle = "C";
        opt.BlankLinesBetweenMembers = false;
        // generate the code and return it
        provider.GenerateCodeFromNamespace(myNamespace, tw, opt);
        return writer.ToString();
    }
}

If only more languages were on .NET I could build the list in https://www2.latech.edu/~acm/HelloWorld.shtml in about couple of hours :)

I get the following output

 In C#
=======
// Generated on 2/27/2006
namespace AbhinabaNameSpace
{
    using System;
    
    public class HelloWorldMainClass
    {
        /// 
        /// Entry point
        /// 
        public static void Main()
        {
            string message;
            message = "hello world";
            System.Console.WriteLine(message);
        }
    }
}

VJ#
=======
// Generated on 2/27/2006
package AbhinabaNameSpace;
import System.*;

public class HelloWorldMainClass
{
    /** Entry point */
    public static void main(String[] args)
    {
        String message;
        message = "hello world";
        System.Console.WriteLine(message);
    }
}



VB
=======
Imports System

'Generated on 2/27/2006
Namespace AbhinabaNameSpace
    Public Class HelloWorldMainClass
        '''
        '''Entry point
        '''
        Public Shared Sub Main()
            Dim message As String
            message = "hello world"
            System.Console.WriteLine(message)
        End Sub
    End Class
End Namespace

Comments

  • Anonymous
    February 26, 2006
    As you can see the code to create a CodeDOM tree can get pretty complex very easily and maintaining it requires a deep understanding of CodeDOM internals.

    I've developed an intermediate language called CDIL (CodeDOM intermediate language) that is easily parsable into a CodeDOM tree. This makes things a bit more maintainable when working with a large volume of generated code.

    It's used in my Sooda project:

    http://www.sooda.org/technical-cdil.html

    Some examples of CDIL templates are here:

    http://trac.sav.net/sooda/browser/trunk/Sooda/src/Sooda.CodeGen/CDIL/Templates/

    The CDIL parser even has a preprocessor so that you can parametrize your code using external variables.

    Jarek
  • Anonymous
    February 26, 2006
    Very interesting. However, I'd have preferred an XML to represent the tree as in CDML (CodeDom Markup Language) and then create a parser for it.....
  • Anonymous
    February 26, 2006
    PingBack from http://blog.marcus-kimpenhaus.de/?p=207
  • Anonymous
    February 28, 2006
    Um ... why would you want to use XML? Especially if it's something that a human would have to maintain?
  • Anonymous
    February 28, 2006
    XML is better in this case because otherwise there'd be one more language to learn. Even in case human maintainance is required XML is proving its worth. XAML is one example. Moreover the model would be to write a tool that helps generate the xml and then have people customize it as necessary. In the same lines of Sparkle->XAML->VS
  • Anonymous
    March 01, 2006
    Sure, but this is imperative code that we're talking about, not a bunch of declarative stuff that is best visualized by a tool like Sparkle.

    Learning a DSL isn't really all that hard, especially if the DSL is based on ideas from an existing language.

    For one data point, in my RubyCLR bridge I created a DSL that looks and feels just like CIL. I use it to generate shims that marshal data back and forth between the Ruby interpreter and the CLR. However, because it was Ruby, I was able to extend CIL via macros to add ruby-specific instructions that greatly simplified maintenance of the code. It is much easier to maintain my code in Ruby than in C++, which the first version of my bridge was written in.

    The DSL is so useful that the Ruby.NET guys are planning on using it to build out their back-end code generator in Ruby (until they can self-host, of course).
  • Anonymous
    August 21, 2006
    There are two ways in which you can use using directive as outlined below
    Style 1using System;using...