experiments/loader.psm1
using module "./core.psm1" function New-CompiledModule($Path) { <# .SYNOPSIS Transpile and instantiate a module. Pipe to Import-Module to bring exports into scope. #> $sourceText = get-content -raw $Path $sb = [scriptblock]::create($sourceText) $ast = $sb.ast # TODO Pass through transformer stack $replacements = [Replacements]::new() foreach($statement in $ast.endblock.statements) { if($statement -is [Management.Automation.Language.FunctionDefinitionAst]) { $transformed = transformFunctionAst $statement $replacements.add($statement, $transformed.extent.text) } } $sourceText = $replacements.apply($ast) # TODO all current transformers assume a FunctionDeclarationAst # Allow transformers to have a *type* so we can invoke them with whole script # or only with function declaration. # Compile and execute as a new module $finalAst = [Management.Automation.Language.Parser]::ParseInput($sourceText, $Path, [ref]@(), [ref]@()) $moduleScriptBlock = $finalAst.getScriptBlock() $Name = Split-Path -LeafBase $Path $module = New-Module -Name $Name -ScriptBlock $moduleScriptBlock $module } $transformers = @( # Catch early limitations 'validate', # Normalize syntax; makes subsequent transformers easier to write because # they don't have to deal with as many corner-cases 'parametersToParamBlock', 'defaultBlockIsProcess', 'createEmptyBlocks', # Additional transformations and behaviors 'swallowBreaks', 'blocksWrappedInTryCatch', 'expandAliases' ) pushd "$PSScriptRoot/.." try { $transformerFunctions = . { foreach($t in $transformers) { import-module -force ./transformers/$t.psm1 get-command $t } } } finally { popd } function transformFunctionAst($ast) { $result = $ast foreach($transformer in $transformerFunctions) { # write-host "Applying transformation $transformer" try { $result = transform $result $transformer } catch { throw $_ } # write-host $result } $result } |