Bending T4 to be a textual DSL host

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: T4,Textual DSL