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...