Commands/Compilers/Compile-LanguageDefinition.ps1


function Compile.LanguageDefinition {

    <#
    .SYNOPSIS
        Compiles a language definition
    .DESCRIPTION
        Compiles a language definition.
        
        Language definitions integrate languages into PipeScript, so that they can be templated, interpreted, and compiled.
    .NOTES
        Language definitions are an open-ended object.

        By providing key properties or methods, a language can support a variety of scenarios.

        |Scenario|Required Properties|
        |-|-|
        |Templating | `.StartPattern`, `.EndPattern`|
        |Interpretation| `.Interpreter` |

        Language definitions should not contain named blocks.
    .EXAMPLE
        Import-PipeScript {
            language function TestLanguage {
                $AnyVariableInThisBlock = 'Will Become a Property'
            }
        }
    #>

    [Alias('PostProcess-LanguageDefinition')]
    [ValidateScript({
        $validating = $_
        if ($validating -is [Management.Automation.Language.FunctionDefinitionAst]) {
            return $validating.Name -match '^Language'
        }
        return $false
    })]
    [OutputType(
        [ScriptBlock],
        [Management.Automation.Language.FunctionDefinitionAst]
    )]
    param(
    # A Language Definition, as a Script Block
    [Parameter(Mandatory,ParameterSetName='ScriptBlock',ValueFromPipeline)]
    [Alias('ScriptBlock','Definition')]
    [ScriptBlock]
    $LanguageDefinition,

    # A Language Function Definition
    [Parameter(Mandatory,ParameterSetName='FunctionDefinition',ValueFromPipeline)]
    [Management.Automation.Language.FunctionDefinitionAst]
    $LanguageFunctionAst
    )

    begin { $myCmd = $MyInvocation.MyCommand}

    process {
        switch ($PSCmdlet.ParameterSetName) {
            ScriptBlock {
                $newScriptLines = @(                    
                    "`New-Module {"                    
                    " $LanguageDefinition"
                    " Export-ModuleMember -Variable * -Function * -Alias *"
                    "} -AsCustomObject"
                )

                [ScriptBlock]::Create($newScriptLines -join [Environment]::NewLine)
            }
            FunctionDefinition {
                if ($LanguageFunctionAst.Name -notmatch '^Language\p{P}') { return }
                $newScriptLines = @(
                    $languageName = $LanguageFunctionAst.Name -replace '^Language\p{P}'
                    '$this = $myInvocation.MyCommand'
                    'if (-not $this.Self) {'
                    '$languageDefinition = New-Module {'                    
                    " $($LanguageFunctionAst.Body.EndBlock)"
                    " `$LanguageName = '$languageName'"
                    " Export-ModuleMember -Variable * -Function * -Alias *"
                    "} -AsCustomObject"                    
                    '$languageDefinition.pstypenames.clear()'
                    '$languageDefinition.pstypenames.add("Language")'
                    '$languageDefinition.pstypenames.add("Language.' + $languageName + '")'
                    '$this.psobject.properties.add([PSNoteProperty]::new(''Self'',$languageDefinition))'
                    '}'
                    '$this.Self'
                )                
                
                $newFunctionDefinition = @(if ($LanguageFunctionAst.IsFilter) {
                    "filter", $LanguageFunctionAst.Name, '{' -join ' '
                } else {
                    "function", $LanguageFunctionAst.Name, '{' -join ' '
                }
                $blockComments = @([Regex]::New("
\<\# # The opening tag
(?<Block>
    (?:.|\s)+?(?=\z|\#>) # anything until the closing tag
)
\#\> # the closing tag
"
, 'IgnoreCase,IgnorePatternWhitespace', '00:00:01').Matches($LanguageFunctionAst.Body.Extent)) -as [Text.RegularExpressions.Match[]]
                if ($blockComments) {
                    $blockComments[0]                    
                }
                $LanguageFunctionAst.Body.ParamBlock.Attributes -join [Environment]::NewLine
                'param()'
                $newScriptLines
                "}") -join [Environment]::NewLine
                
                [ScriptBlock]::Create($newFunctionDefinition).Ast.EndBlock.Statements[0]
            }            
        }
        
    }

}