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.
No comments:
Post a Comment