Skip to content

Commit 37aa6a1

Browse files
Merge pull request #72 from StartAutomating/MorePipeScript
More pipe script
2 parents d3fbc2c + 003f2c6 commit 37aa6a1

File tree

10 files changed

+229
-25
lines changed

10 files changed

+229
-25
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
## 0.0.3
2-
* Adding Inline Python Support (#63)
3-
* Adding [OutputFile] transpiler (#64)
2+
* New Transpilers:
3+
* .>ValidateExtension (#64)
4+
* .>OutputFile (#53)
5+
* Inline PipeScript Support for New Languages
6+
* Python (#63)
7+
* PHP (#67)
8+
* Razor (#68)
9+
* Bugfixes / improvements:
10+
* Plugged Invoke-PipeScript Parameter Leak (#69)
11+
* .>ValidateTypes transpiler now returns true (#65)
12+
* .>ValidateTypes transpiler now can apply to a [VariableExpressionAST] (#66)
413
* Building PipeScript with PipeScript (#54)
514
---
615

Invoke-PipeScript.ps1

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,10 @@
377377

378378
# Check that the AttributeSyntaxTree is not a real type (return if it is)
379379
$attributeType = $AttributeSyntaxTree.TypeName.GetReflectionType()
380-
if ($attributeType) { return $AttributeSyntaxTree }
380+
if ($attributeType) { return }
381381

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

385385
# Create a collection for stringified arguments.
386386
$stringArguments = @()
@@ -393,53 +393,72 @@
393393
$AttributeSyntaxTree.TypeName.Name
394394
}
395395

396+
# See if we could find a transpiler that fits.
396397
$foundTranspiler =
397398
if ($InputObject) {
398399
Get-Transpiler -TranspilerName "$transpilerStepName" -CouldPipe $InputObject |
399400
Select-Object -ExpandProperty ExtensionCommand
400401
} else {
401402
Get-Transpiler -TranspilerName "$transpilerStepName"
402403
}
403-
404+
405+
# Collect all of the arguments of the attribute, in the order they were specified.
404406
$argsInOrder = @(
405407
@($AttributeSyntaxTree.PositionalArguments) + @($AttributeSyntaxTree.NamedArguments) | Sort-Object { $_.Extent.StartOffset})
406408

409+
410+
# Now we need to map each of those arguments into either named or positional arguments.
407411
foreach ($attributeArg in $argsInOrder) {
412+
# Named arguments are fairly straightforward:
408413
if ($attributeArg -is [Management.Automation.Language.NamedAttributeArgumentAst]) {
409414
$argName = $attributeArg.ArgumentName
410415
$argAst = $attributeArg.Argument
411416
$parameter[$argName] =
412-
if ($argName -eq $argAst) {
413-
$true
414-
} elseif ($argAst.Value) {
415-
$argAst.Value.ToString()
416-
}
417+
if ($argName -eq $argAst) { # If the argument is the name,
418+
$true # treat it as a [switch] parameter.
419+
}
420+
# If the argument value was an ScriptBlockExpression
417421
elseif ($argAst -is [Management.Automation.Language.ScriptBlockExpressionAST]) {
422+
# Turn it into a [ScriptBlock]
418423
$argScriptBlock = [ScriptBlock]::Create($argAst.Extent.ToString() -replace '^\{' -replace '\}$')
424+
# If the Transpiler had a parameter that took a [ScriptBlock] or [ScriptBlock[]]
419425
if ($foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock] -or
420426
$foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock[]]) {
421-
$argScriptBlock
422-
} elseif ($SafeScriptBlockAttributeEvaluation) {
427+
$argScriptBlock # pass the [ScriptBlock] directly.
428+
}
429+
# Now here is where things get a bit more interesting:
430+
# If the parameter type _was not_ a [ScriptBlock], we can evaluate the [ScriptBlock] to create a real value
431+
# If we want to do this "safely", we can pass -SafeScriptBlockAttributeEvaluation
432+
elseif ($SafeScriptBlockAttributeEvaluation) {
433+
# Which will run the [ScriptBlock] inside of a data block, thus preventing it from running commands.
423434
& ([ScriptBlock]::Create("data {$argScriptBlock}"))
424435
} else {
436+
# Otherwise, we want to run the [ScriptBlock] directly.
425437
& ([ScriptBlock]::Create("$argScriptBlock"))
426438
}
427439
}
440+
elseif ($argAst.Value) {
441+
$argAst.Value.ToString()
442+
}
428443
else {
429444
$argAst.Extent.ToString()
430445
}
431446
} else {
432-
foreach ($eachParameter in $foundTranspiler.Parameters.Values) {
433-
$eachParameter
434-
}
447+
# If we are a positional parameter, for the moment:
435448
if ($parameter.Count) {
449+
# add it to the last named parameter.
436450
$parameter[@($parameter.Keys)[-1]] = @() + $parameter[@($parameter.Keys)[-1]] + $attributeArg.Value.ToString()
437451
} else {
452+
# Or add it to the list of string arguments.
438453
$stringArguments += "$($attributeArg.Value)"
439454
}
455+
456+
# We _should_ get more intelligent over time here.
457+
# See [the GitHub Issue](https://github.com/StartAutomating/PipeScript/issues/70) for more details.
440458
}
441459
}
442460

461+
# If we have found a transpiler, run it.
443462
if ($foundTranspiler) {
444463
$ArgumentList += $stringArguments
445464
if ($InputObject) {
@@ -490,11 +509,11 @@
490509
# Determine if the -Command is a real type.
491510
$realType = $Command.TypeName.GetReflectionType()
492511
if ($realType) {
493-
# If it is, return the connstraint unmodified.
494-
return $command
512+
# If it is, return.
513+
return
495514
}
496515
# Next, make sure it's not ```[ordered]``` (return it if is)
497-
if ($command.TypeName.Name -eq 'ordered') { return $command }
516+
if ($command.TypeName.Name -eq 'ordered') { return}
498517

499518
# Determine the name of the transpiler step.
500519
$transpilerStepName =

PipeScript.psd1

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@{
2-
ModuleVersion = '0.0.2'
2+
ModuleVersion = '0.0.3'
33
Description = 'An Extensible Transpiler for PowerShell (and anything else)'
44
RootModule = 'PipeScript.psm1'
55
PowerShellVersion = '4.0'
@@ -18,8 +18,17 @@
1818
Tags = 'PipeScript','PowerShell', 'Transpilation', 'Compiler'
1919
ReleaseNotes = @'
2020
## 0.0.3
21-
* Adding Inline Python Support (#63)
22-
* Adding [OutputFile] transpiler (#64)
21+
* New Transpilers:
22+
* .>ValidateExtension (#64)
23+
* .>OutputFile (#53)
24+
* Inline PipeScript Support for New Languages
25+
* Python (#63)
26+
* PHP (#67)
27+
* Razor (#68)
28+
* Bugfixes / improvements:
29+
* Plugged Invoke-PipeScript Parameter Leak (#69)
30+
* .>ValidateTypes transpiler now returns true (#65)
31+
* .>ValidateTypes transpiler now can apply to a [VariableExpressionAST] (#66)
2332
* Building PipeScript with PipeScript (#54)
2433
---
2534
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<#
2+
.SYNOPSIS
3+
PHP PipeScript Transpiler.
4+
.DESCRIPTION
5+
Transpiles PHP with Inline PipeScript into PHP.
6+
7+
Multiline comments blocks like this ```<!--{}-->``` will be treated as blocks of PipeScript.
8+
9+
JavaScript/CSS/PHP comment blocks like ```/*{}*/``` will also be treated as blocks of PipeScript.
10+
#>
11+
[ValidateScript({
12+
$cmdInfo = $_
13+
if ($cmdInfo.Source -match '\.php$') {
14+
return $true
15+
}
16+
return $false
17+
})]
18+
param(
19+
# The command information. This will include the path to the file.
20+
[Parameter(Mandatory,ValueFromPipeline)]
21+
$CommandInfo
22+
)
23+
24+
begin {
25+
# We start off by declaring a number of regular expressions:
26+
$startComment = '(?><\!--|/\*)' # * Start Comments ```<!--```
27+
$endComment = '(?>-->|\*/)' # * End Comments ```-->```
28+
$Whitespace = '[\s\n\r]{0,}'
29+
# * StartRegex ```$StartComment + '{' + $Whitespace```
30+
$startRegex = "(?<PSStart>${startComment}\{$Whitespace)"
31+
# * EndRegex ```$whitespace + '}' + $EndComment```
32+
$endRegex = "(?<PSEnd>$Whitespace\}${endComment}\s{0,})"
33+
34+
$sourcePattern = [Regex]::New("(?>$(
35+
$startRegex, $endRegex -join ([Environment]::NewLine + '|' + [Environment]::NewLine)
36+
))", "IgnoreCase, IgnorePatternWhitespace", "00:00:05")
37+
}
38+
39+
process {
40+
41+
$fileInfo = $commandInfo.Source -as [IO.FileInfo]
42+
$fileText = [IO.File]::ReadAllText($fileInfo.Fullname)
43+
44+
.>PipeScript.Inline -SourceFile $CommandInfo.Source -SourceText $fileText -SourcePattern $sourcePattern
45+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<#
2+
.SYNOPSIS
3+
Razor Inline PipeScript Transpiler.
4+
.DESCRIPTION
5+
Transpiles Razor with Inline PipeScript into Razor.
6+
7+
Multiline comments blocks like this ```<!--{}-->``` will be treated as blocks of PipeScript.
8+
9+
JavaScript/CSS comment blocks like ```/*{}*/``` will also be treated as blocks of PipeScript.
10+
11+
Razor comment blocks like ```@*{}*@``` will also be treated as blocks of PipeScript.
12+
#>
13+
[ValidateScript({
14+
$cmdInfo = $_
15+
if ($cmdInfo.Source -match '\.(cshtml|razor)$') {
16+
return $true
17+
}
18+
return $false
19+
})]
20+
param(
21+
# The command information. This will include the path to the file.
22+
[Parameter(Mandatory,ValueFromPipeline)]
23+
$CommandInfo
24+
)
25+
26+
begin {
27+
# We start off by declaring a number of regular expressions:
28+
$startComment = '(?><\!--|/\*|\@\*)'
29+
$endComment = '(?>-->|\*/|\*@)'
30+
$Whitespace = '[\s\n\r]{0,}'
31+
# * StartRegex ```$StartComment + '{' + $Whitespace```
32+
$startRegex = "(?<PSStart>${startComment}\{$Whitespace)"
33+
# * EndRegex ```$whitespace + '}' + $EndComment```
34+
$endRegex = "(?<PSEnd>$Whitespace\}${endComment}\s{0,})"
35+
36+
$sourcePattern = [Regex]::New("(?>$(
37+
$startRegex, $endRegex -join ([Environment]::NewLine + '|' + [Environment]::NewLine)
38+
))", "IgnoreCase, IgnorePatternWhitespace", "00:00:05")
39+
}
40+
41+
process {
42+
43+
$fileInfo = $commandInfo.Source -as [IO.FileInfo]
44+
$fileText = [IO.File]::ReadAllText($fileInfo.Fullname)
45+
46+
.>PipeScript.Inline -SourceFile $CommandInfo.Source -SourceText $fileText -SourcePattern $sourcePattern
47+
}

Transpilers/Inline/Inline.TypeScript.psx.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#>
2020
[ValidateScript({
2121
$cmdInfo = $_
22-
if ($cmdInfo.Source -match '\.ts)$') {
22+
if ($cmdInfo.Source -match '\.ts$') {
2323
return $true
2424
}
2525
return $false

Transpilers/Inline/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
This directory contains Inline PipeScript transpilers for several languages.
22

3-
PipeScript can currently be embedded in 17 languages.
3+
PipeScript can currently be embedded in 19 languages.
44

55
Transpilers in this directory should be named ```Inline.NameOfLanguage.psx.ps1```.
66
Each file should handle one and only one language (better explicit than terse).
@@ -21,7 +21,9 @@ Transpilers should call ```.>PipeScript.Inline``` to simplify and standarize pro
2121
|[Markdown](Inline.Markdown.psx.ps1) |[Markdown File Transpiler.](Inline.Markdown.psx.ps1) |
2222
|[ObjectiveC](Inline.ObjectiveC.psx.ps1)|[Objective C PipeScript Transpiler.](Inline.ObjectiveC.psx.ps1) |
2323
|[OpenSCAD](Inline.OpenSCAD.psx.ps1) |[OpenSCAD Inline PipeScript Transpiler.](Inline.OpenSCAD.psx.ps1) |
24+
|[PHP](Inline.PHP.psx.ps1) |[PHP PipeScript Transpiler.](Inline.PHP.psx.ps1) |
2425
|[Python](Inline.Python.psx.ps1) |[Python Inline PipeScript Transpiler.](Inline.Python.psx.ps1) |
26+
|[Razor](Inline.Razor.psx.ps1) |[Razor Inline PipeScript Transpiler.](Inline.Razor.psx.ps1) |
2527
|[Ruby](Inline.Ruby.psx.ps1) |[Ruby Inline PipeScript Transpiler.](Inline.Ruby.psx.ps1) |
2628
|[Rust](Inline.Rust.psx.ps1) |[Rust Inline PipeScript Transpiler.](Inline.Rust.psx.ps1) |
2729
|[TOML](Inline.TOML.psx.ps1) |[TOML Inline PipeScript Transpiler.](Inline.TOML.psx.ps1) |

Transpilers/Parameters/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ When this is the case it is common for the transpiler to add a ```[ValidateScrip
1717

1818
|DisplayName |Synopsis |
1919
|----------------------------------------------------|---------------------------------------------------------------------|
20+
|[ValidateExtension](ValidateExtension.psx.ps1) |[Validates Extensions](ValidateExtension.psx.ps1) |
2021
|[ValidatePlatform](ValidatePlatform.psx.ps1) |[Validates the Platform](ValidatePlatform.psx.ps1) |
2122
|[ValidatePropertyName](ValidatePropertyName.psx.ps1)|[Validates Property Names](ValidatePropertyName.psx.ps1) |
2223
|[ValidateTypes](ValidateTypes.psx.ps1) |[Validates if an object is one or more types.](ValidateTypes.psx.ps1)|
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<#
2+
.SYNOPSIS
3+
Validates Extensions
4+
.DESCRIPTION
5+
Validates that a parameter or object has one or more extensions.
6+
7+
This creates a [ValidatePattern] that will ensure the extension matches.
8+
.Example
9+
{
10+
param(
11+
[ValidateExtension(Extension=".cs", ".ps1")]
12+
$FilePath
13+
)
14+
} |.>PipeScript
15+
.Example
16+
.> {
17+
param(
18+
[ValidateExtension(Extension=".cs", ".ps1")]
19+
$FilePath
20+
)
21+
22+
$FilePath
23+
} -Parameter @{FilePath=".ps1"}
24+
.EXAMPLE
25+
.> {
26+
param(
27+
[ValidateExtension(Extension=".cs", ".ps1")]
28+
$FilePath
29+
)
30+
31+
$FilePath
32+
} -Parameter @{FilePath="foo.txt"}
33+
#>
34+
[CmdletBinding(DefaultParameterSetName='Parameter')]
35+
param(
36+
# The extensions being validated.
37+
[Parameter(Mandatory,Position=0)]
38+
[string[]]
39+
$Extension,
40+
41+
# A variable expression.
42+
# If this is provided, will apply a ```[ValidatePattern({})]``` attribute to the variable, constraining future values.
43+
[Parameter(ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
44+
[Management.Automation.Language.VariableExpressionAST]
45+
$VariableAST
46+
)
47+
48+
process {
49+
$validExtensionRegex = "\.(?>$($extension -replace '^\.' -join "|"))$"
50+
[ScriptBlock]::Create(@"
51+
[ValidatePattern('$($validExtensionRegex.Replace("'","''"))')]
52+
$(
53+
if ($psCmdlet.ParameterSetName -eq 'Parameter') {
54+
'param()'
55+
} else {
56+
'$' + $VariableAST.variablePath.ToString()
57+
}
58+
)
59+
"@)
60+
}

Transpilers/Parameters/ValidateTypes.psx.ps1

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ param(
4242
# TypeNames will be treated first as real types, then as exact matches, then as wildcards, and then as regular expressions.
4343
[Parameter(Mandatory,Position=0)]
4444
[string[]]
45-
$TypeName
45+
$TypeName,
46+
47+
# The variable that will be validated.
48+
[Parameter(ValueFromPipeline,ParameterSetName='VariableExpressionAST')]
49+
[Management.Automation.Language.VariableExpressionAST]
50+
$VariableAST
4651
)
4752

4853

@@ -90,7 +95,14 @@ $checkTypes
9095
if (-not `$isTypeOk) {
9196
throw "Unexpected type '`$(@(`$thisType)[0])'. Must be '$($typeName -join "','")'."
9297
}
98+
return `$true
9399
})]
94-
param()
100+
$(
101+
if ($psCmdlet.ParameterSetName -eq 'Parameter') {
102+
'param()'
103+
} else {
104+
'$' + $VariableAST.variablePath.ToString()
105+
}
106+
)
95107
"@)
96108
}

0 commit comments

Comments
 (0)