Showing posts with label PowerShell. Show all posts
Showing posts with label PowerShell. Show all posts

Saturday, November 24, 2007

PowerShell DSLs: Using hashtables and scriptblocks together

Previously I presented the use of hashtables and scriptblocks as DSL input formats. You can also convert between them, with processing in between. I tried that out, and I think the result is interesting enough to post. Again, here is the input:

class MyClass {

    field ([string]) S

}

The previous code would use the 'field' function to manipulate the class object as appropriate:

    function field

    {

        param (

            [Type] $type,

            [String] $name

        )

        

        # add the field declaration

        $field = New-Object System.CodeDom.CodeMemberField($type, $name)

        $field.Attributes = [System.CodeDom.MemberAttributes]::Public

        $class.Members.Add( $field )

        

        # add the ctor parameter

        $ctorParameter = New-Object System.CodeDom.CodeParameterDeclarationExpression($type, $name)

        $constructor.Parameters.Add($ctorParameter)


 

        # add the ctor initializer

        $fieldReference = New-Object System.CodeDom.CodeFieldReferenceExpression(

            (New-Object System.CodeDom.CodeThisReferenceExpression),

            $name

        )

        

        $ctorInitializer = New-Object System.CodeDom.CodeAssignStatement(

            $fieldReference,

            (New-Object System.CodeDom.CodeVariableReferenceExpression $name)

        )

        

        $constructor.Statements.Add( $ctorInitializer )

    }

    

    & $memberScriptBlock | Out-Null


 

But another idea is to have the 'field' function produce a collection of CodeDom objects that can be assembled later.

    # return a hash of the CodeDom objects related to

    # this field

    function field

    {

        param (

            [Type] $type,

            [String] $name

        )

        

        @{

            fieldDeclaration = $(

                $field = New-Object System.CodeDom.CodeMemberField($type, $name)

                $field.Attributes = [System.CodeDom.MemberAttributes]::Public

                $field

                )

            parameter = New-Object System.CodeDom.CodeParameterDeclarationExpression($type, $name)

            fieldReference = New-Object System.CodeDom.CodeFieldReferenceExpression(

                (New-Object System.CodeDom.CodeThisReferenceExpression),

                $name

            )

            parameterReference = New-Object System.CodeDom.CodeVariableReferenceExpression $name

        }                

    }

    

    & $memberScriptBlock | foreach {

        $class.Members.Add( $_.fieldDeclaration )

        

        $constructor.Parameters.Add( $_.parameter )


 

        $constructor.Statements.Add(

            $(New-Object System.CodeDom.CodeAssignStatement( $_.fieldReference, $_.parameterReference ))

        )

    } | Out-Null

I suspect that the latter model is a little better because it separates concerns. Consider if I were to add other statements to my language, such as 'property'. The 'foreach' at the end could probably be written in a way that works for both fields and properties. However, the hashtable is slightly concerning, because it's not typed – if I get the key names wrong somewhere, I'm screwed.

I find that I'm spending quite a lot of time on this, but it's important that I find a way to create DSLs quickly. I figured that right now I'm just learning the techniques, and then if I master them, then I can do it more quickly when the time comes.

Friday, November 23, 2007

Immutable data class generator: Skeleton implementation

I've made a little progress on my PowerShell DSL for data classes, and figured it's a good time to show it off.

Here's the input format at this point:

class MyClass {

field ([string]) S

}

(I'm using the ScriptBlock approach.) I generally like the syntax, except for the need to add parentheses around the type. The alternative is to use a fully-qualified string:

class MyClass {

field System.String S

}

I'm not sure which is better, but I'm going with the first one for now, because it allows a more specific type than string.

The basis of the implementation is to write methods named 'class' and 'field'. 'class' is simple; here's an excerpt of the important bits:

$class = New-Object System.CodeDom.CodeTypeDeclaration $name

$class.TypeAttributes = [System.Reflection.TypeAttributes]::NotPublic

$class.IsPartial = $true

$constructor = New-Object System.CodeDom.CodeConstructor

$class.Members.Add( $constructor ) Out-Null

$class

The implementation of 'field' could go in several different ways. For now, I've written it to manipulate the class as appropriate, e.g.:

$field = New-Object System.CodeDom.CodeMemberField($type, $name)

$class.Members.Add( $field )


$ctorParameter = New-Object System.CodeDom.CodeParameterDeclarationExpression($type, $name)

$constructor.Parameters.Add($ctorParameter)


$fieldReference = New-Object System.CodeDom.CodeFieldReferenceExpression(

(New-Object System.CodeDom.CodeThisReferenceExpression),

$name

)


$ctorInitializer = New-Object System.CodeDom.CodeAssignStatement(

$fieldReference,

(New-Object System.CodeDom.CodeVariableReferenceExpression $name)

)


$constructor.Statements.Add( $ctorInitializer )


(I'd like to attach the full script, but I don't see a way to do that in blogspot). It generates this output:

internal partial class MyClass

{

public string S;

private MyClass(string S)

{

this.S = S;

}

}



EDIT: See PowerShell DSLs: Using hashtables and scriptblocks together for a different view of this code.

Immutable data class generator

I'm working on a DSL implementation for defining "data classes" with certain properties in C# (see http://blogs.gotdotnet.com/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx for an example of the output).

Table of Contents

  1. Goals
  2. Skeleton implementation
  3. ???

PowerShell DSLs: Script block input

As an alternative to using a hashtable for input, you can use a script block. Here's a script-block-way of doing the same thing as before:

class MyClass -accessibilty:public {

member string S

}


and here's one of many ways of handling it

function class {

param (

[String] $name = $( throw
"name is required" ),

[String] $accessibilty,

[ScriptBlock] $memberScriptBlock = $( throw
"member script block is require" )

)


" $accessibilty class $name {"


& $memberScriptBlock % { " public $($_.declaration)" }


" }"

}


function member {

param (

[String] $type = $( throw
"type is required" ),

[String] $name = $( throw
"name is required" )

)


@{

declaration = "$type $name;"


}

}


What's happening here is that the first word on any line becomes a function, and the remaining words are parameters to that function. So, you're now creating an "internal DSL" in PowerShell. This lets you work in a more idoimatic PowerShell manner.


PowerShell DSLs: Hash table input

lassOne way to take PowerShell DSL input is in the form of a hash table. Taking from the Data Type with Builder example, you could write input as:

@{
kind = 'class'
name = 'MyClass'
accessibilty = 'public'
members = @{
type = 'string'
name = 'S'
}
}


How do you consume this input? Here's an example. (Note that the code generation side is not the focal point here -- the way that we interpet the input is what matters.)


if ($input.kind = 'class') {
" $($input.accessibilty) class $($input.name) {"

$input.members | foreach {
" public $($_.type) $($_.name);"
}

" }"

}


Which generates this output:


public class MyClass {
public System.String S;
}


This approach seems most effective when you need to express a lot of attributes on a single element. The downside is that the input seems a bit verbose and unnatural in some cases.

PowerShell DSLs

I've written before about my attraction to Domain-Specific Languages and my curiosity about using PowerShell to handle them, but I only recently got any time to think about it more. I'm working on a DSL implementation for defining "data classes" with certain properties in C# (see http://blogs.gotdotnet.com/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx#comments for an example).

I have only 3 examples of PowerShell DSLs to work from, and I don't see any guidance in this area, so I figured I'd write about what I find.

Table of Contents

  1. Prior art
  2. Why PowerShell?
  3. Custom invocation w/ a library
  4. DSL tool invocation
  5. Hash table input
  6. Script block input
  7. Using hashtables and scriptblocks together

 
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.