Skip to content

Commit

Permalink
* Adding new expressions
Browse files Browse the repository at this point in the history
** EmailAddress
** MultiLineComment (Generator)
* Use-Regex will now add the generator name as a capture name
* Use-Regex will not handle errors running the RegEx, and attach the input object
* Adding a reference document for saved regular expressions
* Adding an example to Get-RegEx to generate that document
* Fixing the example of an email address regex in Write-Regex
  • Loading branch information
StartAutomating committed Sep 18, 2019
1 parent ec3491d commit f7a9bb1
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 49 deletions.
9 changes: 9 additions & 0 deletions Get-RegEx.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
Get-RegEx
.Example
Get-RegEx -Name NextWord
.Example
Get-RegEx | # Gets all saved Regular Expressions as a Markdown table
Sort-Object Name |
ForEach-Object -Begin {
'|Name|Description|IsGenerator|'
'|:---|:----------|:----------|'
} -Process {
"|$($_.Name)|$($_.Description)|$($_.IsGenerator)|"
}
.Link
Use-RegEx
.Link
Expand Down
6 changes: 3 additions & 3 deletions Irregular.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ describe Use-Regex {

it 'Can use a dynamic generator' {
$rx = Use-RegEx -Generator {param($t) "$t"} -Parameter @{t='hi'}
"$rx"| should be hi
"$rx"| should belike *hi*
}

}
Expand Down Expand Up @@ -543,11 +543,11 @@ describe Write-Regex {
it 'Can refer to a capture generator (parameters can be passed with () or {})' {
Write-RegEx -Pattern '?<BalancedCode>{(}' |
Use-RegEx -IsMatch -Match '({}' |
should be false
should be $false

Write-RegEx -Pattern '?<BalancedCode>({)' |
Use-RegEx -IsMatch -Match '({}' |
should be true
should be $true
}
}

Expand Down
3 changes: 2 additions & 1 deletion RegEx/Colon.RegEx.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
:
# Matches a colon
:
2 changes: 2 additions & 0 deletions RegEx/EmailAddress.regex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# An email address (captures the UserName and Domain)
(?<UserName>\w{1,1}[\w\.\-]{0,})(?:\@)(?<Domain>\w[\w\-]{0,}\.\w{1,})
4 changes: 2 additions & 2 deletions RegEx/GetMarkupTag.regex.ps1
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<#
.Synopsis
Gets markup tags
.Description
Gets a balanced markup tag
Gets one or more specific markup tags. By default, anchor tags.
#>
param(
[Parameter(ValueFromRemainingArguments=$true)]
Expand Down
3 changes: 2 additions & 1 deletion RegEx/LineStartOrEnd.regex.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
^|$
# This will match either a line start or end.
^|$
66 changes: 66 additions & 0 deletions RegEx/MultilineComment.regex.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<#
.Synopsis
Matches Multiline Comments
.Description
Matches Multline Comments from a variety of languages.
Currently supported: PowerShell, C#, C++, JavaScript, Ruby, HTML, and XML
When this generator is used with a piped in file, the extension will autodetect the format.
If the format could not be autodetected, the match will always fail.
#>
param(
[ValidateSet('PowerShell', 'C#', 'C++', 'C', 'JavaScript', 'Ruby', 'HTML', 'XML','')]
[string]
$Language = 'C'
)

if ($inputObject -and $inputObject -is [IO.FileInfo]) {
$Language =
if ('.h', '.cpp', '.c', '.cs', '.js' -contains $inputObject.Extension) {
'C'
} elseif ('.ps1', '.psm1', '.psd1' -contains $inputObject.Extension) {
'PowerShell'
} elseif ('.htm', '.html', '.xml', '.pswt' -contains $inputObject.Extension -or
$inputObject.Extension -like '.*xml') {
'XML'
}
}

if (-not $PSBoundParameters.Language -and -not $Language) {
return
}


switch ($Language) {
PowerShell {
@'
\<\# # The opening tag
(?<Block>
(.|\s)+?(?=\#>) # anything until the closing tag
)
\#\> # the closing tag
'@
}
{$_ -match '(C\#)|(C\+\+)|(C)|(JavaScript)'} {
@'
/\* # The open comment
(?<Block>
(.|\s)+?(?=\*/)
)\*/
'@
}
Ruby {
@'
^=begin # begin line
(?<Block>(.|\s)+?(?=\=end)) # anything that's not =end
=end # end line
'@
}
{ $_ -match 'HTML|XML' } {
@'
<\!--
(?<Block>(.|\s)+?(?=-->))
-->
'@
}
}
2 changes: 1 addition & 1 deletion RegEx/NewLine.regex.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# A newline
# A newline (in either Windows (\r\n) or Unix (\n) form)
(?>\r\n|\n)
2 changes: 1 addition & 1 deletion RegEx/NextWord.regex.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Finds all text until the next word boundary
# Finds all repeated text that is not whitespace or punctuation.
[\S-[\p{P}]]+
3 changes: 2 additions & 1 deletion RegEx/NumberAndExponent.regex.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
(?<Number>?<Decimals>)
# Matches a decimal number with an exponent.
(?<Number>?<Decimals>)
(?:\^)
(?<Exponent>?<Decimals>)
3 changes: 2 additions & 1 deletion RegEx/OptionalWhitespace.regex.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
\s{0,}
# This matches zero or more whitespace characters
\s{0,}
3 changes: 2 additions & 1 deletion RegEx/Punctuation.regex.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
\p{P}+
# Matches any single or repeated punctuation.
\p{P}+
3 changes: 2 additions & 1 deletion RegEx/Tag.regex.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
\< # The tag Start
# This will match a balanced markup tag.
\< # The tag Start
(?<TagName>\w+) # The name of the Tag is any non number of non-word characters
(?<TagAttributes> # It's attributes are
.+? # anything until
Expand Down
3 changes: 2 additions & 1 deletion RegEx/TrueOrFalse.regex.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
true|false
# Matches the literal 'true' or 'false'
true|false
40 changes: 40 additions & 0 deletions SavedPatterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
|Name|Description|IsGenerator|
|:---|:----------|:----------|
|ArithmeticOperator|Simple Arithmetic Operators|False|
|BalancedBrackets|Matches content in brackets, as long as it is balanced|False|
|BalancedCode|Matches code balanced by a [, {, or (
|True|
|BalancedCurlyBracket|Matches content in {}, as long as it is balanced|False|
|BalancedParenthesis|Matches content in parenthesis, as long as it is balanced|False|
|Colon|Matches a colon|False|
|ColonOrEqual|Matches either a colon or an equal sign.|False|
|Decimals|Matching any series of decimals is deceptively complicated|False|
|Digits|Repeated Digits|False|
|EmailAddress|An email address (captures the UserName and Domain)|False|
|GenericBalancingExpression|This expression matches content that is within "balanced" punctuation.
It does not validate that each type of open/close punctuation is valid.
Just that it any open punctuation is matched by closed punctuation.|False|
|GetMarkupTag|Gets one or more specific markup tags. By default, anchor tags.
|True|
|JSONProperty|A property within a JSON string|False|
|LineEndsWithColon|This returns lines that end with a colon|False|
|LineStartOrEnd|This will match either a line start or end.|False|
|MultilineComment|Matches Multline Comments from a variety of languages.
Currently supported: PowerShell, C#, C++, JavaScript, Ruby, HTML, and XML
When this generator is used with a piped in file, the extension will autodetect the format.
If the format could not be autodetected, the match will always fail.
|True|
|NewLine|A newline (in either Windows (\r\n) or Unix (\n) form)|False|
|NextColon|This returns the position of the next colon, but not the next colon itself.|False|
|NextWord|Finds all repeated text that is not whitespace or punctuation.|False|
|NumberAndExponent|Matches a decimal number with an exponent.|False|
|OptionalWhitespace|This matches zero or more whitespace characters|False|
|PowerShellAttributeKeyValuePair|This expression extracts the key/value pairs from a PowerShell attribute body (the content within parenthesis)|False|
|PowerShellDoubleQuoteString|A PowerShell double-quoted string|False|
|PowerShellInlineHelpField|Matches specific fields from inline help
|True|
|PowerShellSingleQuoteString|Matches a single quoted string, escaped by double single-quotes|False|
|Punctuation|Matches any single or repeated punctuation.|False|
|StartsWithCapture|Matches if the string starts with a named capture, and captures the FirstCaptureName|False|
|Tag|This will match a balanced markup tag.|False|
|TrueOrFalse|Matches the literal 'true' or 'false'|False|
73 changes: 41 additions & 32 deletions Use-RegEx.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Use-RegEx is normally called with an alias that is the name of a saved RegEx, for example:
?<TrueOrFalse>
?<Digits>
.Link
Get-RegEx
.Link
Expand Down Expand Up @@ -238,22 +238,25 @@
#region [ScriptBlock]$FilterMatches
$FilterMatches =
{ process {
$m = $_
if ($_ -is [Boolean] -or $_ -is [string]) { return $_ }
$currentMatch = $_
$MatchMetaData = [Ordered]@{
StartIndex = $_.Index
EndIndex = $_.Index + $_.Length
Input = $_.Result('$_')
}
if ($isExtracting -or $Where) {
$xm = $_ | & $extractMatch
$xm = $currentMatch | & $extractMatch
}
if ($where) {
$in = $this = $_ = $xm
$IsThere = & $where $in
$this = $_ = $xm
$IsThere = . $where $in
if (-not $IsThere) { return }
$_ = $m
$_ = $currentMatch
}

if ($transform) {

return . $decorateString $m.Result($transform) ([Ordered]@{
StartIndex = $m.Index ; EndIndex = $m.Index + $m.Length
Input = $m.Result('$_');
})
return . $decorateString $currentMatch.Result($transform) $matchMetaData
}
if ($if.Count) {
$in = $_ = $xm
Expand All @@ -262,12 +265,9 @@
if ($ifResult) {
if ($ifCondition.Value -is [ScriptBlock]) {
$_ = $xm
& $ifCondition.Value $in
. $ifCondition.Value $in
} elseif ($ifCondition.Value -is [string]) {
. $decorateString $m.Result($ifCondition.Value) ([Ordered]@{
StartIndex = $m.Index ; EndIndex = $m.Index + $m.Length
Input = $m.Result('$_');
})
. $decorateString $currentMatch.Result($ifCondition.Value) $matchMetaData
} else {
$ifCondition.Value
}
Expand All @@ -278,24 +278,24 @@
if ($isextracting) {
return $xm
}
if ($m.psobject.properties['EndIndex'] -isnot [PSScriptProperty]) { # add on two script properties we might want:
$m.psobject.properties.Remove('EndIndex') # EndIndex
$m.psobject.properties.add([PSScriptProperty]::new('EndIndex', { $this.Index + $this.Length }))
if ($currentMatch.psobject.properties['EndIndex'] -isnot [PSScriptProperty]) { # add on two script properties we might want:
$currentMatch.psobject.properties.Remove('EndIndex') # EndIndex
$currentMatch.psobject.properties.add([PSScriptProperty]::new('EndIndex', { $this.Index + $this.Length }))
}
if ($m.psobject.properties['Input'] -isnot [PSScriptProperty]) {
$m.psobject.properties.Remove('Input')
$m.psobject.properties.add([PSScriptProperty]::new('Input', { $this.Result('$_') })) # and Input.
if ($currentMatch.psobject.properties['Input'] -isnot [PSScriptProperty]) {
$currentMatch.psobject.properties.Remove('Input')
$currentMatch.psobject.properties.add([PSScriptProperty]::new('Input', { $this.Result('$_') })) # and Input.
}

if ($inputObject -and $inputObject -ne $m.Input) {
$m.psobject.Properties.Remove('InputObject')
$m.psobject.properties.add([PSNoteProperty]::new('InputObject', $inputObject))
if ($inputObject -and $inputObject -ne $currentMatch.Input) {
$currentMatch.psobject.Properties.Remove('InputObject')
$currentMatch.psobject.properties.add([PSNoteProperty]::new('InputObject', $inputObject))
} else {
$m.psobject.Properties.Remove('InputObject')
$m.psobject.properties.add([PSAliasProperty]::new('InputObject', 'Input'))
$currentMatch.psobject.Properties.Remove('InputObject')
$currentMatch.psobject.properties.add([PSAliasProperty]::new('InputObject', 'Input'))
}

return $m
return $currentMatch
} }
#endregion [ScriptBlock]$FilterMatches

Expand Down Expand Up @@ -369,6 +369,9 @@

if ($Generator) { # (or one was provided)
$regex = & $Generator @argumentList @Parameter # run the generator.
if ($regex -and $mySafeNAme -and -not "$regex".StartsWith("(?<$mySafeName") -and -not $mySafeName -eq 'UseRegEx') {
$regex = "(?<$mySafeName>$($regex;[Environment]::NewLine;))"
}
}

if ($Pattern) { # If we've been provided a pattern
Expand Down Expand Up @@ -500,13 +503,13 @@
elseif ($ReplaceIf) {
{
$tm = $($args[0])
$xm = $($tm | & $extractMatch)
$xm = $($tm | & $filterMatches | & $extractMatch )
foreach ($kv in $ReplaceIf.GetEnumerator()) {
$_ = $xm
$kvR = & $kv.Key $xm
$kvR = . $kv.Key $xm
if ($kvR) {
if ($kv.Value -is [ScriptBlock]) {
return "$(& $kv.Value $xm)"
return "$(. $kv.Value $xm)"
}

return $tm.Result("$($kv.Value)")
Expand Down Expand Up @@ -549,7 +552,13 @@
if ($measure) {
@($regex.$$.Invoke($methodArgs)).Length
} else {
$regex.$$.Invoke($methodArgs) | & $filterMatches
& {
try {
$regex.$$.Invoke($methodArgs)
} catch {
$PSCmdlet.WriteError([Management.Automation.ErrorRecord]::new($_.Exception, 'Regular.Expression.Error', 'NotSpecified', $inputObject))
}
} | & $filterMatches
}
}
}
Expand Down
15 changes: 12 additions & 3 deletions Write-RegEx.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@
Write-RegEx -Pattern '"'
.Example
# A regular expression for an email address. ?<> is an alias for Write-Regex
?<> -Name UserName -LiteralCharacter .- -CharacterClass Word -Repeat |
?<> (?<> '\@' -NoCapture) |
?<> -Name Domain -LiteralCharacter .- -CharacterClass Word -Repeat
?<> -Name EmailAddress -Pattern (
?<> -CharacterClass Word |
?<> -LiteralCharacter -. -CharacterClass Word -Min 0
) |
?<> -LiteralCharacter '@' -NoCapture |
?<> -Name Domain -Pattern (
?<> -CharacterClass Word |
?<> -LiteralCharacter - -CharacterClass Word -Min 0 |
?<> -LiteralCharacter . |
?<> -CharacterClass Word -Min 1
)
#>
[OutputType([Regex], [PSObject])]
Expand Down

0 comments on commit f7a9bb1

Please sign in to comment.