Skip to content
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
## 0.0.3
* Adding Inline Python Support (#63)
* Adding [OutputFile] transpiler (#64)
* New Transpilers:
* .>ValidateExtension (#64)
* .>OutputFile (#53)
* Inline PipeScript Support for New Languages
* Python (#63)
* PHP (#67)
* Razor (#68)
* Bugfixes / improvements:
* Plugged Invoke-PipeScript Parameter Leak (#69)
* .>ValidateTypes transpiler now returns true (#65)
* .>ValidateTypes transpiler now can apply to a [VariableExpressionAST] (#66)
* Building PipeScript with PipeScript (#54)
---

Expand Down
51 changes: 35 additions & 16 deletions Invoke-PipeScript.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,10 @@

# Check that the AttributeSyntaxTree is not a real type (return if it is)
$attributeType = $AttributeSyntaxTree.TypeName.GetReflectionType()
if ($attributeType) { return $AttributeSyntaxTree }
if ($attributeType) { return }

# Check that the typename is not [Ordered] (return if it is).
if ($AttributeSyntaxTree.TypeName.Name -eq 'ordered') { return $AttributeSyntaxTree }
if ($AttributeSyntaxTree.TypeName.Name -eq 'ordered') { return }

# Create a collection for stringified arguments.
$stringArguments = @()
Expand All @@ -393,53 +393,72 @@
$AttributeSyntaxTree.TypeName.Name
}

# See if we could find a transpiler that fits.
$foundTranspiler =
if ($InputObject) {
Get-Transpiler -TranspilerName "$transpilerStepName" -CouldPipe $InputObject |
Select-Object -ExpandProperty ExtensionCommand
} else {
Get-Transpiler -TranspilerName "$transpilerStepName"
}


# Collect all of the arguments of the attribute, in the order they were specified.
$argsInOrder = @(
@($AttributeSyntaxTree.PositionalArguments) + @($AttributeSyntaxTree.NamedArguments) | Sort-Object { $_.Extent.StartOffset})


# Now we need to map each of those arguments into either named or positional arguments.
foreach ($attributeArg in $argsInOrder) {
# Named arguments are fairly straightforward:
if ($attributeArg -is [Management.Automation.Language.NamedAttributeArgumentAst]) {
$argName = $attributeArg.ArgumentName
$argAst = $attributeArg.Argument
$parameter[$argName] =
if ($argName -eq $argAst) {
$true
} elseif ($argAst.Value) {
$argAst.Value.ToString()
}
if ($argName -eq $argAst) { # If the argument is the name,
$true # treat it as a [switch] parameter.
}
# If the argument value was an ScriptBlockExpression
elseif ($argAst -is [Management.Automation.Language.ScriptBlockExpressionAST]) {
# Turn it into a [ScriptBlock]
$argScriptBlock = [ScriptBlock]::Create($argAst.Extent.ToString() -replace '^\{' -replace '\}$')
# If the Transpiler had a parameter that took a [ScriptBlock] or [ScriptBlock[]]
if ($foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock] -or
$foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock[]]) {
$argScriptBlock
} elseif ($SafeScriptBlockAttributeEvaluation) {
$argScriptBlock # pass the [ScriptBlock] directly.
}
# Now here is where things get a bit more interesting:
# If the parameter type _was not_ a [ScriptBlock], we can evaluate the [ScriptBlock] to create a real value
# If we want to do this "safely", we can pass -SafeScriptBlockAttributeEvaluation
elseif ($SafeScriptBlockAttributeEvaluation) {
# Which will run the [ScriptBlock] inside of a data block, thus preventing it from running commands.
& ([ScriptBlock]::Create("data {$argScriptBlock}"))
} else {
# Otherwise, we want to run the [ScriptBlock] directly.
& ([ScriptBlock]::Create("$argScriptBlock"))
}
}
elseif ($argAst.Value) {
$argAst.Value.ToString()
}
else {
$argAst.Extent.ToString()
}
} else {
foreach ($eachParameter in $foundTranspiler.Parameters.Values) {
$eachParameter
}
# If we are a positional parameter, for the moment:
if ($parameter.Count) {
# add it to the last named parameter.
$parameter[@($parameter.Keys)[-1]] = @() + $parameter[@($parameter.Keys)[-1]] + $attributeArg.Value.ToString()
} else {
# Or add it to the list of string arguments.
$stringArguments += "$($attributeArg.Value)"
}

# We _should_ get more intelligent over time here.
# See [the GitHub Issue](https://github.com/StartAutomating/PipeScript/issues/70) for more details.
}
}

# If we have found a transpiler, run it.
if ($foundTranspiler) {
$ArgumentList += $stringArguments
if ($InputObject) {
Expand Down Expand Up @@ -490,11 +509,11 @@
# Determine if the -Command is a real type.
$realType = $Command.TypeName.GetReflectionType()
if ($realType) {
# If it is, return the connstraint unmodified.
return $command
# If it is, return.
return
}
# Next, make sure it's not ```[ordered]``` (return it if is)
if ($command.TypeName.Name -eq 'ordered') { return $command }
if ($command.TypeName.Name -eq 'ordered') { return}

# Determine the name of the transpiler step.
$transpilerStepName =
Expand Down
15 changes: 12 additions & 3 deletions PipeScript.psd1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@{
ModuleVersion = '0.0.2'
ModuleVersion = '0.0.3'
Description = 'An Extensible Transpiler for PowerShell (and anything else)'
RootModule = 'PipeScript.psm1'
PowerShellVersion = '4.0'
Expand All @@ -18,8 +18,17 @@
Tags = 'PipeScript','PowerShell', 'Transpilation', 'Compiler'
ReleaseNotes = @'
## 0.0.3
* Adding Inline Python Support (#63)
* Adding [OutputFile] transpiler (#64)
* New Transpilers:
* .>ValidateExtension (#64)
* .>OutputFile (#53)
* Inline PipeScript Support for New Languages
* Python (#63)
* PHP (#67)
* Razor (#68)
* Bugfixes / improvements:
* Plugged Invoke-PipeScript Parameter Leak (#69)
* .>ValidateTypes transpiler now returns true (#65)
* .>ValidateTypes transpiler now can apply to a [VariableExpressionAST] (#66)
* Building PipeScript with PipeScript (#54)
---

Expand Down
45 changes: 45 additions & 0 deletions Transpilers/Inline/Inline.PHP.psx.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<#
.SYNOPSIS
PHP PipeScript Transpiler.
.DESCRIPTION
Transpiles PHP with Inline PipeScript into PHP.

Multiline comments blocks like this ```<!--{}-->``` will be treated as blocks of PipeScript.

JavaScript/CSS/PHP comment blocks like ```/*{}*/``` will also be treated as blocks of PipeScript.
#>
[ValidateScript({
$cmdInfo = $_
if ($cmdInfo.Source -match '\.php$') {
return $true
}
return $false
})]
param(
# The command information. This will include the path to the file.
[Parameter(Mandatory,ValueFromPipeline)]
$CommandInfo
)

begin {
# We start off by declaring a number of regular expressions:
$startComment = '(?><\!--|/\*)' # * Start Comments ```<!--```
$endComment = '(?>-->|\*/)' # * End Comments ```-->```
$Whitespace = '[\s\n\r]{0,}'
# * StartRegex ```$StartComment + '{' + $Whitespace```
$startRegex = "(?<PSStart>${startComment}\{$Whitespace)"
# * EndRegex ```$whitespace + '}' + $EndComment```
$endRegex = "(?<PSEnd>$Whitespace\}${endComment}\s{0,})"

$sourcePattern = [Regex]::New("(?>$(
$startRegex, $endRegex -join ([Environment]::NewLine + '|' + [Environment]::NewLine)
))", "IgnoreCase, IgnorePatternWhitespace", "00:00:05")
}

process {

$fileInfo = $commandInfo.Source -as [IO.FileInfo]
$fileText = [IO.File]::ReadAllText($fileInfo.Fullname)

.>PipeScript.Inline -SourceFile $CommandInfo.Source -SourceText $fileText -SourcePattern $sourcePattern
}
47 changes: 47 additions & 0 deletions Transpilers/Inline/Inline.Razor.psx.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<#
.SYNOPSIS
Razor Inline PipeScript Transpiler.
.DESCRIPTION
Transpiles Razor with Inline PipeScript into Razor.

Multiline comments blocks like this ```<!--{}-->``` will be treated as blocks of PipeScript.

JavaScript/CSS comment blocks like ```/*{}*/``` will also be treated as blocks of PipeScript.

Razor comment blocks like ```@*{}*@``` will also be treated as blocks of PipeScript.
#>
[ValidateScript({
$cmdInfo = $_
if ($cmdInfo.Source -match '\.(cshtml|razor)$') {
return $true
}
return $false
})]
param(
# The command information. This will include the path to the file.
[Parameter(Mandatory,ValueFromPipeline)]
$CommandInfo
)

begin {
# We start off by declaring a number of regular expressions:
$startComment = '(?><\!--|/\*|\@\*)'
$endComment = '(?>-->|\*/|\*@)'
$Whitespace = '[\s\n\r]{0,}'
# * StartRegex ```$StartComment + '{' + $Whitespace```
$startRegex = "(?<PSStart>${startComment}\{$Whitespace)"
# * EndRegex ```$whitespace + '}' + $EndComment```
$endRegex = "(?<PSEnd>$Whitespace\}${endComment}\s{0,})"

$sourcePattern = [Regex]::New("(?>$(
$startRegex, $endRegex -join ([Environment]::NewLine + '|' + [Environment]::NewLine)
))", "IgnoreCase, IgnorePatternWhitespace", "00:00:05")
}

process {

$fileInfo = $commandInfo.Source -as [IO.FileInfo]
$fileText = [IO.File]::ReadAllText($fileInfo.Fullname)

.>PipeScript.Inline -SourceFile $CommandInfo.Source -SourceText $fileText -SourcePattern $sourcePattern
}
2 changes: 1 addition & 1 deletion Transpilers/Inline/Inline.TypeScript.psx.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#>
[ValidateScript({
$cmdInfo = $_
if ($cmdInfo.Source -match '\.ts)$') {
if ($cmdInfo.Source -match '\.ts$') {
return $true
}
return $false
Expand Down
4 changes: 3 additions & 1 deletion Transpilers/Inline/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
This directory contains Inline PipeScript transpilers for several languages.

PipeScript can currently be embedded in 17 languages.
PipeScript can currently be embedded in 19 languages.

Transpilers in this directory should be named ```Inline.NameOfLanguage.psx.ps1```.
Each file should handle one and only one language (better explicit than terse).
Expand All @@ -21,7 +21,9 @@ Transpilers should call ```.>PipeScript.Inline``` to simplify and standarize pro
|[Markdown](Inline.Markdown.psx.ps1) |[Markdown File Transpiler.](Inline.Markdown.psx.ps1) |
|[ObjectiveC](Inline.ObjectiveC.psx.ps1)|[Objective C PipeScript Transpiler.](Inline.ObjectiveC.psx.ps1) |
|[OpenSCAD](Inline.OpenSCAD.psx.ps1) |[OpenSCAD Inline PipeScript Transpiler.](Inline.OpenSCAD.psx.ps1) |
|[PHP](Inline.PHP.psx.ps1) |[PHP PipeScript Transpiler.](Inline.PHP.psx.ps1) |
|[Python](Inline.Python.psx.ps1) |[Python Inline PipeScript Transpiler.](Inline.Python.psx.ps1) |
|[Razor](Inline.Razor.psx.ps1) |[Razor Inline PipeScript Transpiler.](Inline.Razor.psx.ps1) |
|[Ruby](Inline.Ruby.psx.ps1) |[Ruby Inline PipeScript Transpiler.](Inline.Ruby.psx.ps1) |
|[Rust](Inline.Rust.psx.ps1) |[Rust Inline PipeScript Transpiler.](Inline.Rust.psx.ps1) |
|[TOML](Inline.TOML.psx.ps1) |[TOML Inline PipeScript Transpiler.](Inline.TOML.psx.ps1) |
Expand Down
1 change: 1 addition & 0 deletions Transpilers/Parameters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ When this is the case it is common for the transpiler to add a ```[ValidateScrip

|DisplayName |Synopsis |
|----------------------------------------------------|---------------------------------------------------------------------|
|[ValidateExtension](ValidateExtension.psx.ps1) |[Validates Extensions](ValidateExtension.psx.ps1) |
|[ValidatePlatform](ValidatePlatform.psx.ps1) |[Validates the Platform](ValidatePlatform.psx.ps1) |
|[ValidatePropertyName](ValidatePropertyName.psx.ps1)|[Validates Property Names](ValidatePropertyName.psx.ps1) |
|[ValidateTypes](ValidateTypes.psx.ps1) |[Validates if an object is one or more types.](ValidateTypes.psx.ps1)|
Expand Down
60 changes: 60 additions & 0 deletions Transpilers/Parameters/ValidateExtension.psx.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<#
.SYNOPSIS
Validates Extensions
.DESCRIPTION
Validates that a parameter or object has one or more extensions.

This creates a [ValidatePattern] that will ensure the extension matches.
.Example
{
param(
[ValidateExtension(Extension=".cs", ".ps1")]
$FilePath
)
} |.>PipeScript
.Example
.> {
param(
[ValidateExtension(Extension=".cs", ".ps1")]
$FilePath
)

$FilePath
} -Parameter @{FilePath=".ps1"}
.EXAMPLE
.> {
param(
[ValidateExtension(Extension=".cs", ".ps1")]
$FilePath
)

$FilePath
} -Parameter @{FilePath="foo.txt"}
#>
[CmdletBinding(DefaultParameterSetName='Parameter')]
param(
# The extensions being validated.
[Parameter(Mandatory,Position=0)]
[string[]]
$Extension,

# A variable expression.
# If this is provided, will apply a ```[ValidatePattern({})]``` attribute to the variable, constraining future values.
[Parameter(ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
[Management.Automation.Language.VariableExpressionAST]
$VariableAST
)

process {
$validExtensionRegex = "\.(?>$($extension -replace '^\.' -join "|"))$"
[ScriptBlock]::Create(@"
[ValidatePattern('$($validExtensionRegex.Replace("'","''"))')]
$(
if ($psCmdlet.ParameterSetName -eq 'Parameter') {
'param()'
} else {
'$' + $VariableAST.variablePath.ToString()
}
)
"@)
}
16 changes: 14 additions & 2 deletions Transpilers/Parameters/ValidateTypes.psx.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ param(
# TypeNames will be treated first as real types, then as exact matches, then as wildcards, and then as regular expressions.
[Parameter(Mandatory,Position=0)]
[string[]]
$TypeName
$TypeName,

# The variable that will be validated.
[Parameter(ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
[Management.Automation.Language.VariableExpressionAST]
$VariableAST
)


Expand Down Expand Up @@ -90,7 +95,14 @@ $checkTypes
if (-not `$isTypeOk) {
throw "Unexpected type '`$(@(`$thisType)[0])'. Must be '$($typeName -join "','")'."
}
return `$true
})]
param()
$(
if ($psCmdlet.ParameterSetName -eq 'Parameter') {
'param()'
} else {
'$' + $VariableAST.variablePath.ToString()
}
)
"@)
}