The estimable MVP and T4 enthusiast, Kathleen Dollard has a new post where she's using T4's ability to spit arbitrary text as a host for a textual DSL.

She's set up a small DSL for describing contract interfaces for a MEF framework:

         new Interface()
         {
            Name = "ISearchModelBase",
            Scope = Scope.Public,
            CompositionInfo =
            {
               new Property() {Name="TargetType", PropertyType="Type"}
            },
            Members =
            {
               new Property() {Name="DisplayName", PropertyType="string"},
               new Property() {Name="DataName", PropertyType="string"}
            }
         }.Output() 

Hosting this in T4 with a couple of lines of wrapper code spits out the following output, codifying her standard implementation pattern for MEF contracts and custom attributes:

Option Strict On
Option Explicit On
Option Infer On 
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.ComponentModel.Composition 
 
public Interface ISearchModelBase
   Property DisplayName As string
   Property DataName As string
End Interface
public Interface ISearchModelBaseComposition
   Readonly Property TargetType As Type
End Interface 
 
< MetadataAttribute() > _
< AttributeUsage(AttributeTargets.Class, AllowMultiple:=False) > _
public Class SearchModelBaseAttribute
   Inherits ExportAttribute
   Implements ISearchModelBaseComposition 
 
   Public Sub New( ByVal targetType As Type)
      MyBase.New(GetType(ISearchModelBase))
      _targetType = targetType
   End Sub 
 
   Private _targetType As Type
   Public Readonly Property TargetType As Type Implements ISearchModelBaseComposition.TargetType
      Get
         Return _targetType
      End Get
   End Property 
 
End Class 

 

Fascinating stuff, especially when you see that these days, populating such a structure in C#4.0 is such a snap with the new initializer syntax.

Peeking under the covers, at the attachment to Kathleen's blog entry, the code combines the DSL structure definition with the code output for VB in a way that's a bit too printf-ish for my personal taste, so I was tempted to T4 it up a bit.

The original template passes a StringBuilder down the call tree and builds code using AppendLine():

 private void AppendOpen(System.Text.StringBuilder sb)
      {
         sb.AppendLine("Option Strict On");
         sb.AppendLine("Option Explicit On");
         sb.AppendLine("Option Infer On");
         sb.AppendLine("");
         sb.AppendLine("Imports System");
         sb.AppendLine("Imports System.Collections.Generic");
         sb.AppendLine("Imports System.Linq");
         sb.AppendLine("Imports System.ComponentModel.Composition");
      }

Instead of this, I wanted to use regular T4 syntax with a class feature block:

 

      private void AppendOpen()
      {
#>
Option Strict On
Option Explicit On
Option Infer On
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.ComponentModel.Composition
<#+
      }

 

However, this code is inside a nested class, so the underlying Write statements needed by T4 aren't present.  To get around this, I whipped up a trivial base class that supplies all of the necessary plumbing for T4 to be happy.   This works because this part of T4 doesn't rely on a specific type, rather it just expects access to the members it needs and any class will do.  In my implementation I simply delegate all of the calls back out to the main T4 template.

The only changes necessary to the DSL definition classes are to derive from my DslBase class and to add a trivial constructor to supply the main template to each DSL class instantiation:

public class Interface : DslBase
{
    public Interface(Microsoft.VisualStudio.TextTemplating.TextTransformation outer) : base(outer)
    {
    }

 

I've attached the base class code and a modified version of Kathleen's template for you to play with here. Enjoy.

Technorati Tags: ,