From 5215aaf679dc9c027a5ebc6c3468611912be1316 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:56:05 +0000
Subject: [PATCH 01/24] Add &! ThreadJob background operator support
- Added new TokenKind.AmpersandExclaim for &! operator
- Updated tokenizer to recognize &! operator
- Added BackgroundThreadJob property to PipelineAst
- Updated parser to handle &! operator for ThreadJobs
- Modified MiscOps to use Start-ThreadJob when BackgroundThreadJob is true
- Updated all token enum values to accommodate new token
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/lang/interface/PSToken.cs | 1 +
.../engine/parser/Parser.cs | 18 ++
.../engine/parser/ast.cs | 9 +-
.../engine/parser/token.cs | 261 +++++++++---------
.../engine/parser/tokenizer.cs | 9 +-
.../engine/runtime/Operations/MiscOps.cs | 18 +-
6 files changed, 185 insertions(+), 131 deletions(-)
diff --git a/src/System.Management.Automation/engine/lang/interface/PSToken.cs b/src/System.Management.Automation/engine/lang/interface/PSToken.cs
index 306a64a0b61..854a28f9334 100644
--- a/src/System.Management.Automation/engine/lang/interface/PSToken.cs
+++ b/src/System.Management.Automation/engine/lang/interface/PSToken.cs
@@ -157,6 +157,7 @@ public static PSTokenType GetPSTokenType(Token token)
/* AndAnd */ PSTokenType.Operator,
/* OrOr */ PSTokenType.Operator,
/* Ampersand */ PSTokenType.Operator,
+ /* AmpersandExclaim */ PSTokenType.Operator,
/* Pipe */ PSTokenType.Operator,
/* Comma */ PSTokenType.Operator,
/* MinusMinus */ PSTokenType.Operator,
diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs
index 1592d2e7e7d..298ee664af3 100644
--- a/src/System.Management.Automation/engine/parser/Parser.cs
+++ b/src/System.Management.Automation/engine/parser/Parser.cs
@@ -5938,6 +5938,24 @@ private PipelineBaseAst PipelineChainRule()
background = true;
goto default;
+ // ThreadJob background operator
+ case TokenKind.AmpersandExclaim:
+ SkipToken();
+ nextToken = PeekToken();
+
+ switch (nextToken.Kind)
+ {
+ case TokenKind.AndAnd:
+ case TokenKind.OrOr:
+ SkipToken();
+ ReportError(nextToken.Extent, nameof(ParserStrings.BackgroundOperatorInPipelineChain), ParserStrings.BackgroundOperatorInPipelineChain);
+ return new ErrorStatementAst(ExtentOf(currentPipelineChain ?? nextPipeline, nextToken.Extent));
+ }
+
+ background = true;
+ nextPipeline.BackgroundThreadJob = true;
+ goto default;
+
// No more chain operators -- return
default:
// If we haven't seen a chain yet, pass through the pipeline
diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs
index ba5c48a2752..fb1b28ef2a7 100644
--- a/src/System.Management.Automation/engine/parser/ast.cs
+++ b/src/System.Management.Automation/engine/parser/ast.cs
@@ -5767,6 +5767,11 @@ public PipelineAst(IScriptExtent extent, CommandBaseAst commandAst) : this(exten
///
public bool Background { get; internal set; }
+ ///
+ /// Indicates that this pipeline should be run in the background as a ThreadJob.
+ ///
+ public bool BackgroundThreadJob { get; internal set; }
+
///
/// If the pipeline represents a pure expression, the expression is returned, otherwise null is returned.
///
@@ -5793,7 +5798,9 @@ public override ExpressionAst GetPureExpression()
public override Ast Copy()
{
var newPipelineElements = CopyElements(this.PipelineElements);
- return new PipelineAst(this.Extent, newPipelineElements, this.Background);
+ var copy = new PipelineAst(this.Extent, newPipelineElements, this.Background);
+ copy.BackgroundThreadJob = this.BackgroundThreadJob;
+ return copy;
}
#region Visitors
diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs
index 3cee7580ff9..7f7369b2605 100644
--- a/src/System.Management.Automation/engine/parser/token.cs
+++ b/src/System.Management.Automation/engine/parser/token.cs
@@ -164,432 +164,435 @@ public enum TokenKind
/// The invocation operator '&'.
Ampersand = 28,
+ /// The ThreadJob background operator '&!'.
+ AmpersandExclaim = 29,
+
/// The pipe operator '|'.
- Pipe = 29,
+ Pipe = 30,
/// The unary or binary array operator ','.
- Comma = 30,
+ Comma = 31,
/// The pre-decrement operator '--'.
- MinusMinus = 31,
+ MinusMinus = 32,
/// The pre-increment operator '++'.
- PlusPlus = 32,
+ PlusPlus = 33,
/// The range operator '..'.
- DotDot = 33,
+ DotDot = 34,
/// The static member access operator '::'.
- ColonColon = 34,
+ ColonColon = 35,
/// The instance member access or dot source invocation operator '.'.
- Dot = 35,
+ Dot = 36,
/// The logical not operator '!'.
- Exclaim = 36,
+ Exclaim = 37,
/// The multiplication operator '*'.
- Multiply = 37,
+ Multiply = 38,
/// The division operator '/'.
- Divide = 38,
+ Divide = 39,
/// The modulo division (remainder) operator '%'.
- Rem = 39,
+ Rem = 40,
/// The addition operator '+'.
- Plus = 40,
+ Plus = 41,
/// The subtraction operator '-'.
- Minus = 41,
+ Minus = 42,
/// The assignment operator '='.
- Equals = 42,
+ Equals = 43,
/// The addition assignment operator '+='.
- PlusEquals = 43,
+ PlusEquals = 44,
/// The subtraction assignment operator '-='.
- MinusEquals = 44,
+ MinusEquals = 45,
/// The multiplication assignment operator '*='.
- MultiplyEquals = 45,
+ MultiplyEquals = 46,
/// The division assignment operator '/='.
- DivideEquals = 46,
+ DivideEquals = 47,
/// The modulo division (remainder) assignment operator '%='.
- RemainderEquals = 47,
+ RemainderEquals = 48,
/// A redirection operator such as '2>&1' or '>>'.
- Redirection = 48,
+ Redirection = 49,
/// The (unimplemented) stdin redirection operator '<'.
- RedirectInStd = 49,
+ RedirectInStd = 50,
/// The string format operator '-f'.
- Format = 50,
+ Format = 51,
/// The logical not operator '-not'.
- Not = 51,
+ Not = 52,
/// The bitwise not operator '-bnot'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bnot = 52,
+ Bnot = 53,
/// The logical and operator '-and'.
- And = 53,
+ And = 54,
/// The logical or operator '-or'.
- Or = 54,
+ Or = 55,
/// The logical exclusive or operator '-xor'.
- Xor = 55,
+ Xor = 56,
/// The bitwise and operator '-band'.
- Band = 56,
+ Band = 57,
/// The bitwise or operator '-bor'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bor = 57,
+ Bor = 58,
/// The bitwise exclusive or operator '-xor'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bxor = 58,
+ Bxor = 59,
/// The join operator '-join'.
- Join = 59,
+ Join = 60,
/// The case insensitive equal operator '-ieq' or '-eq'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ieq = 60,
+ Ieq = 61,
/// The case insensitive not equal operator '-ine' or '-ne'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ine = 61,
+ Ine = 62,
/// The case insensitive greater than or equal operator '-ige' or '-ge'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ige = 62,
+ Ige = 63,
/// The case insensitive greater than operator '-igt' or '-gt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Igt = 63,
+ Igt = 64,
/// The case insensitive less than operator '-ilt' or '-lt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilt = 64,
+ Ilt = 65,
/// The case insensitive less than or equal operator '-ile' or '-le'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ile = 65,
+ Ile = 66,
/// The case insensitive like operator '-ilike' or '-like'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilike = 66,
+ Ilike = 67,
/// The case insensitive not like operator '-inotlike' or '-notlike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotlike = 67,
+ Inotlike = 68,
/// The case insensitive match operator '-imatch' or '-match'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Imatch = 68,
+ Imatch = 69,
/// The case insensitive not match operator '-inotmatch' or '-notmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotmatch = 69,
+ Inotmatch = 70,
/// The case insensitive replace operator '-ireplace' or '-replace'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ireplace = 70,
+ Ireplace = 71,
/// The case insensitive contains operator '-icontains' or '-contains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Icontains = 71,
+ Icontains = 72,
/// The case insensitive notcontains operator '-inotcontains' or '-notcontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotcontains = 72,
+ Inotcontains = 73,
/// The case insensitive in operator '-iin' or '-in'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Iin = 73,
+ Iin = 74,
/// The case insensitive notin operator '-inotin' or '-notin'
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotin = 74,
+ Inotin = 75,
/// The case insensitive split operator '-isplit' or '-split'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Isplit = 75,
+ Isplit = 76,
/// The case sensitive equal operator '-ceq'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ceq = 76,
+ Ceq = 77,
/// The case sensitive not equal operator '-cne'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cne = 77,
+ Cne = 78,
/// The case sensitive greater than or equal operator '-cge'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cge = 78,
+ Cge = 79,
/// The case sensitive greater than operator '-cgt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cgt = 79,
+ Cgt = 80,
/// The case sensitive less than operator '-clt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clt = 80,
+ Clt = 81,
/// The case sensitive less than or equal operator '-cle'.
- Cle = 81,
+ Cle = 82,
/// The case sensitive like operator '-clike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clike = 82,
+ Clike = 83,
/// The case sensitive notlike operator '-cnotlike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotlike = 83,
+ Cnotlike = 84,
/// The case sensitive match operator '-cmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cmatch = 84,
+ Cmatch = 85,
/// The case sensitive not match operator '-cnotmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotmatch = 85,
+ Cnotmatch = 86,
/// The case sensitive replace operator '-creplace'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Creplace = 86,
+ Creplace = 87,
/// The case sensitive contains operator '-ccontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ccontains = 87,
+ Ccontains = 88,
/// The case sensitive not contains operator '-cnotcontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotcontains = 88,
+ Cnotcontains = 89,
/// The case sensitive in operator '-cin'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cin = 89,
+ Cin = 90,
/// The case sensitive not in operator '-notin'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotin = 90,
+ Cnotin = 91,
/// The case sensitive split operator '-csplit'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Csplit = 91,
+ Csplit = 92,
/// The type test operator '-is'.
- Is = 92,
+ Is = 93,
/// The type test operator '-isnot'.
- IsNot = 93,
+ IsNot = 94,
/// The type conversion operator '-as'.
- As = 94,
+ As = 95,
/// The post-increment operator '++'.
- PostfixPlusPlus = 95,
+ PostfixPlusPlus = 96,
/// The post-decrement operator '--'.
- PostfixMinusMinus = 96,
+ PostfixMinusMinus = 97,
/// The shift left operator.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shl = 97,
+ Shl = 98,
/// The shift right operator.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shr = 98,
+ Shr = 99,
/// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls.
- Colon = 99,
+ Colon = 100,
/// The ternary operator '?'.
- QuestionMark = 100,
+ QuestionMark = 101,
/// The null conditional assignment operator '??='.
- QuestionQuestionEquals = 101,
+ QuestionQuestionEquals = 102,
/// The null coalesce operator '??'.
- QuestionQuestion = 102,
+ QuestionQuestion = 103,
/// The null conditional member access operator '?.'.
- QuestionDot = 103,
+ QuestionDot = 104,
/// The null conditional index access operator '?[]'.
- QuestionLBracket = 104,
+ QuestionLBracket = 105,
#endregion Operators
#region Keywords
/// The 'begin' keyword.
- Begin = 119,
+ Begin = 120,
/// The 'break' keyword.
- Break = 120,
+ Break = 121,
/// The 'catch' keyword.
- Catch = 121,
+ Catch = 122,
/// The 'class' keyword.
- Class = 122,
+ Class = 123,
/// The 'continue' keyword.
- Continue = 123,
+ Continue = 124,
/// The 'data' keyword.
- Data = 124,
+ Data = 125,
/// The (unimplemented) 'define' keyword.
- Define = 125,
+ Define = 126,
/// The 'do' keyword.
- Do = 126,
+ Do = 127,
/// The 'dynamicparam' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Dynamicparam = 127,
+ Dynamicparam = 128,
/// The 'else' keyword.
- Else = 128,
+ Else = 129,
/// The 'elseif' keyword.
- ElseIf = 129,
+ ElseIf = 130,
/// The 'end' keyword.
- End = 130,
+ End = 131,
/// The 'exit' keyword.
- Exit = 131,
+ Exit = 132,
/// The 'filter' keyword.
- Filter = 132,
+ Filter = 133,
/// The 'finally' keyword.
- Finally = 133,
+ Finally = 134,
/// The 'for' keyword.
- For = 134,
+ For = 135,
/// The 'foreach' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Foreach = 135,
+ Foreach = 136,
/// The (unimplemented) 'from' keyword.
- From = 136,
+ From = 137,
/// The 'function' keyword.
- Function = 137,
+ Function = 138,
/// The 'if' keyword.
- If = 138,
+ If = 139,
/// The 'in' keyword.
- In = 139,
+ In = 140,
/// The 'param' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Param = 140,
+ Param = 141,
/// The 'process' keyword.
- Process = 141,
+ Process = 142,
/// The 'return' keyword.
- Return = 142,
+ Return = 143,
/// The 'switch' keyword.
- Switch = 143,
+ Switch = 144,
/// The 'throw' keyword.
- Throw = 144,
+ Throw = 145,
/// The 'trap' keyword.
- Trap = 145,
+ Trap = 146,
/// The 'try' keyword.
- Try = 146,
+ Try = 147,
/// The 'until' keyword.
- Until = 147,
+ Until = 148,
/// The (unimplemented) 'using' keyword.
- Using = 148,
+ Using = 149,
/// The (unimplemented) 'var' keyword.
- Var = 149,
+ Var = 150,
/// The 'while' keyword.
- While = 150,
+ While = 151,
/// The 'workflow' keyword.
- Workflow = 151,
+ Workflow = 152,
/// The 'parallel' keyword.
- Parallel = 152,
+ Parallel = 153,
/// The 'sequence' keyword.
- Sequence = 153,
+ Sequence = 154,
/// The 'InlineScript' keyword
- InlineScript = 154,
+ InlineScript = 155,
/// The "configuration" keyword
- Configuration = 155,
+ Configuration = 156,
/// The token kind for dynamic keywords
- DynamicKeyword = 156,
+ DynamicKeyword = 157,
/// The 'public' keyword
- Public = 157,
+ Public = 158,
/// The 'private' keyword
- Private = 158,
+ Private = 159,
/// The 'static' keyword
- Static = 159,
+ Static = 160,
/// The 'interface' keyword
- Interface = 160,
+ Interface = 161,
/// The 'enum' keyword
- Enum = 161,
+ Enum = 162,
/// The 'namespace' keyword
- Namespace = 162,
+ Namespace = 163,
/// The 'module' keyword
- Module = 163,
+ Module = 164,
/// The 'type' keyword
- Type = 164,
+ Type = 165,
/// The 'assembly' keyword
- Assembly = 165,
+ Assembly = 166,
/// The 'command' keyword
- Command = 166,
+ Command = 167,
/// The 'hidden' keyword
- Hidden = 167,
+ Hidden = 168,
/// The 'base' keyword
- Base = 168,
+ Base = 169,
/// The 'default' keyword
- Default = 169,
+ Default = 170,
/// The 'clean' keyword.
- Clean = 170,
+ Clean = 171,
#endregion Keywords
}
@@ -805,6 +808,7 @@ public static class TokenTraits
/* AndAnd */ TokenFlags.ParseModeInvariant,
/* OrOr */ TokenFlags.ParseModeInvariant,
/* Ampersand */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
+ /* AmpersandExclaim */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
/* Pipe */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
/* Comma */ TokenFlags.UnaryOperator | TokenFlags.ParseModeInvariant,
/* MinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
@@ -1005,6 +1009,7 @@ public static class TokenTraits
/* AndAnd */ "&&",
/* OrOr */ "||",
/* Ampersand */ "&",
+ /* AmpersandExclaim */ "&!",
/* Pipe */ "|",
/* Comma */ ",",
/* MinusMinus */ "--",
diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs
index e2aed94cc98..4804d574fd0 100644
--- a/src/System.Management.Automation/engine/parser/tokenizer.cs
+++ b/src/System.Management.Automation/engine/parser/tokenizer.cs
@@ -4975,12 +4975,19 @@ internal Token NextToken()
return ScanNumber(c);
case '&':
- if (PeekChar() == '&')
+ c1 = PeekChar();
+ if (c1 == '&')
{
SkipChar();
return NewToken(TokenKind.AndAnd);
}
+ if (c1 == '!')
+ {
+ SkipChar();
+ return NewToken(TokenKind.AmpersandExclaim);
+ }
+
return NewToken(TokenKind.Ampersand);
case '|':
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index d584666ab62..dae89644a64 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -585,7 +585,23 @@ internal static void InvokePipelineInBackground(
updatedScriptblock.Append(scriptblockBodyString.AsSpan(position));
var sb = ScriptBlock.Create(updatedScriptblock.ToString());
- var commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+
+ // Use Start-ThreadJob if BackgroundThreadJob is set, otherwise use Start-Job
+ CmdletInfo commandInfo;
+ if (pipelineAst.BackgroundThreadJob)
+ {
+ commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
+ if (commandInfo == null)
+ {
+ // Fall back to Start-Job if Start-ThreadJob is not available
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
+ }
+ else
+ {
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
+
commandProcessor = context.CommandDiscovery.LookupCommandProcessor(commandInfo, CommandOrigin.Internal, false, context.EngineSessionState);
var workingDirectoryParameter = CommandParameterInternal.CreateParameterWithArgument(
From 06f7fefbada827cae8fb0adf2002231e08c88357 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:57:45 +0000
Subject: [PATCH 02/24] Add tests for ThreadJob background operator
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../ThreadJobBackgroundOperator.Tests.ps1 | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
create mode 100644 test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
diff --git a/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1 b/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
new file mode 100644
index 00000000000..0eb20674666
--- /dev/null
+++ b/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
@@ -0,0 +1,152 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+Describe "ThreadJob Background Operator &! Tests" -Tag CI {
+ BeforeAll {
+ # Ensure ThreadJob module is available
+ $threadJobAvailable = $null -ne (Get-Command Start-ThreadJob -ErrorAction SilentlyContinue)
+
+ if (-not $threadJobAvailable) {
+ Write-Warning "Start-ThreadJob command not available. Tests may fall back to regular jobs."
+ }
+ }
+
+ It "Creates a background job with &! operator" {
+ $job = Write-Output "Hello from ThreadJob" &!
+ $job | Should -Not -BeNullOrEmpty
+ $job | Should -BeOfType [System.Management.Automation.Job]
+ $job | Wait-Job | Remove-Job
+ }
+
+ It "Receives output from ThreadJob background job" {
+ $job = Write-Output "Test Output" &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be "Test Output"
+ $job | Remove-Job
+ }
+
+ It "Runs simple expression as ThreadJob" {
+ $job = 1 + 1 &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be 2
+ $job | Remove-Job
+ }
+
+ It "Captures variables with $using: in ThreadJob" {
+ $testVar = "CapturedValue"
+ $job = { $using:testVar } &!
+ # Note: The implementation should automatically convert variables to $using:
+ # For now, this test documents expected behavior
+ $job | Wait-Job | Remove-Job
+ }
+
+ It "Runs pipeline as ThreadJob" {
+ $job = 1,2,3 | ForEach-Object { $_ * 2 } &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be @(2, 4, 6)
+ $job | Remove-Job
+ }
+
+ It "Works with variable assignment" {
+ $job = 1 + 2 &!
+ $job | Should -Not -BeNullOrEmpty
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be 3
+ $job | Remove-Job
+ }
+
+ It "Can be combined with && operator" -Skip {
+ # This test is skipped as the interaction between &! and && needs to be verified
+ $job = testexe -returncode 0 && Write-Output "success" &!
+ $result = $job | Wait-Job | Receive-Job
+ $job | Remove-Job
+ }
+
+ It "Rejects &! with && in invalid syntax" {
+ $tokens = $errors = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! && testexe -returncode 1', [ref]$tokens, [ref]$errors)
+
+ $errors.Count | Should -BeGreaterThan 0
+ $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
+ }
+
+ It "Rejects &! with || in invalid syntax" {
+ $tokens = $errors = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! || testexe -returncode 1', [ref]$tokens, [ref]$errors)
+
+ $errors.Count | Should -BeGreaterThan 0
+ $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
+ }
+
+ It "Works with command execution" {
+ $job = Get-Process -Id $PID | Select-Object -ExpandProperty Name &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Not -BeNullOrEmpty
+ $job | Remove-Job
+ }
+
+ It "Handles errors in ThreadJob" {
+ $job = { throw "Test Error" } &!
+ $job | Wait-Job
+ $job.State | Should -Be 'Failed'
+ $job | Remove-Job
+ }
+
+ It "Creates multiple ThreadJobs" {
+ $job1 = Write-Output "Job1" &!
+ $job2 = Write-Output "Job2" &!
+ $job3 = Write-Output "Job3" &!
+
+ $results = $job1, $job2, $job3 | Wait-Job | Receive-Job
+ $results | Should -Contain "Job1"
+ $results | Should -Contain "Job2"
+ $results | Should -Contain "Job3"
+
+ $job1, $job2, $job3 | Remove-Job
+ }
+
+ Context "Parser AST Tests" {
+ It "Parses &! operator correctly" {
+ $ast = [System.Management.Automation.Language.Parser]::ParseInput('Write-Output "test" &!', [ref]$null, [ref]$null)
+ $pipelineAst = $ast.EndBlock.Statements[0]
+ $pipelineAst | Should -BeOfType [System.Management.Automation.Language.PipelineAst]
+ $pipelineAst.Background | Should -Be $true
+ $pipelineAst.BackgroundThreadJob | Should -Be $true
+ }
+
+ It "Distinguishes between & and &! operators" {
+ $ast1 = [System.Management.Automation.Language.Parser]::ParseInput('Write-Output "test" &', [ref]$null, [ref]$null)
+ $pipelineAst1 = $ast1.EndBlock.Statements[0]
+ $pipelineAst1.Background | Should -Be $true
+ $pipelineAst1.BackgroundThreadJob | Should -Be $false
+
+ $ast2 = [System.Management.Automation.Language.Parser]::ParseInput('Write-Output "test" &!', [ref]$null, [ref]$null)
+ $pipelineAst2 = $ast2.EndBlock.Statements[0]
+ $pipelineAst2.Background | Should -Be $true
+ $pipelineAst2.BackgroundThreadJob | Should -Be $true
+ }
+ }
+
+ Context "Tokenizer Tests" {
+ It "Tokenizes &! as AmpersandExclaim" {
+ $tokens = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('Write-Output "test" &!', [ref]$tokens, [ref]$null)
+
+ $ampersandExclaimToken = $tokens | Where-Object { $_.Kind -eq [System.Management.Automation.Language.TokenKind]::AmpersandExclaim }
+ $ampersandExclaimToken | Should -Not -BeNullOrEmpty
+ $ampersandExclaimToken.Text | Should -Be '&!'
+ }
+
+ It "Distinguishes & and &! tokens" {
+ $tokens1 = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('test &', [ref]$tokens1, [ref]$null)
+ $ampToken = $tokens1 | Where-Object { $_.Kind -eq [System.Management.Automation.Language.TokenKind]::Ampersand }
+ $ampToken | Should -Not -BeNullOrEmpty
+
+ $tokens2 = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('test &!', [ref]$tokens2, [ref]$null)
+ $ampExclaimToken = $tokens2 | Where-Object { $_.Kind -eq [System.Management.Automation.Language.TokenKind]::AmpersandExclaim }
+ $ampExclaimToken | Should -Not -BeNullOrEmpty
+ }
+ }
+}
From f05ee031aab44487f4412ce25834b0301c544788 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 1 Jan 2026 22:59:00 +0000
Subject: [PATCH 03/24] Add exception handling for Start-ThreadJob cmdlet
lookup
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index dae89644a64..f2a0eb11e71 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -590,10 +590,18 @@ internal static void InvokePipelineInBackground(
CmdletInfo commandInfo;
if (pipelineAst.BackgroundThreadJob)
{
- commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
- if (commandInfo == null)
+ try
+ {
+ commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
+ if (commandInfo == null)
+ {
+ // Fall back to Start-Job if Start-ThreadJob is not available
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
+ }
+ catch
{
- // Fall back to Start-Job if Start-ThreadJob is not available
+ // Fall back to Start-Job if Start-ThreadJob lookup fails
commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
}
}
From d70d91407442ad2655757b5673e6e3553aba01bc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 1 Jan 2026 23:00:27 +0000
Subject: [PATCH 04/24] Use specific exception types in catch blocks for
Start-ThreadJob lookup
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index f2a0eb11e71..8b36e7efda0 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -599,9 +599,14 @@ internal static void InvokePipelineInBackground(
commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
}
}
- catch
+ catch (CommandNotFoundException)
{
- // Fall back to Start-Job if Start-ThreadJob lookup fails
+ // Fall back to Start-Job if Start-ThreadJob is not found
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
+ catch (InvalidOperationException)
+ {
+ // Fall back to Start-Job if cmdlet lookup fails
commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
}
}
From 87f3e984c051cdd407435e2a6a4d5ea6373b0b60 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 2 Jan 2026 00:08:21 +0000
Subject: [PATCH 05/24] Fix compilation errors: move BackgroundThreadJob to
ChainableAst and use compound assignment
- Moved BackgroundThreadJob property from PipelineAst to ChainableAst base class
to make it accessible to both PipelineAst and PipelineChainAst
- Updated PipelineChainAst.Copy() to preserve BackgroundThreadJob property
- Fixed IDE0074 warning by using ??= compound assignment operator
- Fixes CS1061 error where PipelineBaseAst didn't have BackgroundThreadJob property
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/parser/ast.cs | 14 ++++++++------
.../engine/runtime/Operations/MiscOps.cs | 7 ++-----
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs
index fb1b28ef2a7..f8e703d4d71 100644
--- a/src/System.Management.Automation/engine/parser/ast.cs
+++ b/src/System.Management.Automation/engine/parser/ast.cs
@@ -5540,6 +5540,11 @@ public abstract class ChainableAst : PipelineBaseAst
protected ChainableAst(IScriptExtent extent) : base(extent)
{
}
+
+ ///
+ /// Indicates that this pipeline should be run in the background as a ThreadJob.
+ ///
+ public bool BackgroundThreadJob { get; internal set; }
}
///
@@ -5610,7 +5615,9 @@ public PipelineChainAst(
///
public override Ast Copy()
{
- return new PipelineChainAst(Extent, CopyElement(LhsPipelineChain), CopyElement(RhsPipeline), Operator, Background);
+ var copy = new PipelineChainAst(Extent, CopyElement(LhsPipelineChain), CopyElement(RhsPipeline), Operator, Background);
+ copy.BackgroundThreadJob = this.BackgroundThreadJob;
+ return copy;
}
internal override object Accept(ICustomAstVisitor visitor)
@@ -5767,11 +5774,6 @@ public PipelineAst(IScriptExtent extent, CommandBaseAst commandAst) : this(exten
///
public bool Background { get; internal set; }
- ///
- /// Indicates that this pipeline should be run in the background as a ThreadJob.
- ///
- public bool BackgroundThreadJob { get; internal set; }
-
///
/// If the pipeline represents a pure expression, the expression is returned, otherwise null is returned.
///
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index 8b36e7efda0..87ab7d0564e 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -593,11 +593,8 @@ internal static void InvokePipelineInBackground(
try
{
commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
- if (commandInfo == null)
- {
- // Fall back to Start-Job if Start-ThreadJob is not available
- commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
- }
+ // Fall back to Start-Job if Start-ThreadJob is not available
+ commandInfo ??= new CmdletInfo("Start-Job", typeof(StartJobCommand));
}
catch (CommandNotFoundException)
{
From 3eccbc8d9bf1705f1b90affc7b317be3f79993cb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 2 Jan 2026 00:23:46 +0000
Subject: [PATCH 06/24] Move BackgroundThreadJob property to PipelineBaseAst
base class
- Moved BackgroundThreadJob from ChainableAst to PipelineBaseAst
- This ensures the property is accessible when the parameter type is PipelineBaseAst
- Fixes CS1061 compilation error where PipelineBaseAst didn't have BackgroundThreadJob
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
src/System.Management.Automation/engine/parser/ast.cs | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs
index f8e703d4d71..5c040c9ba3c 100644
--- a/src/System.Management.Automation/engine/parser/ast.cs
+++ b/src/System.Management.Automation/engine/parser/ast.cs
@@ -5540,11 +5540,6 @@ public abstract class ChainableAst : PipelineBaseAst
protected ChainableAst(IScriptExtent extent) : base(extent)
{
}
-
- ///
- /// Indicates that this pipeline should be run in the background as a ThreadJob.
- ///
- public bool BackgroundThreadJob { get; internal set; }
}
///
@@ -5682,6 +5677,11 @@ public virtual ExpressionAst GetPureExpression()
{
return null;
}
+
+ ///
+ /// Indicates that this pipeline should be run in the background as a ThreadJob.
+ ///
+ public bool BackgroundThreadJob { get; internal set; }
}
///
From 51096c12eaf4cd57a6780f56474db19c3260c142 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 02:54:05 +0000
Subject: [PATCH 07/24] Address PR review feedback: improve tests and simplify
exception handling
- Reorganized tests into Context blocks (Runtime, Syntax, Parser, Tokenizer)
- Added tests to validate ThreadJob vs fallback behavior with -Skip conditions
- Fixed variable capture test to assert actual captured values
- Enabled and improved the && operator combination test
- Simplified exception handling in MiscOps.cs (removed unnecessary try-catch)
- GetCmdlet returns null when not found, doesn't throw in common case
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 20 +-
.../ThreadJobBackgroundOperator.Tests.ps1 | 193 ++++++++++--------
2 files changed, 117 insertions(+), 96 deletions(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index 87ab7d0564e..e5719f9e41c 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -590,22 +590,10 @@ internal static void InvokePipelineInBackground(
CmdletInfo commandInfo;
if (pipelineAst.BackgroundThreadJob)
{
- try
- {
- commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
- // Fall back to Start-Job if Start-ThreadJob is not available
- commandInfo ??= new CmdletInfo("Start-Job", typeof(StartJobCommand));
- }
- catch (CommandNotFoundException)
- {
- // Fall back to Start-Job if Start-ThreadJob is not found
- commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
- }
- catch (InvalidOperationException)
- {
- // Fall back to Start-Job if cmdlet lookup fails
- commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
- }
+ // GetCmdlet returns null when the cmdlet isn't found (it doesn't throw in common case)
+ commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
+ // Fall back to Start-Job if Start-ThreadJob is not available
+ commandInfo ??= new CmdletInfo("Start-Job", typeof(StartJobCommand));
}
else
{
diff --git a/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1 b/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
index 0eb20674666..3cbaa3134a1 100644
--- a/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
+++ b/test/powershell/Language/Operators/ThreadJobBackgroundOperator.Tests.ps1
@@ -11,98 +11,131 @@ Describe "ThreadJob Background Operator &! Tests" -Tag CI {
}
}
- It "Creates a background job with &! operator" {
- $job = Write-Output "Hello from ThreadJob" &!
- $job | Should -Not -BeNullOrEmpty
- $job | Should -BeOfType [System.Management.Automation.Job]
- $job | Wait-Job | Remove-Job
- }
+ Context "Runtime ThreadJob Tests" {
+ It "Creates a background job with &! operator" {
+ $job = Write-Output "Hello from ThreadJob" &!
+ $job | Should -Not -BeNullOrEmpty
+ $job | Should -BeOfType [System.Management.Automation.Job]
+ $job | Wait-Job | Remove-Job
+ }
- It "Receives output from ThreadJob background job" {
- $job = Write-Output "Test Output" &!
- $result = $job | Wait-Job | Receive-Job
- $result | Should -Be "Test Output"
- $job | Remove-Job
- }
+ It "Receives output from ThreadJob background job" {
+ $job = Write-Output "Test Output" &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be "Test Output"
+ $job | Remove-Job
+ }
- It "Runs simple expression as ThreadJob" {
- $job = 1 + 1 &!
- $result = $job | Wait-Job | Receive-Job
- $result | Should -Be 2
- $job | Remove-Job
- }
+ It "Runs simple expression as ThreadJob" {
+ $job = 1 + 1 &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be 2
+ $job | Remove-Job
+ }
- It "Captures variables with $using: in ThreadJob" {
- $testVar = "CapturedValue"
- $job = { $using:testVar } &!
- # Note: The implementation should automatically convert variables to $using:
- # For now, this test documents expected behavior
- $job | Wait-Job | Remove-Job
- }
+ It "Validates ThreadJob is created when Start-ThreadJob is available" -Skip:(-not $threadJobAvailable) {
+ $job = Write-Output "ThreadJob Test" &!
+ $job | Should -Not -BeNullOrEmpty
+ # ThreadJobs have a PSTypeName that includes 'ThreadJob'
+ $job.PSObject.TypeNames | Should -Contain 'ThreadJob'
+ $job | Wait-Job | Remove-Job
+ }
- It "Runs pipeline as ThreadJob" {
- $job = 1,2,3 | ForEach-Object { $_ * 2 } &!
- $result = $job | Wait-Job | Receive-Job
- $result | Should -Be @(2, 4, 6)
- $job | Remove-Job
- }
+ It "Falls back to regular job when Start-ThreadJob is unavailable" -Skip:$threadJobAvailable {
+ # This test runs only when ThreadJob is not available
+ $job = Write-Output "Fallback Test" &!
+ $job | Should -Not -BeNullOrEmpty
+ $job | Should -BeOfType [System.Management.Automation.Job]
+ # Should not be a ThreadJob
+ $job.PSObject.TypeNames | Should -Not -Contain 'ThreadJob'
+ $job | Wait-Job | Remove-Job
+ }
- It "Works with variable assignment" {
- $job = 1 + 2 &!
- $job | Should -Not -BeNullOrEmpty
- $result = $job | Wait-Job | Receive-Job
- $result | Should -Be 3
- $job | Remove-Job
- }
+ It "Captures variables automatically without explicit $using:" {
+ $testVar = "CapturedValue"
+ $job = Write-Output $testVar &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be "CapturedValue"
+ $job | Remove-Job
+ }
- It "Can be combined with && operator" -Skip {
- # This test is skipped as the interaction between &! and && needs to be verified
- $job = testexe -returncode 0 && Write-Output "success" &!
- $result = $job | Wait-Job | Receive-Job
- $job | Remove-Job
- }
+ It "Captures variables with explicit $using: in scriptblock" {
+ $testVar = "CapturedValueWithUsing"
+ $job = { $using:testVar } &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be "CapturedValueWithUsing"
+ $job | Remove-Job
+ }
- It "Rejects &! with && in invalid syntax" {
- $tokens = $errors = $null
- $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! && testexe -returncode 1', [ref]$tokens, [ref]$errors)
-
- $errors.Count | Should -BeGreaterThan 0
- $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
- }
+ It "Runs pipeline as ThreadJob" {
+ $job = 1,2,3 | ForEach-Object { $_ * 2 } &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be @(2, 4, 6)
+ $job | Remove-Job
+ }
- It "Rejects &! with || in invalid syntax" {
- $tokens = $errors = $null
- $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! || testexe -returncode 1', [ref]$tokens, [ref]$errors)
-
- $errors.Count | Should -BeGreaterThan 0
- $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
- }
+ It "Works with variable assignment" {
+ $job = 1 + 2 &!
+ $job | Should -Not -BeNullOrEmpty
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Be 3
+ $job | Remove-Job
+ }
- It "Works with command execution" {
- $job = Get-Process -Id $PID | Select-Object -ExpandProperty Name &!
- $result = $job | Wait-Job | Receive-Job
- $result | Should -Not -BeNullOrEmpty
- $job | Remove-Job
- }
+ It "Can be combined with && operator" {
+ $job = testexe -returncode 0 && Write-Output "success" &!
+ $job | Should -Not -BeNullOrEmpty
+ $job | Should -BeOfType [System.Management.Automation.Job]
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Contain "0"
+ $result | Should -Contain "success"
+ $job | Remove-Job
+ }
- It "Handles errors in ThreadJob" {
- $job = { throw "Test Error" } &!
- $job | Wait-Job
- $job.State | Should -Be 'Failed'
- $job | Remove-Job
+ It "Works with command execution" {
+ $job = Get-Process -Id $PID | Select-Object -ExpandProperty Name &!
+ $result = $job | Wait-Job | Receive-Job
+ $result | Should -Not -BeNullOrEmpty
+ $job | Remove-Job
+ }
+
+ It "Handles errors in ThreadJob" {
+ $job = { throw "Test Error" } &!
+ $job | Wait-Job
+ $job.State | Should -Be 'Failed'
+ $job | Remove-Job
+ }
+
+ It "Creates multiple ThreadJobs" {
+ $job1 = Write-Output "Job1" &!
+ $job2 = Write-Output "Job2" &!
+ $job3 = Write-Output "Job3" &!
+
+ $results = $job1, $job2, $job3 | Wait-Job | Receive-Job
+ $results | Should -Contain "Job1"
+ $results | Should -Contain "Job2"
+ $results | Should -Contain "Job3"
+
+ $job1, $job2, $job3 | Remove-Job
+ }
}
- It "Creates multiple ThreadJobs" {
- $job1 = Write-Output "Job1" &!
- $job2 = Write-Output "Job2" &!
- $job3 = Write-Output "Job3" &!
-
- $results = $job1, $job2, $job3 | Wait-Job | Receive-Job
- $results | Should -Contain "Job1"
- $results | Should -Contain "Job2"
- $results | Should -Contain "Job3"
-
- $job1, $job2, $job3 | Remove-Job
+ Context "Syntax Validation Tests" {
+ It "Rejects &! with && in invalid syntax" {
+ $tokens = $errors = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! && testexe -returncode 1', [ref]$tokens, [ref]$errors)
+
+ $errors.Count | Should -BeGreaterThan 0
+ $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
+ }
+
+ It "Rejects &! with || in invalid syntax" {
+ $tokens = $errors = $null
+ $null = [System.Management.Automation.Language.Parser]::ParseInput('testexe -returncode 0 &! || testexe -returncode 1', [ref]$tokens, [ref]$errors)
+
+ $errors.Count | Should -BeGreaterThan 0
+ $errors[0].ErrorId | Should -Be 'BackgroundOperatorInPipelineChain'
+ }
}
Context "Parser AST Tests" {
From d9cf9e882097d8e2b66974e73bea58fad26ca9c7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 03:12:30 +0000
Subject: [PATCH 08/24] Fix binary-breaking change: move AmpersandExclaim token
to end of enum
- Moved TokenKind.AmpersandExclaim from position 29 to position 171 (end of enum)
- Restored all token enum values (Pipe through Clean) to their original positions
- Updated token flags and text arrays to place AmpersandExclaim at the end
- Updated PSToken.cs mapping array accordingly
- Added missing Clean entry to PSToken.cs
- This avoids breaking compiled consumers that rely on stable enum values
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/lang/interface/PSToken.cs | 3 +-
.../engine/parser/token.cs | 266 +--
.../engine/parser/token.cs.backup | 1580 +++++++++++++++++
3 files changed, 1715 insertions(+), 134 deletions(-)
create mode 100644 src/System.Management.Automation/engine/parser/token.cs.backup
diff --git a/src/System.Management.Automation/engine/lang/interface/PSToken.cs b/src/System.Management.Automation/engine/lang/interface/PSToken.cs
index 854a28f9334..f5eb79be678 100644
--- a/src/System.Management.Automation/engine/lang/interface/PSToken.cs
+++ b/src/System.Management.Automation/engine/lang/interface/PSToken.cs
@@ -157,7 +157,6 @@ public static PSTokenType GetPSTokenType(Token token)
/* AndAnd */ PSTokenType.Operator,
/* OrOr */ PSTokenType.Operator,
/* Ampersand */ PSTokenType.Operator,
- /* AmpersandExclaim */ PSTokenType.Operator,
/* Pipe */ PSTokenType.Operator,
/* Comma */ PSTokenType.Operator,
/* MinusMinus */ PSTokenType.Operator,
@@ -304,6 +303,8 @@ public static PSTokenType GetPSTokenType(Token token)
/* Hidden */ PSTokenType.Keyword,
/* Base */ PSTokenType.Keyword,
/* Default */ PSTokenType.Keyword,
+ /* Clean */ PSTokenType.Keyword,
+ /* AmpersandExclaim */ PSTokenType.Operator,
#endregion Flags for keywords
diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs
index 7f7369b2605..218dd6ff10a 100644
--- a/src/System.Management.Automation/engine/parser/token.cs
+++ b/src/System.Management.Automation/engine/parser/token.cs
@@ -164,435 +164,435 @@ public enum TokenKind
/// The invocation operator '&'.
Ampersand = 28,
- /// The ThreadJob background operator '&!'.
- AmpersandExclaim = 29,
-
/// The pipe operator '|'.
- Pipe = 30,
+ Pipe = 29,
/// The unary or binary array operator ','.
- Comma = 31,
+ Comma = 30,
/// The pre-decrement operator '--'.
- MinusMinus = 32,
+ MinusMinus = 31,
/// The pre-increment operator '++'.
- PlusPlus = 33,
+ PlusPlus = 32,
/// The range operator '..'.
- DotDot = 34,
+ DotDot = 33,
/// The static member access operator '::'.
- ColonColon = 35,
+ ColonColon = 34,
/// The instance member access or dot source invocation operator '.'.
- Dot = 36,
+ Dot = 35,
/// The logical not operator '!'.
- Exclaim = 37,
+ Exclaim = 36,
/// The multiplication operator '*'.
- Multiply = 38,
+ Multiply = 37,
/// The division operator '/'.
- Divide = 39,
+ Divide = 38,
/// The modulo division (remainder) operator '%'.
- Rem = 40,
+ Rem = 39,
/// The addition operator '+'.
- Plus = 41,
+ Plus = 40,
/// The subtraction operator '-'.
- Minus = 42,
+ Minus = 41,
/// The assignment operator '='.
- Equals = 43,
+ Equals = 42,
/// The addition assignment operator '+='.
- PlusEquals = 44,
+ PlusEquals = 43,
/// The subtraction assignment operator '-='.
- MinusEquals = 45,
+ MinusEquals = 44,
/// The multiplication assignment operator '*='.
- MultiplyEquals = 46,
+ MultiplyEquals = 45,
/// The division assignment operator '/='.
- DivideEquals = 47,
+ DivideEquals = 46,
/// The modulo division (remainder) assignment operator '%='.
- RemainderEquals = 48,
+ RemainderEquals = 47,
/// A redirection operator such as '2>&1' or '>>'.
- Redirection = 49,
+ Redirection = 48,
/// The (unimplemented) stdin redirection operator '<'.
- RedirectInStd = 50,
+ RedirectInStd = 49,
/// The string format operator '-f'.
- Format = 51,
+ Format = 50,
/// The logical not operator '-not'.
- Not = 52,
+ Not = 51,
/// The bitwise not operator '-bnot'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bnot = 53,
+ Bnot = 52,
/// The logical and operator '-and'.
- And = 54,
+ And = 53,
/// The logical or operator '-or'.
- Or = 55,
+ Or = 54,
/// The logical exclusive or operator '-xor'.
- Xor = 56,
+ Xor = 55,
/// The bitwise and operator '-band'.
- Band = 57,
+ Band = 56,
/// The bitwise or operator '-bor'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bor = 58,
+ Bor = 57,
/// The bitwise exclusive or operator '-xor'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bxor = 59,
+ Bxor = 58,
/// The join operator '-join'.
- Join = 60,
+ Join = 59,
/// The case insensitive equal operator '-ieq' or '-eq'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ieq = 61,
+ Ieq = 60,
/// The case insensitive not equal operator '-ine' or '-ne'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ine = 62,
+ Ine = 61,
/// The case insensitive greater than or equal operator '-ige' or '-ge'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ige = 63,
+ Ige = 62,
/// The case insensitive greater than operator '-igt' or '-gt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Igt = 64,
+ Igt = 63,
/// The case insensitive less than operator '-ilt' or '-lt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilt = 65,
+ Ilt = 64,
/// The case insensitive less than or equal operator '-ile' or '-le'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ile = 66,
+ Ile = 65,
/// The case insensitive like operator '-ilike' or '-like'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilike = 67,
+ Ilike = 66,
/// The case insensitive not like operator '-inotlike' or '-notlike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotlike = 68,
+ Inotlike = 67,
/// The case insensitive match operator '-imatch' or '-match'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Imatch = 69,
+ Imatch = 68,
/// The case insensitive not match operator '-inotmatch' or '-notmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotmatch = 70,
+ Inotmatch = 69,
/// The case insensitive replace operator '-ireplace' or '-replace'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ireplace = 71,
+ Ireplace = 70,
/// The case insensitive contains operator '-icontains' or '-contains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Icontains = 72,
+ Icontains = 71,
/// The case insensitive notcontains operator '-inotcontains' or '-notcontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotcontains = 73,
+ Inotcontains = 72,
/// The case insensitive in operator '-iin' or '-in'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Iin = 74,
+ Iin = 73,
/// The case insensitive notin operator '-inotin' or '-notin'
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotin = 75,
+ Inotin = 74,
/// The case insensitive split operator '-isplit' or '-split'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Isplit = 76,
+ Isplit = 75,
/// The case sensitive equal operator '-ceq'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ceq = 77,
+ Ceq = 76,
/// The case sensitive not equal operator '-cne'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cne = 78,
+ Cne = 77,
/// The case sensitive greater than or equal operator '-cge'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cge = 79,
+ Cge = 78,
/// The case sensitive greater than operator '-cgt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cgt = 80,
+ Cgt = 79,
/// The case sensitive less than operator '-clt'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clt = 81,
+ Clt = 80,
/// The case sensitive less than or equal operator '-cle'.
- Cle = 82,
+ Cle = 81,
/// The case sensitive like operator '-clike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clike = 83,
+ Clike = 82,
/// The case sensitive notlike operator '-cnotlike'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotlike = 84,
+ Cnotlike = 83,
/// The case sensitive match operator '-cmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cmatch = 85,
+ Cmatch = 84,
/// The case sensitive not match operator '-cnotmatch'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotmatch = 86,
+ Cnotmatch = 85,
/// The case sensitive replace operator '-creplace'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Creplace = 87,
+ Creplace = 86,
/// The case sensitive contains operator '-ccontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ccontains = 88,
+ Ccontains = 87,
/// The case sensitive not contains operator '-cnotcontains'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotcontains = 89,
+ Cnotcontains = 88,
/// The case sensitive in operator '-cin'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cin = 90,
+ Cin = 89,
/// The case sensitive not in operator '-notin'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotin = 91,
+ Cnotin = 90,
/// The case sensitive split operator '-csplit'.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Csplit = 92,
+ Csplit = 91,
/// The type test operator '-is'.
- Is = 93,
+ Is = 92,
/// The type test operator '-isnot'.
- IsNot = 94,
+ IsNot = 93,
/// The type conversion operator '-as'.
- As = 95,
+ As = 94,
/// The post-increment operator '++'.
- PostfixPlusPlus = 96,
+ PostfixPlusPlus = 95,
/// The post-decrement operator '--'.
- PostfixMinusMinus = 97,
+ PostfixMinusMinus = 96,
/// The shift left operator.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shl = 98,
+ Shl = 97,
/// The shift right operator.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shr = 99,
+ Shr = 98,
/// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls.
- Colon = 100,
+ Colon = 99,
/// The ternary operator '?'.
- QuestionMark = 101,
+ QuestionMark = 100,
/// The null conditional assignment operator '??='.
- QuestionQuestionEquals = 102,
+ QuestionQuestionEquals = 101,
/// The null coalesce operator '??'.
- QuestionQuestion = 103,
+ QuestionQuestion = 102,
/// The null conditional member access operator '?.'.
- QuestionDot = 104,
+ QuestionDot = 103,
/// The null conditional index access operator '?[]'.
- QuestionLBracket = 105,
+ QuestionLBracket = 104,
#endregion Operators
#region Keywords
/// The 'begin' keyword.
- Begin = 120,
+ Begin = 119,
/// The 'break' keyword.
- Break = 121,
+ Break = 120,
/// The 'catch' keyword.
- Catch = 122,
+ Catch = 121,
/// The 'class' keyword.
- Class = 123,
+ Class = 122,
/// The 'continue' keyword.
- Continue = 124,
+ Continue = 123,
/// The 'data' keyword.
- Data = 125,
+ Data = 124,
/// The (unimplemented) 'define' keyword.
- Define = 126,
+ Define = 125,
/// The 'do' keyword.
- Do = 127,
+ Do = 126,
/// The 'dynamicparam' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Dynamicparam = 128,
+ Dynamicparam = 127,
/// The 'else' keyword.
- Else = 129,
+ Else = 128,
/// The 'elseif' keyword.
- ElseIf = 130,
+ ElseIf = 129,
/// The 'end' keyword.
- End = 131,
+ End = 130,
/// The 'exit' keyword.
- Exit = 132,
+ Exit = 131,
/// The 'filter' keyword.
- Filter = 133,
+ Filter = 132,
/// The 'finally' keyword.
- Finally = 134,
+ Finally = 133,
/// The 'for' keyword.
- For = 135,
+ For = 134,
/// The 'foreach' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Foreach = 136,
+ Foreach = 135,
/// The (unimplemented) 'from' keyword.
- From = 137,
+ From = 136,
/// The 'function' keyword.
- Function = 138,
+ Function = 137,
/// The 'if' keyword.
- If = 139,
+ If = 138,
/// The 'in' keyword.
- In = 140,
+ In = 139,
/// The 'param' keyword.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Param = 141,
+ Param = 140,
/// The 'process' keyword.
- Process = 142,
+ Process = 141,
/// The 'return' keyword.
- Return = 143,
+ Return = 142,
/// The 'switch' keyword.
- Switch = 144,
+ Switch = 143,
/// The 'throw' keyword.
- Throw = 145,
+ Throw = 144,
/// The 'trap' keyword.
- Trap = 146,
+ Trap = 145,
/// The 'try' keyword.
- Try = 147,
+ Try = 146,
/// The 'until' keyword.
- Until = 148,
+ Until = 147,
/// The (unimplemented) 'using' keyword.
- Using = 149,
+ Using = 148,
/// The (unimplemented) 'var' keyword.
- Var = 150,
+ Var = 149,
/// The 'while' keyword.
- While = 151,
+ While = 150,
/// The 'workflow' keyword.
- Workflow = 152,
+ Workflow = 151,
/// The 'parallel' keyword.
- Parallel = 153,
+ Parallel = 152,
/// The 'sequence' keyword.
- Sequence = 154,
+ Sequence = 153,
/// The 'InlineScript' keyword
- InlineScript = 155,
+ InlineScript = 154,
/// The "configuration" keyword
- Configuration = 156,
+ Configuration = 155,
/// The token kind for dynamic keywords
- DynamicKeyword = 157,
+ DynamicKeyword = 156,
/// The 'public' keyword
- Public = 158,
+ Public = 157,
/// The 'private' keyword
- Private = 159,
+ Private = 158,
/// The 'static' keyword
- Static = 160,
+ Static = 159,
/// The 'interface' keyword
- Interface = 161,
+ Interface = 160,
/// The 'enum' keyword
- Enum = 162,
+ Enum = 161,
/// The 'namespace' keyword
- Namespace = 163,
+ Namespace = 162,
/// The 'module' keyword
- Module = 164,
+ Module = 163,
/// The 'type' keyword
- Type = 165,
+ Type = 164,
/// The 'assembly' keyword
- Assembly = 166,
+ Assembly = 165,
/// The 'command' keyword
- Command = 167,
+ Command = 166,
/// The 'hidden' keyword
- Hidden = 168,
+ Hidden = 167,
/// The 'base' keyword
- Base = 169,
+ Base = 168,
/// The 'default' keyword
- Default = 170,
+ Default = 169,
/// The 'clean' keyword.
- Clean = 171,
+ Clean = 170,
+
+ /// The ThreadJob background operator '&!'.
+ AmpersandExclaim = 171,
#endregion Keywords
}
@@ -808,7 +808,6 @@ public static class TokenTraits
/* AndAnd */ TokenFlags.ParseModeInvariant,
/* OrOr */ TokenFlags.ParseModeInvariant,
/* Ampersand */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
- /* AmpersandExclaim */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
/* Pipe */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
/* Comma */ TokenFlags.UnaryOperator | TokenFlags.ParseModeInvariant,
/* MinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
@@ -956,6 +955,7 @@ public static class TokenTraits
/* Base */ TokenFlags.Keyword,
/* Default */ TokenFlags.Keyword,
/* Clean */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+ /* AmpersandExclaim */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
#endregion Flags for keywords
};
@@ -1009,7 +1009,6 @@ public static class TokenTraits
/* AndAnd */ "&&",
/* OrOr */ "||",
/* Ampersand */ "&",
- /* AmpersandExclaim */ "&!",
/* Pipe */ "|",
/* Comma */ ",",
/* MinusMinus */ "--",
@@ -1157,6 +1156,7 @@ public static class TokenTraits
/* Base */ "base",
/* Default */ "default",
/* Clean */ "clean",
+ /* AmpersandExclaim */ "&!",
#endregion Text for keywords
};
diff --git a/src/System.Management.Automation/engine/parser/token.cs.backup b/src/System.Management.Automation/engine/parser/token.cs.backup
new file mode 100644
index 00000000000..7f7369b2605
--- /dev/null
+++ b/src/System.Management.Automation/engine/parser/token.cs.backup
@@ -0,0 +1,1580 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Management.Automation.Internal;
+using System.Text;
+
+namespace System.Management.Automation.Language
+{
+ ///
+ /// The specific kind of token.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
+ public enum TokenKind
+ {
+ // When adding any new tokens, be sure to update the following tables:
+ // * TokenTraits.StaticTokenFlags
+ // * TokenTraits.Text
+
+ #region Unclassified Tokens
+
+ /// An unknown token, signifies an error condition.
+ Unknown = 0,
+
+ ///
+ /// A variable token, always begins with '$' and followed by the variable name, possibly enclose in curly braces.
+ /// Tokens with this kind are always instances of .
+ ///
+ Variable = 1,
+
+ ///
+ /// A splatted variable token, always begins with '@' and followed by the variable name.
+ /// Tokens with this kind are always instances of .
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ SplattedVariable = 2,
+
+ ///
+ /// A parameter to a command, always begins with a dash ('-'), followed by the parameter name.
+ /// Tokens with this kind are always instances of .
+ ///
+ Parameter = 3,
+
+ ///
+ /// Any numerical literal token.
+ /// Tokens with this kind are always instances of .
+ ///
+ Number = 4,
+
+ ///
+ /// A label token - always begins with ':', followed by the label name.
+ /// Tokens with this kind are always instances of .
+ ///
+ Label = 5,
+
+ ///
+ /// A simple identifier, always begins with a letter or '_', and is followed by letters, numbers, or '_'.
+ ///
+ Identifier = 6,
+
+ ///
+ /// A token that is only valid as a command name, command argument, function name, or configuration name. It may contain
+ /// characters not allowed in identifiers.
+ /// Tokens with this kind are always instances of
+ /// or if the token contains variable
+ /// references or subexpressions.
+ ///
+ Generic = 7,
+
+ /// A newline (one of '\n', '\r', or '\r\n').
+ NewLine = 8,
+
+ /// A line continuation (backtick followed by newline).
+ LineContinuation = 9,
+
+ /// A single line comment, or a delimited comment.
+ Comment = 10,
+
+ /// Marks the end of the input script or file.
+ EndOfInput = 11,
+
+ #endregion Unclassified Tokens
+
+ #region Strings
+
+ ///
+ /// A single quoted string literal.
+ /// Tokens with this kind are always instances of .
+ ///
+ StringLiteral = 12,
+
+ ///
+ /// A double quoted string literal.
+ /// Tokens with this kind are always instances of
+ /// even if there are no nested tokens to expand.
+ ///
+ StringExpandable = 13,
+
+ ///
+ /// A single quoted here string literal.
+ /// Tokens with this kind are always instances of .
+ ///
+ HereStringLiteral = 14,
+
+ ///
+ /// A double quoted here string literal.
+ /// Tokens with this kind are always instances of .
+ /// even if there are no nested tokens to expand.
+ ///
+ HereStringExpandable = 15,
+
+ #endregion Strings
+
+ #region Punctuators
+
+ /// The opening parenthesis token '('.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ LParen = 16,
+
+ /// The closing parenthesis token ')'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ RParen = 17,
+
+ /// The opening curly brace token '{'.
+ LCurly = 18,
+
+ /// The closing curly brace token '}'.
+ RCurly = 19,
+
+ /// The opening square brace token '['.
+ LBracket = 20,
+
+ /// The closing square brace token ']'.
+ RBracket = 21,
+
+ /// The opening token of an array expression '@('.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ AtParen = 22,
+
+ /// The opening token of a hash expression '@{'.
+ AtCurly = 23,
+
+ /// The opening token of a sub-expression '$('.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ DollarParen = 24,
+
+ /// The statement terminator ';'.
+ Semi = 25,
+
+ #endregion Punctuators
+
+ #region Operators
+
+ /// The (unimplemented) operator '&&'.
+ AndAnd = 26,
+
+ /// The (unimplemented) operator '||'.
+ OrOr = 27,
+
+ /// The invocation operator '&'.
+ Ampersand = 28,
+
+ /// The ThreadJob background operator '&!'.
+ AmpersandExclaim = 29,
+
+ /// The pipe operator '|'.
+ Pipe = 30,
+
+ /// The unary or binary array operator ','.
+ Comma = 31,
+
+ /// The pre-decrement operator '--'.
+ MinusMinus = 32,
+
+ /// The pre-increment operator '++'.
+ PlusPlus = 33,
+
+ /// The range operator '..'.
+ DotDot = 34,
+
+ /// The static member access operator '::'.
+ ColonColon = 35,
+
+ /// The instance member access or dot source invocation operator '.'.
+ Dot = 36,
+
+ /// The logical not operator '!'.
+ Exclaim = 37,
+
+ /// The multiplication operator '*'.
+ Multiply = 38,
+
+ /// The division operator '/'.
+ Divide = 39,
+
+ /// The modulo division (remainder) operator '%'.
+ Rem = 40,
+
+ /// The addition operator '+'.
+ Plus = 41,
+
+ /// The subtraction operator '-'.
+ Minus = 42,
+
+ /// The assignment operator '='.
+ Equals = 43,
+
+ /// The addition assignment operator '+='.
+ PlusEquals = 44,
+
+ /// The subtraction assignment operator '-='.
+ MinusEquals = 45,
+
+ /// The multiplication assignment operator '*='.
+ MultiplyEquals = 46,
+
+ /// The division assignment operator '/='.
+ DivideEquals = 47,
+
+ /// The modulo division (remainder) assignment operator '%='.
+ RemainderEquals = 48,
+
+ /// A redirection operator such as '2>&1' or '>>'.
+ Redirection = 49,
+
+ /// The (unimplemented) stdin redirection operator '<'.
+ RedirectInStd = 50,
+
+ /// The string format operator '-f'.
+ Format = 51,
+
+ /// The logical not operator '-not'.
+ Not = 52,
+
+ /// The bitwise not operator '-bnot'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Bnot = 53,
+
+ /// The logical and operator '-and'.
+ And = 54,
+
+ /// The logical or operator '-or'.
+ Or = 55,
+
+ /// The logical exclusive or operator '-xor'.
+ Xor = 56,
+
+ /// The bitwise and operator '-band'.
+ Band = 57,
+
+ /// The bitwise or operator '-bor'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Bor = 58,
+
+ /// The bitwise exclusive or operator '-xor'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Bxor = 59,
+
+ /// The join operator '-join'.
+ Join = 60,
+
+ /// The case insensitive equal operator '-ieq' or '-eq'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ieq = 61,
+
+ /// The case insensitive not equal operator '-ine' or '-ne'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ine = 62,
+
+ /// The case insensitive greater than or equal operator '-ige' or '-ge'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ige = 63,
+
+ /// The case insensitive greater than operator '-igt' or '-gt'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Igt = 64,
+
+ /// The case insensitive less than operator '-ilt' or '-lt'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ilt = 65,
+
+ /// The case insensitive less than or equal operator '-ile' or '-le'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ile = 66,
+
+ /// The case insensitive like operator '-ilike' or '-like'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ilike = 67,
+
+ /// The case insensitive not like operator '-inotlike' or '-notlike'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Inotlike = 68,
+
+ /// The case insensitive match operator '-imatch' or '-match'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Imatch = 69,
+
+ /// The case insensitive not match operator '-inotmatch' or '-notmatch'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Inotmatch = 70,
+
+ /// The case insensitive replace operator '-ireplace' or '-replace'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ireplace = 71,
+
+ /// The case insensitive contains operator '-icontains' or '-contains'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Icontains = 72,
+
+ /// The case insensitive notcontains operator '-inotcontains' or '-notcontains'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Inotcontains = 73,
+
+ /// The case insensitive in operator '-iin' or '-in'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Iin = 74,
+
+ /// The case insensitive notin operator '-inotin' or '-notin'
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Inotin = 75,
+
+ /// The case insensitive split operator '-isplit' or '-split'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Isplit = 76,
+
+ /// The case sensitive equal operator '-ceq'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ceq = 77,
+
+ /// The case sensitive not equal operator '-cne'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cne = 78,
+
+ /// The case sensitive greater than or equal operator '-cge'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cge = 79,
+
+ /// The case sensitive greater than operator '-cgt'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cgt = 80,
+
+ /// The case sensitive less than operator '-clt'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Clt = 81,
+
+ /// The case sensitive less than or equal operator '-cle'.
+ Cle = 82,
+
+ /// The case sensitive like operator '-clike'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Clike = 83,
+
+ /// The case sensitive notlike operator '-cnotlike'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cnotlike = 84,
+
+ /// The case sensitive match operator '-cmatch'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cmatch = 85,
+
+ /// The case sensitive not match operator '-cnotmatch'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cnotmatch = 86,
+
+ /// The case sensitive replace operator '-creplace'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Creplace = 87,
+
+ /// The case sensitive contains operator '-ccontains'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Ccontains = 88,
+
+ /// The case sensitive not contains operator '-cnotcontains'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cnotcontains = 89,
+
+ /// The case sensitive in operator '-cin'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cin = 90,
+
+ /// The case sensitive not in operator '-notin'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Cnotin = 91,
+
+ /// The case sensitive split operator '-csplit'.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Csplit = 92,
+
+ /// The type test operator '-is'.
+ Is = 93,
+
+ /// The type test operator '-isnot'.
+ IsNot = 94,
+
+ /// The type conversion operator '-as'.
+ As = 95,
+
+ /// The post-increment operator '++'.
+ PostfixPlusPlus = 96,
+
+ /// The post-decrement operator '--'.
+ PostfixMinusMinus = 97,
+
+ /// The shift left operator.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Shl = 98,
+
+ /// The shift right operator.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Shr = 99,
+
+ /// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls.
+ Colon = 100,
+
+ /// The ternary operator '?'.
+ QuestionMark = 101,
+
+ /// The null conditional assignment operator '??='.
+ QuestionQuestionEquals = 102,
+
+ /// The null coalesce operator '??'.
+ QuestionQuestion = 103,
+
+ /// The null conditional member access operator '?.'.
+ QuestionDot = 104,
+
+ /// The null conditional index access operator '?[]'.
+ QuestionLBracket = 105,
+
+ #endregion Operators
+
+ #region Keywords
+
+ /// The 'begin' keyword.
+ Begin = 120,
+
+ /// The 'break' keyword.
+ Break = 121,
+
+ /// The 'catch' keyword.
+ Catch = 122,
+
+ /// The 'class' keyword.
+ Class = 123,
+
+ /// The 'continue' keyword.
+ Continue = 124,
+
+ /// The 'data' keyword.
+ Data = 125,
+
+ /// The (unimplemented) 'define' keyword.
+ Define = 126,
+
+ /// The 'do' keyword.
+ Do = 127,
+
+ /// The 'dynamicparam' keyword.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Dynamicparam = 128,
+
+ /// The 'else' keyword.
+ Else = 129,
+
+ /// The 'elseif' keyword.
+ ElseIf = 130,
+
+ /// The 'end' keyword.
+ End = 131,
+
+ /// The 'exit' keyword.
+ Exit = 132,
+
+ /// The 'filter' keyword.
+ Filter = 133,
+
+ /// The 'finally' keyword.
+ Finally = 134,
+
+ /// The 'for' keyword.
+ For = 135,
+
+ /// The 'foreach' keyword.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Foreach = 136,
+
+ /// The (unimplemented) 'from' keyword.
+ From = 137,
+
+ /// The 'function' keyword.
+ Function = 138,
+
+ /// The 'if' keyword.
+ If = 139,
+
+ /// The 'in' keyword.
+ In = 140,
+
+ /// The 'param' keyword.
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ Param = 141,
+
+ /// The 'process' keyword.
+ Process = 142,
+
+ /// The 'return' keyword.
+ Return = 143,
+
+ /// The 'switch' keyword.
+ Switch = 144,
+
+ /// The 'throw' keyword.
+ Throw = 145,
+
+ /// The 'trap' keyword.
+ Trap = 146,
+
+ /// The 'try' keyword.
+ Try = 147,
+
+ /// The 'until' keyword.
+ Until = 148,
+
+ /// The (unimplemented) 'using' keyword.
+ Using = 149,
+
+ /// The (unimplemented) 'var' keyword.
+ Var = 150,
+
+ /// The 'while' keyword.
+ While = 151,
+
+ /// The 'workflow' keyword.
+ Workflow = 152,
+
+ /// The 'parallel' keyword.
+ Parallel = 153,
+
+ /// The 'sequence' keyword.
+ Sequence = 154,
+
+ /// The 'InlineScript' keyword
+ InlineScript = 155,
+
+ /// The "configuration" keyword
+ Configuration = 156,
+
+ /// The token kind for dynamic keywords
+ DynamicKeyword = 157,
+
+ /// The 'public' keyword
+ Public = 158,
+
+ /// The 'private' keyword
+ Private = 159,
+
+ /// The 'static' keyword
+ Static = 160,
+
+ /// The 'interface' keyword
+ Interface = 161,
+
+ /// The 'enum' keyword
+ Enum = 162,
+
+ /// The 'namespace' keyword
+ Namespace = 163,
+
+ /// The 'module' keyword
+ Module = 164,
+
+ /// The 'type' keyword
+ Type = 165,
+
+ /// The 'assembly' keyword
+ Assembly = 166,
+
+ /// The 'command' keyword
+ Command = 167,
+
+ /// The 'hidden' keyword
+ Hidden = 168,
+
+ /// The 'base' keyword
+ Base = 169,
+
+ /// The 'default' keyword
+ Default = 170,
+
+ /// The 'clean' keyword.
+ Clean = 171,
+
+ #endregion Keywords
+ }
+
+ ///
+ /// Flags that specify additional information about a given token.
+ ///
+ [Flags]
+ public enum TokenFlags
+ {
+ ///
+ /// The token has no flags.
+ ///
+ None = 0x00000000,
+
+ #region Precedence Values
+
+ ///
+ /// The precedence of the logical operators '-and', '-or', and '-xor'.
+ ///
+ BinaryPrecedenceLogical = 0x1,
+
+ ///
+ /// The precedence of the bitwise operators '-band', '-bor', and '-bxor'
+ ///
+ BinaryPrecedenceBitwise = 0x2,
+
+ ///
+ /// The precedence of comparison operators including: '-eq', '-ne', '-ge', '-gt', '-lt', '-le', '-like', '-notlike',
+ /// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as',
+ /// and all of the case sensitive variants of these operators, if they exists.
+ ///
+ BinaryPrecedenceComparison = 0x5,
+
+ ///
+ /// The precedence of null coalesce operator '??'.
+ ///
+ BinaryPrecedenceCoalesce = 0x7,
+
+ ///
+ /// The precedence of the binary operators '+' and '-'.
+ ///
+ BinaryPrecedenceAdd = 0x9,
+
+ ///
+ /// The precedence of the operators '*', '/', and '%'.
+ ///
+ BinaryPrecedenceMultiply = 0xa,
+
+ ///
+ /// The precedence of the '-f' operator.
+ ///
+ BinaryPrecedenceFormat = 0xc,
+
+ ///
+ /// The precedence of the '..' operator.
+ ///
+ BinaryPrecedenceRange = 0xd,
+
+ #endregion Precedence Values
+
+ ///
+ /// A bitmask to get the precedence of binary operators.
+ ///
+ BinaryPrecedenceMask = 0x0000000f,
+
+ ///
+ /// The token is a keyword.
+ ///
+ Keyword = 0x00000010,
+
+ ///
+ /// The token is one of the keywords that is a part of a script block: 'begin', 'process', 'end', 'clean', or 'dynamicparam'.
+ ///
+ ScriptBlockBlockName = 0x00000020,
+
+ ///
+ /// The token is a binary operator.
+ ///
+ BinaryOperator = 0x00000100,
+
+ ///
+ /// The token is a unary operator.
+ ///
+ UnaryOperator = 0x00000200,
+
+ ///
+ /// The token is a case sensitive operator such as '-ceq' or '-ccontains'.
+ ///
+ CaseSensitiveOperator = 0x00000400,
+
+ ///
+ /// The token is a ternary operator '?'.
+ ///
+ TernaryOperator = 0x00000800,
+
+ ///
+ /// The operators '&', '|', and the member access operators ':' and '::'.
+ ///
+ SpecialOperator = 0x00001000,
+
+ ///
+ /// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', '%=' or '??='
+ ///
+ AssignmentOperator = 0x00002000,
+
+ ///
+ /// The token is scanned identically in expression mode or command mode.
+ ///
+ ParseModeInvariant = 0x00008000,
+
+ ///
+ /// The token has some error associated with it. For example, it may be a string missing it's terminator.
+ ///
+ TokenInError = 0x00010000,
+
+ ///
+ /// The operator is not allowed in restricted language mode or in the data language.
+ ///
+ DisallowedInRestrictedMode = 0x00020000,
+
+ ///
+ /// The token is either a prefix or postfix '++' or '--'.
+ ///
+ PrefixOrPostfixOperator = 0x00040000,
+
+ ///
+ /// The token names a command in a pipeline.
+ ///
+ CommandName = 0x00080000,
+
+ ///
+ /// The token names a member of a class.
+ ///
+ MemberName = 0x00100000,
+
+ ///
+ /// The token names a type.
+ ///
+ TypeName = 0x00200000,
+
+ ///
+ /// The token names an attribute.
+ ///
+ AttributeName = 0x00400000,
+
+ ///
+ /// The token is a valid operator to use when doing constant folding.
+ ///
+ // Some operators that could be marked with this flag aren't because the current implementation depends
+ // on the execution context (e.g. -split, -join, -like, etc.)
+ // If the operator is culture sensitive (e.g. -f), then it shouldn't be marked as suitable for constant
+ // folding because evaluation of the operator could differ if the thread's culture changes.
+ CanConstantFold = 0x00800000,
+
+ ///
+ /// The token is a statement but does not support attributes.
+ ///
+ StatementDoesntSupportAttributes = 0x01000000,
+ }
+
+ ///
+ /// A utility class to get statically known traits and invariant traits about PowerShell tokens.
+ ///
+ public static class TokenTraits
+ {
+ private static readonly TokenFlags[] s_staticTokenFlags = new TokenFlags[]
+ {
+ #region Flags for unclassified tokens
+
+ /* Unknown */ TokenFlags.None,
+ /* Variable */ TokenFlags.None,
+ /* SplattedVariable */ TokenFlags.None,
+ /* Parameter */ TokenFlags.None,
+ /* Number */ TokenFlags.None,
+ /* Label */ TokenFlags.None,
+ /* Identifier */ TokenFlags.None,
+
+ /* Generic */ TokenFlags.None,
+ /* Newline */ TokenFlags.ParseModeInvariant,
+ /* LineContinuation */ TokenFlags.ParseModeInvariant,
+ /* Comment */ TokenFlags.ParseModeInvariant,
+ /* EndOfInput */ TokenFlags.ParseModeInvariant,
+
+ #endregion Flags for unclassified tokens
+
+ #region Flags for strings
+
+ /* StringLiteral */ TokenFlags.ParseModeInvariant,
+ /* StringExpandable */ TokenFlags.ParseModeInvariant,
+ /* HereStringLiteral */ TokenFlags.ParseModeInvariant,
+ /* HereStringExpandable */ TokenFlags.ParseModeInvariant,
+
+ #endregion Flags for strings
+
+ #region Flags for punctuators
+
+ /* LParen */ TokenFlags.ParseModeInvariant,
+ /* RParen */ TokenFlags.ParseModeInvariant,
+ /* LCurly */ TokenFlags.ParseModeInvariant,
+ /* RCurly */ TokenFlags.ParseModeInvariant,
+ /* LBracket */ TokenFlags.None,
+ /* RBracket */ TokenFlags.ParseModeInvariant,
+ /* AtParen */ TokenFlags.ParseModeInvariant,
+ /* AtCurly */ TokenFlags.ParseModeInvariant,
+ /* DollarParen */ TokenFlags.ParseModeInvariant,
+ /* Semi */ TokenFlags.ParseModeInvariant,
+
+ #endregion Flags for punctuators
+
+ #region Flags for operators
+
+ /* AndAnd */ TokenFlags.ParseModeInvariant,
+ /* OrOr */ TokenFlags.ParseModeInvariant,
+ /* Ampersand */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
+ /* AmpersandExclaim */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
+ /* Pipe */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
+ /* Comma */ TokenFlags.UnaryOperator | TokenFlags.ParseModeInvariant,
+ /* MinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* PlusPlus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* DotDot */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceRange | TokenFlags.DisallowedInRestrictedMode,
+ /* ColonColon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Dot */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Exclaim */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
+ /* Multiply */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
+ /* Divide */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
+ /* Rem */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
+ /* Plus */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceAdd | TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
+ /* Minus */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceAdd | TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
+ /* Equals */ TokenFlags.AssignmentOperator,
+ /* PlusEquals */ TokenFlags.AssignmentOperator,
+ /* MinusEquals */ TokenFlags.AssignmentOperator,
+ /* MultiplyEquals */ TokenFlags.AssignmentOperator,
+ /* DivideEquals */ TokenFlags.AssignmentOperator,
+ /* RemainderEquals */ TokenFlags.AssignmentOperator,
+ /* Redirection */ TokenFlags.DisallowedInRestrictedMode,
+ /* RedirectInStd */ TokenFlags.ParseModeInvariant | TokenFlags.DisallowedInRestrictedMode,
+ /* Format */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceFormat | TokenFlags.DisallowedInRestrictedMode,
+ /* Not */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
+ /* Bnot */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
+ /* And */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
+ /* Or */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
+ /* Xor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
+ /* Band */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
+ /* Bor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
+ /* Bxor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
+ /* Join */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* Ieq */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Ine */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Ige */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Igt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Ilt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Ile */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Ilike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Inotlike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Imatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* Inotmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* Ireplace */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* Icontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Inotcontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Iin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Inotin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* Isplit */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* Ceq */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cne */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cge */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cgt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Clt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cle */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Clike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cnotlike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Cnotmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Creplace */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Ccontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cnotcontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Cnotin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
+ /* Csplit */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Is */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* IsNot */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
+ /* As */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
+ /* PostFixPlusPlus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* PostFixMinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* Shl */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
+ /* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
+ /* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* QuestionMark */ TokenFlags.TernaryOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* QuestionQuestionEquals */ TokenFlags.AssignmentOperator,
+ /* QuestionQuestion */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceCoalesce,
+ /* QuestionDot */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
+ /* QuestionLBracket */ TokenFlags.None,
+ /* Reserved slot 7 */ TokenFlags.None,
+ /* Reserved slot 8 */ TokenFlags.None,
+ /* Reserved slot 9 */ TokenFlags.None,
+ /* Reserved slot 10 */ TokenFlags.None,
+ /* Reserved slot 11 */ TokenFlags.None,
+ /* Reserved slot 12 */ TokenFlags.None,
+ /* Reserved slot 13 */ TokenFlags.None,
+ /* Reserved slot 14 */ TokenFlags.None,
+ /* Reserved slot 15 */ TokenFlags.None,
+ /* Reserved slot 16 */ TokenFlags.None,
+ /* Reserved slot 17 */ TokenFlags.None,
+ /* Reserved slot 18 */ TokenFlags.None,
+ /* Reserved slot 19 */ TokenFlags.None,
+ /* Reserved slot 20 */ TokenFlags.None,
+
+ #endregion Flags for operators
+
+ #region Flags for keywords
+
+ /* Begin */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+ /* Break */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Catch */ TokenFlags.Keyword,
+ /* Class */ TokenFlags.Keyword,
+ /* Continue */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Data */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Define */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Do */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Dynamicparam */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+ /* Else */ TokenFlags.Keyword,
+ /* ElseIf */ TokenFlags.Keyword,
+ /* End */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+ /* Exit */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Filter */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Finally */ TokenFlags.Keyword,
+ /* For */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Foreach */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* From */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Function */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* If */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* In */ TokenFlags.Keyword,
+ /* Param */ TokenFlags.Keyword,
+ /* Process */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+ /* Return */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Switch */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Throw */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Trap */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Try */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Until */ TokenFlags.Keyword,
+ /* Using */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Var */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* While */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Workflow */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Parallel */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Sequence */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* InlineScript */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
+ /* Configuration */ TokenFlags.Keyword,
+ /* */ TokenFlags.Keyword,
+ /* Public */ TokenFlags.Keyword,
+ /* Private */ TokenFlags.Keyword,
+ /* Static */ TokenFlags.Keyword,
+ /* Interface */ TokenFlags.Keyword,
+ /* Enum */ TokenFlags.Keyword,
+ /* Namespace */ TokenFlags.Keyword,
+ /* Module */ TokenFlags.Keyword,
+ /* Type */ TokenFlags.Keyword,
+ /* Assembly */ TokenFlags.Keyword,
+ /* Command */ TokenFlags.Keyword,
+ /* Hidden */ TokenFlags.Keyword,
+ /* Base */ TokenFlags.Keyword,
+ /* Default */ TokenFlags.Keyword,
+ /* Clean */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
+
+ #endregion Flags for keywords
+ };
+
+ private static readonly string[] s_tokenText = new string[]
+ {
+ #region Text for unclassified tokens
+
+ /* Unknown */ "unknown",
+ /* Variable */ "var",
+ /* SplattedVariable */ "@var",
+ /* Parameter */ "param",
+ /* Number */ "number",
+ /* Label */ "label",
+ /* Identifier */ "ident",
+
+ /* Generic */ "generic",
+ /* Newline */ "newline",
+ /* LineContinuation */ "line continuation",
+ /* Comment */ "comment",
+ /* EndOfInput */ "eof",
+
+ #endregion Text for unclassified tokens
+
+ #region Text for strings
+
+ /* StringLiteral */ "sqstr",
+ /* StringExpandable */ "dqstr",
+ /* HereStringLiteral */ "sq here string",
+ /* HereStringExpandable */ "dq here string",
+
+ #endregion Text for strings
+
+ #region Text for punctuators
+
+ /* LParen */ "(",
+ /* RParen */ ")",
+ /* LCurly */ "{",
+ /* RCurly */ "}",
+ /* LBracket */ "[",
+ /* RBracket */ "]",
+ /* AtParen */ "@(",
+ /* AtCurly */ "@{",
+ /* DollarParen */ "$(",
+ /* Semi */ ";",
+
+ #endregion Text for punctuators
+
+ #region Text for operators
+
+ /* AndAnd */ "&&",
+ /* OrOr */ "||",
+ /* Ampersand */ "&",
+ /* AmpersandExclaim */ "&!",
+ /* Pipe */ "|",
+ /* Comma */ ",",
+ /* MinusMinus */ "--",
+ /* PlusPlus */ "++",
+ /* DotDot */ "..",
+ /* ColonColon */ "::",
+ /* Dot */ ".",
+ /* Exclaim */ "!",
+ /* Multiply */ "*",
+ /* Divide */ "/",
+ /* Rem */ "%",
+ /* Plus */ "+",
+ /* Minus */ "-",
+ /* Equals */ "=",
+ /* PlusEquals */ "+=",
+ /* MinusEquals */ "-=",
+ /* MultiplyEquals */ "*=",
+ /* DivideEquals */ "/=",
+ /* RemainderEquals */ "%=",
+ /* Redirection */ "redirection",
+ /* RedirectInStd */ "<",
+ /* Format */ "-f",
+ /* Not */ "-not",
+ /* Bnot */ "-bnot",
+ /* And */ "-and",
+ /* Or */ "-or",
+ /* Xor */ "-xor",
+ /* Band */ "-band",
+ /* Bor */ "-bor",
+ /* Bxor */ "-bxor",
+ /* Join */ "-join",
+ /* Ieq */ "-eq",
+ /* Ine */ "-ne",
+ /* Ige */ "-ge",
+ /* Igt */ "-gt",
+ /* Ilt */ "-lt",
+ /* Ile */ "-le",
+ /* Ilike */ "-ilike",
+ /* Inotlike */ "-inotlike",
+ /* Imatch */ "-imatch",
+ /* Inotmatch */ "-inotmatch",
+ /* Ireplace */ "-ireplace",
+ /* Icontains */ "-icontains",
+ /* Inotcontains */ "-inotcontains",
+ /* Iin */ "-iin",
+ /* Inotin */ "-inotin",
+ /* Isplit */ "-isplit",
+ /* Ceq */ "-ceq",
+ /* Cne */ "-cne",
+ /* Cge */ "-cge",
+ /* Cgt */ "-cgt",
+ /* Clt */ "-clt",
+ /* Cle */ "-cle",
+ /* Clike */ "-clike",
+ /* Cnotlike */ "-cnotlike",
+ /* Cmatch */ "-cmatch",
+ /* Cnotmatch */ "-cnotmatch",
+ /* Creplace */ "-creplace",
+ /* Ccontains */ "-ccontains",
+ /* Cnotcontains */ "-cnotcontains",
+ /* Cin */ "-cin",
+ /* Cnotin */ "-cnotin",
+ /* Csplit */ "-csplit",
+ /* Is */ "-is",
+ /* IsNot */ "-isnot",
+ /* As */ "-as",
+ /* PostFixPlusPlus */ "++",
+ /* PostFixMinusMinus */ "--",
+ /* Shl */ "-shl",
+ /* Shr */ "-shr",
+ /* Colon */ ":",
+ /* QuestionMark */ "?",
+ /* QuestionQuestionEquals */ "??=",
+ /* QuestionQuestion */ "??",
+ /* QuestionDot */ "?.",
+ /* QuestionLBracket */ "?[",
+ /* Reserved slot 7 */ string.Empty,
+ /* Reserved slot 8 */ string.Empty,
+ /* Reserved slot 9 */ string.Empty,
+ /* Reserved slot 10 */ string.Empty,
+ /* Reserved slot 11 */ string.Empty,
+ /* Reserved slot 12 */ string.Empty,
+ /* Reserved slot 13 */ string.Empty,
+ /* Reserved slot 14 */ string.Empty,
+ /* Reserved slot 15 */ string.Empty,
+ /* Reserved slot 16 */ string.Empty,
+ /* Reserved slot 17 */ string.Empty,
+ /* Reserved slot 18 */ string.Empty,
+ /* Reserved slot 19 */ string.Empty,
+ /* Reserved slot 20 */ string.Empty,
+
+ #endregion Text for operators
+
+ #region Text for keywords
+
+ /* Begin */ "begin",
+ /* Break */ "break",
+ /* Catch */ "catch",
+ /* Class */ "class",
+ /* Continue */ "continue",
+ /* Data */ "data",
+ /* Define */ "define",
+ /* Do */ "do",
+ /* Dynamicparam */ "dynamicparam",
+ /* Else */ "else",
+ /* ElseIf */ "elseif",
+ /* End */ "end",
+ /* Exit */ "exit",
+ /* Filter */ "filter",
+ /* Finally */ "finally",
+ /* For */ "for",
+ /* Foreach */ "foreach",
+ /* From */ "from",
+ /* Function */ "function",
+ /* If */ "if",
+ /* In */ "in",
+ /* Param */ "param",
+ /* Process */ "process",
+ /* Return */ "return",
+ /* Switch */ "switch",
+ /* Throw */ "throw",
+ /* Trap */ "trap",
+ /* Try */ "try",
+ /* Until */ "until",
+ /* Using */ "using",
+ /* Var */ "var",
+ /* While */ "while",
+ /* Workflow */ "workflow",
+ /* Parallel */ "parallel",
+ /* Sequence */ "sequence",
+ /* InlineScript */ "inlinescript",
+ /* Configuration */ "configuration",
+ /* */ "",
+ /* Public */ "public",
+ /* Private */ "private",
+ /* Static */ "static",
+ /* Interface */ "interface",
+ /* Enum */ "enum",
+ /* Namespace */ "namespace",
+ /* Module */ "module",
+ /* Type */ "type",
+ /* Assembly */ "assembly",
+ /* Command */ "command",
+ /* Hidden */ "hidden",
+ /* Base */ "base",
+ /* Default */ "default",
+ /* Clean */ "clean",
+
+ #endregion Text for keywords
+ };
+
+#if DEBUG
+ static TokenTraits()
+ {
+ Diagnostics.Assert(
+ s_staticTokenFlags.Length == ((int)TokenKind.Clean + 1),
+ "Table size out of sync with enum - _staticTokenFlags");
+ Diagnostics.Assert(
+ s_tokenText.Length == ((int)TokenKind.Clean + 1),
+ "Table size out of sync with enum - _tokenText");
+ // Some random assertions to make sure the enum and the traits are in sync
+ Diagnostics.Assert(GetTraits(TokenKind.Begin) == (TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName),
+ "Table out of sync with enum - flags Begin");
+ Diagnostics.Assert(GetTraits(TokenKind.Workflow) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes),
+ "Table out of sync with enum - flags Workflow");
+ Diagnostics.Assert(GetTraits(TokenKind.Sequence) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes),
+ "Table out of sync with enum - flags Sequence");
+ Diagnostics.Assert(GetTraits(TokenKind.Shr) == (TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold),
+ "Table out of sync with enum - flags Shr");
+ Diagnostics.Assert(s_tokenText[(int)TokenKind.Shr].Equals("-shr", StringComparison.OrdinalIgnoreCase),
+ "Table out of sync with enum - text Shr");
+ }
+#endif
+
+ ///
+ /// Return all the flags for a given .
+ ///
+ public static TokenFlags GetTraits(this TokenKind kind)
+ {
+ return s_staticTokenFlags[(int)kind];
+ }
+
+ ///
+ /// Return true if the has the given trait.
+ ///
+ public static bool HasTrait(this TokenKind kind, TokenFlags flag)
+ {
+ return (GetTraits(kind) & flag) != TokenFlags.None;
+ }
+
+ internal static int GetBinaryPrecedence(this TokenKind kind)
+ {
+ Diagnostics.Assert(HasTrait(kind, TokenFlags.BinaryOperator), "Token doesn't have binary precedence.");
+ return (int)(s_staticTokenFlags[(int)kind] & TokenFlags.BinaryPrecedenceMask);
+ }
+
+ ///
+ /// Return the text for a given .
+ ///
+ public static string Text(this TokenKind kind)
+ {
+ return s_tokenText[(int)kind];
+ }
+ }
+
+ ///
+ /// Represents many of the various PowerShell tokens, and is the base class for all PowerShell tokens.
+ ///
+ public class Token
+ {
+ private TokenKind _kind;
+ private TokenFlags _tokenFlags;
+ private readonly InternalScriptExtent _scriptExtent;
+
+ internal Token(InternalScriptExtent scriptExtent, TokenKind kind, TokenFlags tokenFlags)
+ {
+ _scriptExtent = scriptExtent;
+ _kind = kind;
+ _tokenFlags = tokenFlags | kind.GetTraits();
+ }
+
+ internal void SetIsCommandArgument()
+ {
+ // Rather than expose the setter, we have an explicit method to change a token so that it is
+ // considered a command argument. This prevent arbitrary changes to the kind which should be safer.
+ if (_kind != TokenKind.Identifier)
+ {
+ _kind = TokenKind.Generic;
+ }
+ }
+
+ ///
+ /// Return the text of the token as it appeared in the script.
+ ///
+ public string Text { get { return _scriptExtent.Text; } }
+
+ ///
+ /// Return the flags for the token.
+ ///
+ public TokenFlags TokenFlags { get { return _tokenFlags; } internal set { _tokenFlags = value; } }
+
+ ///
+ /// Return the kind of token.
+ ///
+ public TokenKind Kind { get { return _kind; } }
+
+ ///
+ /// Returns true if the token is in error somehow, such as missing a closing quote.
+ ///
+ public bool HasError { get { return (_tokenFlags & TokenFlags.TokenInError) != 0; } }
+
+ ///
+ /// Return the extent in the script of the token.
+ ///
+ public IScriptExtent Extent { get { return _scriptExtent; } }
+
+ ///
+ /// Return the text of the token as it appeared in the script.
+ ///
+ public override string ToString()
+ {
+ return (_kind == TokenKind.EndOfInput) ? "" : Text;
+ }
+
+ internal virtual string ToDebugString(int indent)
+ {
+ return string.Create(CultureInfo.InvariantCulture, $"{StringUtil.Padding(indent)}{_kind}: <{Text}>");
+ }
+ }
+
+ ///
+ /// A constant number token. The value may be any numeric type including int, long, double, or decimal.
+ ///
+ public class NumberToken : Token
+ {
+ private readonly object _value;
+
+ internal NumberToken(InternalScriptExtent scriptExtent, object value, TokenFlags tokenFlags)
+ : base(scriptExtent, TokenKind.Number, tokenFlags)
+ {
+ _value = value;
+ }
+
+ internal override string ToDebugString(int indent)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}{1}: <{2}> Value:<{3}> Type:<{4}>",
+ StringUtil.Padding(indent),
+ Kind,
+ Text,
+ _value,
+ _value.GetType().Name);
+ }
+
+ ///
+ /// The numeric value of this token.
+ ///
+ public object Value { get { return _value; } }
+ }
+
+ ///
+ /// A parameter to a cmdlet (always starts with a dash, like -Path).
+ ///
+ public class ParameterToken : Token
+ {
+ private readonly string _parameterName;
+ private readonly bool _usedColon;
+
+ internal ParameterToken(InternalScriptExtent scriptExtent, string parameterName, bool usedColon)
+ : base(scriptExtent, TokenKind.Parameter, TokenFlags.None)
+ {
+ Diagnostics.Assert(!string.IsNullOrEmpty(parameterName), "parameterName can't be null or empty");
+ _parameterName = parameterName;
+ _usedColon = usedColon;
+ }
+
+ ///
+ /// The parameter name without the leading dash. It is never
+ /// null or an empty string.
+ ///
+ public string ParameterName { get { return _parameterName; } }
+
+ ///
+ /// When passing an parameter with argument in the form:
+ /// dir -Path:*
+ /// The colon is part of the ParameterToken. This property
+ /// returns true when this form is used, false otherwise.
+ ///
+ public bool UsedColon { get { return _usedColon; } }
+
+ internal override string ToDebugString(int indent)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}{1}: <-{2}{3}>",
+ StringUtil.Padding(indent),
+ Kind,
+ _parameterName,
+ _usedColon ? ":" : string.Empty);
+ }
+ }
+
+ ///
+ /// A variable token - either a regular variable, such as $_, or a splatted variable like @PSBoundParameters.
+ ///
+ public class VariableToken : Token
+ {
+ internal VariableToken(InternalScriptExtent scriptExtent, VariablePath path, TokenFlags tokenFlags, bool splatted)
+ : base(scriptExtent, splatted ? TokenKind.SplattedVariable : TokenKind.Variable, tokenFlags)
+ {
+ VariablePath = path;
+ }
+
+ ///
+ /// The simple name of the variable, without any scope or drive qualification.
+ ///
+ public string Name { get { return VariablePath.UnqualifiedPath; } }
+
+ ///
+ /// The full details of the variable path.
+ ///
+ public VariablePath VariablePath { get; }
+
+ internal override string ToDebugString(int indent)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}{1}: <{2}> Name:<{3}>",
+ StringUtil.Padding(indent),
+ Kind,
+ Text,
+ Name);
+ }
+ }
+
+ ///
+ /// The base class for any string token, including single quoted string, double quoted strings, and here strings.
+ ///
+ public abstract class StringToken : Token
+ {
+ internal StringToken(InternalScriptExtent scriptExtent, TokenKind kind, TokenFlags tokenFlags, string value)
+ : base(scriptExtent, kind, tokenFlags)
+ {
+ Value = value;
+ }
+
+ ///
+ /// The string value without quotes or leading newlines in the case of a here string.
+ ///
+ public string Value { get; }
+
+ internal override string ToDebugString(int indent)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0}{1}: <{2}> Value:<{3}>",
+ StringUtil.Padding(indent),
+ Kind,
+ Text,
+ Value);
+ }
+ }
+
+ ///
+ /// A single quoted string, or a single quoted here string.
+ ///
+ public class StringLiteralToken : StringToken
+ {
+ internal StringLiteralToken(InternalScriptExtent scriptExtent, TokenFlags flags, TokenKind tokenKind, string value)
+ : base(scriptExtent, tokenKind, flags, value)
+ {
+ }
+ }
+
+ ///
+ /// A double quoted string, or a double quoted here string.
+ ///
+ public class StringExpandableToken : StringToken
+ {
+ private ReadOnlyCollection _nestedTokens;
+
+ internal StringExpandableToken(InternalScriptExtent scriptExtent, TokenKind tokenKind, string value, string formatString, List nestedTokens, TokenFlags flags)
+ : base(scriptExtent, tokenKind, flags, value)
+ {
+ if (nestedTokens != null && nestedTokens.Count > 0)
+ {
+ _nestedTokens = new ReadOnlyCollection(nestedTokens.ToArray());
+ }
+
+ FormatString = formatString;
+ }
+
+ internal static void ToDebugString(ReadOnlyCollection nestedTokens,
+ StringBuilder sb, int indent)
+ {
+ Diagnostics.Assert(nestedTokens != null, "caller to verify");
+
+ foreach (Token token in nestedTokens)
+ {
+ sb.Append(Environment.NewLine);
+ sb.Append(token.ToDebugString(indent + 4));
+ }
+ }
+
+ ///
+ /// This collection holds any tokens from variable references and sub-expressions within the string.
+ /// For example:
+ /// "In $([DateTime]::Now.Year - $age), $name was born"
+ /// has a nested expression with a sequence of tokens, plus the variable reference $name.
+ ///
+ public ReadOnlyCollection NestedTokens
+ {
+ get { return _nestedTokens; }
+
+ internal set { _nestedTokens = value; }
+ }
+
+ internal string FormatString { get; }
+
+ internal override string ToDebugString(int indent)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.Append(base.ToDebugString(indent));
+ if (_nestedTokens != null)
+ {
+ ToDebugString(_nestedTokens, sb, indent);
+ }
+
+ return sb.ToString();
+ }
+ }
+
+ ///
+ ///
+ public class LabelToken : Token
+ {
+ internal LabelToken(InternalScriptExtent scriptExtent, TokenFlags tokenFlags, string labelText)
+ : base(scriptExtent, TokenKind.Label, tokenFlags)
+ {
+ LabelText = labelText;
+ }
+
+ ///
+ ///
+ public string LabelText { get; }
+ }
+
+ ///
+ /// An abstract base class for merging and file redirections.
+ ///
+ public abstract class RedirectionToken : Token
+ {
+ internal RedirectionToken(InternalScriptExtent scriptExtent, TokenKind kind)
+ : base(scriptExtent, kind, TokenFlags.None)
+ {
+ }
+ }
+
+ ///
+ /// The (currently unimplemented) input redirection.
+ ///
+ public class InputRedirectionToken : RedirectionToken
+ {
+ internal InputRedirectionToken(InternalScriptExtent scriptExtent)
+ : base(scriptExtent, TokenKind.RedirectInStd)
+ {
+ }
+ }
+
+ ///
+ /// A merging redirection.
+ ///
+ public class MergingRedirectionToken : RedirectionToken
+ {
+ internal MergingRedirectionToken(InternalScriptExtent scriptExtent, RedirectionStream from, RedirectionStream to)
+ : base(scriptExtent, TokenKind.Redirection)
+ {
+ this.FromStream = from;
+ this.ToStream = to;
+ }
+
+ ///
+ /// The stream being redirected.
+ ///
+ public RedirectionStream FromStream { get; }
+
+ ///
+ /// The stream being written to.
+ ///
+ public RedirectionStream ToStream { get; }
+ }
+
+ ///
+ /// A file redirection.
+ ///
+ public class FileRedirectionToken : RedirectionToken
+ {
+ internal FileRedirectionToken(InternalScriptExtent scriptExtent, RedirectionStream from, bool append)
+ : base(scriptExtent, TokenKind.Redirection)
+ {
+ this.FromStream = from;
+ this.Append = append;
+ }
+
+ ///
+ /// The stream being redirected.
+ ///
+ public RedirectionStream FromStream { get; }
+
+ ///
+ /// True if the redirection should append the file rather than create a new file.
+ ///
+ public bool Append { get; }
+ }
+
+ internal class UnscannedSubExprToken : StringLiteralToken
+ {
+ internal UnscannedSubExprToken(InternalScriptExtent scriptExtent, TokenFlags tokenFlags, string value, BitArray skippedCharOffsets)
+ : base(scriptExtent, tokenFlags, TokenKind.StringLiteral, value)
+ {
+ this.SkippedCharOffsets = skippedCharOffsets;
+ }
+
+ internal BitArray SkippedCharOffsets { get; }
+ }
+}
From d5ee4ab3f17ec580817b41936a380da76f4045d9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 03:12:51 +0000
Subject: [PATCH 09/24] Remove accidentally committed backup file
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/parser/token.cs.backup | 1580 -----------------
1 file changed, 1580 deletions(-)
delete mode 100644 src/System.Management.Automation/engine/parser/token.cs.backup
diff --git a/src/System.Management.Automation/engine/parser/token.cs.backup b/src/System.Management.Automation/engine/parser/token.cs.backup
deleted file mode 100644
index 7f7369b2605..00000000000
--- a/src/System.Management.Automation/engine/parser/token.cs.backup
+++ /dev/null
@@ -1,1580 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.Management.Automation.Internal;
-using System.Text;
-
-namespace System.Management.Automation.Language
-{
- ///
- /// The specific kind of token.
- ///
- [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
- public enum TokenKind
- {
- // When adding any new tokens, be sure to update the following tables:
- // * TokenTraits.StaticTokenFlags
- // * TokenTraits.Text
-
- #region Unclassified Tokens
-
- /// An unknown token, signifies an error condition.
- Unknown = 0,
-
- ///
- /// A variable token, always begins with '$' and followed by the variable name, possibly enclose in curly braces.
- /// Tokens with this kind are always instances of .
- ///
- Variable = 1,
-
- ///
- /// A splatted variable token, always begins with '@' and followed by the variable name.
- /// Tokens with this kind are always instances of .
- ///
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- SplattedVariable = 2,
-
- ///
- /// A parameter to a command, always begins with a dash ('-'), followed by the parameter name.
- /// Tokens with this kind are always instances of .
- ///
- Parameter = 3,
-
- ///
- /// Any numerical literal token.
- /// Tokens with this kind are always instances of .
- ///
- Number = 4,
-
- ///
- /// A label token - always begins with ':', followed by the label name.
- /// Tokens with this kind are always instances of .
- ///
- Label = 5,
-
- ///
- /// A simple identifier, always begins with a letter or '_', and is followed by letters, numbers, or '_'.
- ///
- Identifier = 6,
-
- ///
- /// A token that is only valid as a command name, command argument, function name, or configuration name. It may contain
- /// characters not allowed in identifiers.
- /// Tokens with this kind are always instances of
- /// or if the token contains variable
- /// references or subexpressions.
- ///
- Generic = 7,
-
- /// A newline (one of '\n', '\r', or '\r\n').
- NewLine = 8,
-
- /// A line continuation (backtick followed by newline).
- LineContinuation = 9,
-
- /// A single line comment, or a delimited comment.
- Comment = 10,
-
- /// Marks the end of the input script or file.
- EndOfInput = 11,
-
- #endregion Unclassified Tokens
-
- #region Strings
-
- ///
- /// A single quoted string literal.
- /// Tokens with this kind are always instances of .
- ///
- StringLiteral = 12,
-
- ///
- /// A double quoted string literal.
- /// Tokens with this kind are always instances of
- /// even if there are no nested tokens to expand.
- ///
- StringExpandable = 13,
-
- ///
- /// A single quoted here string literal.
- /// Tokens with this kind are always instances of .
- ///
- HereStringLiteral = 14,
-
- ///
- /// A double quoted here string literal.
- /// Tokens with this kind are always instances of .
- /// even if there are no nested tokens to expand.
- ///
- HereStringExpandable = 15,
-
- #endregion Strings
-
- #region Punctuators
-
- /// The opening parenthesis token '('.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- LParen = 16,
-
- /// The closing parenthesis token ')'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- RParen = 17,
-
- /// The opening curly brace token '{'.
- LCurly = 18,
-
- /// The closing curly brace token '}'.
- RCurly = 19,
-
- /// The opening square brace token '['.
- LBracket = 20,
-
- /// The closing square brace token ']'.
- RBracket = 21,
-
- /// The opening token of an array expression '@('.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- AtParen = 22,
-
- /// The opening token of a hash expression '@{'.
- AtCurly = 23,
-
- /// The opening token of a sub-expression '$('.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- DollarParen = 24,
-
- /// The statement terminator ';'.
- Semi = 25,
-
- #endregion Punctuators
-
- #region Operators
-
- /// The (unimplemented) operator '&&'.
- AndAnd = 26,
-
- /// The (unimplemented) operator '||'.
- OrOr = 27,
-
- /// The invocation operator '&'.
- Ampersand = 28,
-
- /// The ThreadJob background operator '&!'.
- AmpersandExclaim = 29,
-
- /// The pipe operator '|'.
- Pipe = 30,
-
- /// The unary or binary array operator ','.
- Comma = 31,
-
- /// The pre-decrement operator '--'.
- MinusMinus = 32,
-
- /// The pre-increment operator '++'.
- PlusPlus = 33,
-
- /// The range operator '..'.
- DotDot = 34,
-
- /// The static member access operator '::'.
- ColonColon = 35,
-
- /// The instance member access or dot source invocation operator '.'.
- Dot = 36,
-
- /// The logical not operator '!'.
- Exclaim = 37,
-
- /// The multiplication operator '*'.
- Multiply = 38,
-
- /// The division operator '/'.
- Divide = 39,
-
- /// The modulo division (remainder) operator '%'.
- Rem = 40,
-
- /// The addition operator '+'.
- Plus = 41,
-
- /// The subtraction operator '-'.
- Minus = 42,
-
- /// The assignment operator '='.
- Equals = 43,
-
- /// The addition assignment operator '+='.
- PlusEquals = 44,
-
- /// The subtraction assignment operator '-='.
- MinusEquals = 45,
-
- /// The multiplication assignment operator '*='.
- MultiplyEquals = 46,
-
- /// The division assignment operator '/='.
- DivideEquals = 47,
-
- /// The modulo division (remainder) assignment operator '%='.
- RemainderEquals = 48,
-
- /// A redirection operator such as '2>&1' or '>>'.
- Redirection = 49,
-
- /// The (unimplemented) stdin redirection operator '<'.
- RedirectInStd = 50,
-
- /// The string format operator '-f'.
- Format = 51,
-
- /// The logical not operator '-not'.
- Not = 52,
-
- /// The bitwise not operator '-bnot'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bnot = 53,
-
- /// The logical and operator '-and'.
- And = 54,
-
- /// The logical or operator '-or'.
- Or = 55,
-
- /// The logical exclusive or operator '-xor'.
- Xor = 56,
-
- /// The bitwise and operator '-band'.
- Band = 57,
-
- /// The bitwise or operator '-bor'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bor = 58,
-
- /// The bitwise exclusive or operator '-xor'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Bxor = 59,
-
- /// The join operator '-join'.
- Join = 60,
-
- /// The case insensitive equal operator '-ieq' or '-eq'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ieq = 61,
-
- /// The case insensitive not equal operator '-ine' or '-ne'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ine = 62,
-
- /// The case insensitive greater than or equal operator '-ige' or '-ge'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ige = 63,
-
- /// The case insensitive greater than operator '-igt' or '-gt'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Igt = 64,
-
- /// The case insensitive less than operator '-ilt' or '-lt'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilt = 65,
-
- /// The case insensitive less than or equal operator '-ile' or '-le'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ile = 66,
-
- /// The case insensitive like operator '-ilike' or '-like'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ilike = 67,
-
- /// The case insensitive not like operator '-inotlike' or '-notlike'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotlike = 68,
-
- /// The case insensitive match operator '-imatch' or '-match'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Imatch = 69,
-
- /// The case insensitive not match operator '-inotmatch' or '-notmatch'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotmatch = 70,
-
- /// The case insensitive replace operator '-ireplace' or '-replace'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ireplace = 71,
-
- /// The case insensitive contains operator '-icontains' or '-contains'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Icontains = 72,
-
- /// The case insensitive notcontains operator '-inotcontains' or '-notcontains'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotcontains = 73,
-
- /// The case insensitive in operator '-iin' or '-in'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Iin = 74,
-
- /// The case insensitive notin operator '-inotin' or '-notin'
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Inotin = 75,
-
- /// The case insensitive split operator '-isplit' or '-split'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Isplit = 76,
-
- /// The case sensitive equal operator '-ceq'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ceq = 77,
-
- /// The case sensitive not equal operator '-cne'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cne = 78,
-
- /// The case sensitive greater than or equal operator '-cge'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cge = 79,
-
- /// The case sensitive greater than operator '-cgt'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cgt = 80,
-
- /// The case sensitive less than operator '-clt'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clt = 81,
-
- /// The case sensitive less than or equal operator '-cle'.
- Cle = 82,
-
- /// The case sensitive like operator '-clike'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Clike = 83,
-
- /// The case sensitive notlike operator '-cnotlike'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotlike = 84,
-
- /// The case sensitive match operator '-cmatch'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cmatch = 85,
-
- /// The case sensitive not match operator '-cnotmatch'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotmatch = 86,
-
- /// The case sensitive replace operator '-creplace'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Creplace = 87,
-
- /// The case sensitive contains operator '-ccontains'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Ccontains = 88,
-
- /// The case sensitive not contains operator '-cnotcontains'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotcontains = 89,
-
- /// The case sensitive in operator '-cin'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cin = 90,
-
- /// The case sensitive not in operator '-notin'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Cnotin = 91,
-
- /// The case sensitive split operator '-csplit'.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Csplit = 92,
-
- /// The type test operator '-is'.
- Is = 93,
-
- /// The type test operator '-isnot'.
- IsNot = 94,
-
- /// The type conversion operator '-as'.
- As = 95,
-
- /// The post-increment operator '++'.
- PostfixPlusPlus = 96,
-
- /// The post-decrement operator '--'.
- PostfixMinusMinus = 97,
-
- /// The shift left operator.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shl = 98,
-
- /// The shift right operator.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Shr = 99,
-
- /// The PS class base class and implemented interfaces operator ':'. Also used in base class ctor calls.
- Colon = 100,
-
- /// The ternary operator '?'.
- QuestionMark = 101,
-
- /// The null conditional assignment operator '??='.
- QuestionQuestionEquals = 102,
-
- /// The null coalesce operator '??'.
- QuestionQuestion = 103,
-
- /// The null conditional member access operator '?.'.
- QuestionDot = 104,
-
- /// The null conditional index access operator '?[]'.
- QuestionLBracket = 105,
-
- #endregion Operators
-
- #region Keywords
-
- /// The 'begin' keyword.
- Begin = 120,
-
- /// The 'break' keyword.
- Break = 121,
-
- /// The 'catch' keyword.
- Catch = 122,
-
- /// The 'class' keyword.
- Class = 123,
-
- /// The 'continue' keyword.
- Continue = 124,
-
- /// The 'data' keyword.
- Data = 125,
-
- /// The (unimplemented) 'define' keyword.
- Define = 126,
-
- /// The 'do' keyword.
- Do = 127,
-
- /// The 'dynamicparam' keyword.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Dynamicparam = 128,
-
- /// The 'else' keyword.
- Else = 129,
-
- /// The 'elseif' keyword.
- ElseIf = 130,
-
- /// The 'end' keyword.
- End = 131,
-
- /// The 'exit' keyword.
- Exit = 132,
-
- /// The 'filter' keyword.
- Filter = 133,
-
- /// The 'finally' keyword.
- Finally = 134,
-
- /// The 'for' keyword.
- For = 135,
-
- /// The 'foreach' keyword.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Foreach = 136,
-
- /// The (unimplemented) 'from' keyword.
- From = 137,
-
- /// The 'function' keyword.
- Function = 138,
-
- /// The 'if' keyword.
- If = 139,
-
- /// The 'in' keyword.
- In = 140,
-
- /// The 'param' keyword.
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
- Param = 141,
-
- /// The 'process' keyword.
- Process = 142,
-
- /// The 'return' keyword.
- Return = 143,
-
- /// The 'switch' keyword.
- Switch = 144,
-
- /// The 'throw' keyword.
- Throw = 145,
-
- /// The 'trap' keyword.
- Trap = 146,
-
- /// The 'try' keyword.
- Try = 147,
-
- /// The 'until' keyword.
- Until = 148,
-
- /// The (unimplemented) 'using' keyword.
- Using = 149,
-
- /// The (unimplemented) 'var' keyword.
- Var = 150,
-
- /// The 'while' keyword.
- While = 151,
-
- /// The 'workflow' keyword.
- Workflow = 152,
-
- /// The 'parallel' keyword.
- Parallel = 153,
-
- /// The 'sequence' keyword.
- Sequence = 154,
-
- /// The 'InlineScript' keyword
- InlineScript = 155,
-
- /// The "configuration" keyword
- Configuration = 156,
-
- /// The token kind for dynamic keywords
- DynamicKeyword = 157,
-
- /// The 'public' keyword
- Public = 158,
-
- /// The 'private' keyword
- Private = 159,
-
- /// The 'static' keyword
- Static = 160,
-
- /// The 'interface' keyword
- Interface = 161,
-
- /// The 'enum' keyword
- Enum = 162,
-
- /// The 'namespace' keyword
- Namespace = 163,
-
- /// The 'module' keyword
- Module = 164,
-
- /// The 'type' keyword
- Type = 165,
-
- /// The 'assembly' keyword
- Assembly = 166,
-
- /// The 'command' keyword
- Command = 167,
-
- /// The 'hidden' keyword
- Hidden = 168,
-
- /// The 'base' keyword
- Base = 169,
-
- /// The 'default' keyword
- Default = 170,
-
- /// The 'clean' keyword.
- Clean = 171,
-
- #endregion Keywords
- }
-
- ///
- /// Flags that specify additional information about a given token.
- ///
- [Flags]
- public enum TokenFlags
- {
- ///
- /// The token has no flags.
- ///
- None = 0x00000000,
-
- #region Precedence Values
-
- ///
- /// The precedence of the logical operators '-and', '-or', and '-xor'.
- ///
- BinaryPrecedenceLogical = 0x1,
-
- ///
- /// The precedence of the bitwise operators '-band', '-bor', and '-bxor'
- ///
- BinaryPrecedenceBitwise = 0x2,
-
- ///
- /// The precedence of comparison operators including: '-eq', '-ne', '-ge', '-gt', '-lt', '-le', '-like', '-notlike',
- /// '-match', '-notmatch', '-replace', '-contains', '-notcontains', '-in', '-notin', '-split', '-join', '-is', '-isnot', '-as',
- /// and all of the case sensitive variants of these operators, if they exists.
- ///
- BinaryPrecedenceComparison = 0x5,
-
- ///
- /// The precedence of null coalesce operator '??'.
- ///
- BinaryPrecedenceCoalesce = 0x7,
-
- ///
- /// The precedence of the binary operators '+' and '-'.
- ///
- BinaryPrecedenceAdd = 0x9,
-
- ///
- /// The precedence of the operators '*', '/', and '%'.
- ///
- BinaryPrecedenceMultiply = 0xa,
-
- ///
- /// The precedence of the '-f' operator.
- ///
- BinaryPrecedenceFormat = 0xc,
-
- ///
- /// The precedence of the '..' operator.
- ///
- BinaryPrecedenceRange = 0xd,
-
- #endregion Precedence Values
-
- ///
- /// A bitmask to get the precedence of binary operators.
- ///
- BinaryPrecedenceMask = 0x0000000f,
-
- ///
- /// The token is a keyword.
- ///
- Keyword = 0x00000010,
-
- ///
- /// The token is one of the keywords that is a part of a script block: 'begin', 'process', 'end', 'clean', or 'dynamicparam'.
- ///
- ScriptBlockBlockName = 0x00000020,
-
- ///
- /// The token is a binary operator.
- ///
- BinaryOperator = 0x00000100,
-
- ///
- /// The token is a unary operator.
- ///
- UnaryOperator = 0x00000200,
-
- ///
- /// The token is a case sensitive operator such as '-ceq' or '-ccontains'.
- ///
- CaseSensitiveOperator = 0x00000400,
-
- ///
- /// The token is a ternary operator '?'.
- ///
- TernaryOperator = 0x00000800,
-
- ///
- /// The operators '&', '|', and the member access operators ':' and '::'.
- ///
- SpecialOperator = 0x00001000,
-
- ///
- /// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', '%=' or '??='
- ///
- AssignmentOperator = 0x00002000,
-
- ///
- /// The token is scanned identically in expression mode or command mode.
- ///
- ParseModeInvariant = 0x00008000,
-
- ///
- /// The token has some error associated with it. For example, it may be a string missing it's terminator.
- ///
- TokenInError = 0x00010000,
-
- ///
- /// The operator is not allowed in restricted language mode or in the data language.
- ///
- DisallowedInRestrictedMode = 0x00020000,
-
- ///
- /// The token is either a prefix or postfix '++' or '--'.
- ///
- PrefixOrPostfixOperator = 0x00040000,
-
- ///
- /// The token names a command in a pipeline.
- ///
- CommandName = 0x00080000,
-
- ///
- /// The token names a member of a class.
- ///
- MemberName = 0x00100000,
-
- ///
- /// The token names a type.
- ///
- TypeName = 0x00200000,
-
- ///
- /// The token names an attribute.
- ///
- AttributeName = 0x00400000,
-
- ///
- /// The token is a valid operator to use when doing constant folding.
- ///
- // Some operators that could be marked with this flag aren't because the current implementation depends
- // on the execution context (e.g. -split, -join, -like, etc.)
- // If the operator is culture sensitive (e.g. -f), then it shouldn't be marked as suitable for constant
- // folding because evaluation of the operator could differ if the thread's culture changes.
- CanConstantFold = 0x00800000,
-
- ///
- /// The token is a statement but does not support attributes.
- ///
- StatementDoesntSupportAttributes = 0x01000000,
- }
-
- ///
- /// A utility class to get statically known traits and invariant traits about PowerShell tokens.
- ///
- public static class TokenTraits
- {
- private static readonly TokenFlags[] s_staticTokenFlags = new TokenFlags[]
- {
- #region Flags for unclassified tokens
-
- /* Unknown */ TokenFlags.None,
- /* Variable */ TokenFlags.None,
- /* SplattedVariable */ TokenFlags.None,
- /* Parameter */ TokenFlags.None,
- /* Number */ TokenFlags.None,
- /* Label */ TokenFlags.None,
- /* Identifier */ TokenFlags.None,
-
- /* Generic */ TokenFlags.None,
- /* Newline */ TokenFlags.ParseModeInvariant,
- /* LineContinuation */ TokenFlags.ParseModeInvariant,
- /* Comment */ TokenFlags.ParseModeInvariant,
- /* EndOfInput */ TokenFlags.ParseModeInvariant,
-
- #endregion Flags for unclassified tokens
-
- #region Flags for strings
-
- /* StringLiteral */ TokenFlags.ParseModeInvariant,
- /* StringExpandable */ TokenFlags.ParseModeInvariant,
- /* HereStringLiteral */ TokenFlags.ParseModeInvariant,
- /* HereStringExpandable */ TokenFlags.ParseModeInvariant,
-
- #endregion Flags for strings
-
- #region Flags for punctuators
-
- /* LParen */ TokenFlags.ParseModeInvariant,
- /* RParen */ TokenFlags.ParseModeInvariant,
- /* LCurly */ TokenFlags.ParseModeInvariant,
- /* RCurly */ TokenFlags.ParseModeInvariant,
- /* LBracket */ TokenFlags.None,
- /* RBracket */ TokenFlags.ParseModeInvariant,
- /* AtParen */ TokenFlags.ParseModeInvariant,
- /* AtCurly */ TokenFlags.ParseModeInvariant,
- /* DollarParen */ TokenFlags.ParseModeInvariant,
- /* Semi */ TokenFlags.ParseModeInvariant,
-
- #endregion Flags for punctuators
-
- #region Flags for operators
-
- /* AndAnd */ TokenFlags.ParseModeInvariant,
- /* OrOr */ TokenFlags.ParseModeInvariant,
- /* Ampersand */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
- /* AmpersandExclaim */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
- /* Pipe */ TokenFlags.SpecialOperator | TokenFlags.ParseModeInvariant,
- /* Comma */ TokenFlags.UnaryOperator | TokenFlags.ParseModeInvariant,
- /* MinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
- /* PlusPlus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
- /* DotDot */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceRange | TokenFlags.DisallowedInRestrictedMode,
- /* ColonColon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Dot */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Exclaim */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
- /* Multiply */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
- /* Divide */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
- /* Rem */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceMultiply | TokenFlags.CanConstantFold,
- /* Plus */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceAdd | TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
- /* Minus */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceAdd | TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
- /* Equals */ TokenFlags.AssignmentOperator,
- /* PlusEquals */ TokenFlags.AssignmentOperator,
- /* MinusEquals */ TokenFlags.AssignmentOperator,
- /* MultiplyEquals */ TokenFlags.AssignmentOperator,
- /* DivideEquals */ TokenFlags.AssignmentOperator,
- /* RemainderEquals */ TokenFlags.AssignmentOperator,
- /* Redirection */ TokenFlags.DisallowedInRestrictedMode,
- /* RedirectInStd */ TokenFlags.ParseModeInvariant | TokenFlags.DisallowedInRestrictedMode,
- /* Format */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceFormat | TokenFlags.DisallowedInRestrictedMode,
- /* Not */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
- /* Bnot */ TokenFlags.UnaryOperator | TokenFlags.CanConstantFold,
- /* And */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
- /* Or */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
- /* Xor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceLogical | TokenFlags.CanConstantFold,
- /* Band */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
- /* Bor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
- /* Bxor */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceBitwise | TokenFlags.CanConstantFold,
- /* Join */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* Ieq */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Ine */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Ige */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Igt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Ilt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Ile */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Ilike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Inotlike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Imatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* Inotmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* Ireplace */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* Icontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Inotcontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Iin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Inotin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* Isplit */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* Ceq */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cne */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cge */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cgt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Clt */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cle */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Clike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cnotlike */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Cnotmatch */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Creplace */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Ccontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cnotcontains */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Cnotin */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator,
- /* Csplit */ TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CaseSensitiveOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Is */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* IsNot */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison,
- /* As */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.DisallowedInRestrictedMode,
- /* PostFixPlusPlus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
- /* PostFixMinusMinus */ TokenFlags.UnaryOperator | TokenFlags.PrefixOrPostfixOperator | TokenFlags.DisallowedInRestrictedMode,
- /* Shl */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
- /* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
- /* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
- /* QuestionMark */ TokenFlags.TernaryOperator | TokenFlags.DisallowedInRestrictedMode,
- /* QuestionQuestionEquals */ TokenFlags.AssignmentOperator,
- /* QuestionQuestion */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceCoalesce,
- /* QuestionDot */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
- /* QuestionLBracket */ TokenFlags.None,
- /* Reserved slot 7 */ TokenFlags.None,
- /* Reserved slot 8 */ TokenFlags.None,
- /* Reserved slot 9 */ TokenFlags.None,
- /* Reserved slot 10 */ TokenFlags.None,
- /* Reserved slot 11 */ TokenFlags.None,
- /* Reserved slot 12 */ TokenFlags.None,
- /* Reserved slot 13 */ TokenFlags.None,
- /* Reserved slot 14 */ TokenFlags.None,
- /* Reserved slot 15 */ TokenFlags.None,
- /* Reserved slot 16 */ TokenFlags.None,
- /* Reserved slot 17 */ TokenFlags.None,
- /* Reserved slot 18 */ TokenFlags.None,
- /* Reserved slot 19 */ TokenFlags.None,
- /* Reserved slot 20 */ TokenFlags.None,
-
- #endregion Flags for operators
-
- #region Flags for keywords
-
- /* Begin */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
- /* Break */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Catch */ TokenFlags.Keyword,
- /* Class */ TokenFlags.Keyword,
- /* Continue */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Data */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Define */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Do */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Dynamicparam */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
- /* Else */ TokenFlags.Keyword,
- /* ElseIf */ TokenFlags.Keyword,
- /* End */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
- /* Exit */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Filter */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Finally */ TokenFlags.Keyword,
- /* For */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Foreach */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* From */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Function */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* If */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* In */ TokenFlags.Keyword,
- /* Param */ TokenFlags.Keyword,
- /* Process */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
- /* Return */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Switch */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Throw */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Trap */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Try */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Until */ TokenFlags.Keyword,
- /* Using */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Var */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* While */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Workflow */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Parallel */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Sequence */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* InlineScript */ TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes,
- /* Configuration */ TokenFlags.Keyword,
- /* */ TokenFlags.Keyword,
- /* Public */ TokenFlags.Keyword,
- /* Private */ TokenFlags.Keyword,
- /* Static */ TokenFlags.Keyword,
- /* Interface */ TokenFlags.Keyword,
- /* Enum */ TokenFlags.Keyword,
- /* Namespace */ TokenFlags.Keyword,
- /* Module */ TokenFlags.Keyword,
- /* Type */ TokenFlags.Keyword,
- /* Assembly */ TokenFlags.Keyword,
- /* Command */ TokenFlags.Keyword,
- /* Hidden */ TokenFlags.Keyword,
- /* Base */ TokenFlags.Keyword,
- /* Default */ TokenFlags.Keyword,
- /* Clean */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName,
-
- #endregion Flags for keywords
- };
-
- private static readonly string[] s_tokenText = new string[]
- {
- #region Text for unclassified tokens
-
- /* Unknown */ "unknown",
- /* Variable */ "var",
- /* SplattedVariable */ "@var",
- /* Parameter */ "param",
- /* Number */ "number",
- /* Label */ "label",
- /* Identifier */ "ident",
-
- /* Generic */ "generic",
- /* Newline */ "newline",
- /* LineContinuation */ "line continuation",
- /* Comment */ "comment",
- /* EndOfInput */ "eof",
-
- #endregion Text for unclassified tokens
-
- #region Text for strings
-
- /* StringLiteral */ "sqstr",
- /* StringExpandable */ "dqstr",
- /* HereStringLiteral */ "sq here string",
- /* HereStringExpandable */ "dq here string",
-
- #endregion Text for strings
-
- #region Text for punctuators
-
- /* LParen */ "(",
- /* RParen */ ")",
- /* LCurly */ "{",
- /* RCurly */ "}",
- /* LBracket */ "[",
- /* RBracket */ "]",
- /* AtParen */ "@(",
- /* AtCurly */ "@{",
- /* DollarParen */ "$(",
- /* Semi */ ";",
-
- #endregion Text for punctuators
-
- #region Text for operators
-
- /* AndAnd */ "&&",
- /* OrOr */ "||",
- /* Ampersand */ "&",
- /* AmpersandExclaim */ "&!",
- /* Pipe */ "|",
- /* Comma */ ",",
- /* MinusMinus */ "--",
- /* PlusPlus */ "++",
- /* DotDot */ "..",
- /* ColonColon */ "::",
- /* Dot */ ".",
- /* Exclaim */ "!",
- /* Multiply */ "*",
- /* Divide */ "/",
- /* Rem */ "%",
- /* Plus */ "+",
- /* Minus */ "-",
- /* Equals */ "=",
- /* PlusEquals */ "+=",
- /* MinusEquals */ "-=",
- /* MultiplyEquals */ "*=",
- /* DivideEquals */ "/=",
- /* RemainderEquals */ "%=",
- /* Redirection */ "redirection",
- /* RedirectInStd */ "<",
- /* Format */ "-f",
- /* Not */ "-not",
- /* Bnot */ "-bnot",
- /* And */ "-and",
- /* Or */ "-or",
- /* Xor */ "-xor",
- /* Band */ "-band",
- /* Bor */ "-bor",
- /* Bxor */ "-bxor",
- /* Join */ "-join",
- /* Ieq */ "-eq",
- /* Ine */ "-ne",
- /* Ige */ "-ge",
- /* Igt */ "-gt",
- /* Ilt */ "-lt",
- /* Ile */ "-le",
- /* Ilike */ "-ilike",
- /* Inotlike */ "-inotlike",
- /* Imatch */ "-imatch",
- /* Inotmatch */ "-inotmatch",
- /* Ireplace */ "-ireplace",
- /* Icontains */ "-icontains",
- /* Inotcontains */ "-inotcontains",
- /* Iin */ "-iin",
- /* Inotin */ "-inotin",
- /* Isplit */ "-isplit",
- /* Ceq */ "-ceq",
- /* Cne */ "-cne",
- /* Cge */ "-cge",
- /* Cgt */ "-cgt",
- /* Clt */ "-clt",
- /* Cle */ "-cle",
- /* Clike */ "-clike",
- /* Cnotlike */ "-cnotlike",
- /* Cmatch */ "-cmatch",
- /* Cnotmatch */ "-cnotmatch",
- /* Creplace */ "-creplace",
- /* Ccontains */ "-ccontains",
- /* Cnotcontains */ "-cnotcontains",
- /* Cin */ "-cin",
- /* Cnotin */ "-cnotin",
- /* Csplit */ "-csplit",
- /* Is */ "-is",
- /* IsNot */ "-isnot",
- /* As */ "-as",
- /* PostFixPlusPlus */ "++",
- /* PostFixMinusMinus */ "--",
- /* Shl */ "-shl",
- /* Shr */ "-shr",
- /* Colon */ ":",
- /* QuestionMark */ "?",
- /* QuestionQuestionEquals */ "??=",
- /* QuestionQuestion */ "??",
- /* QuestionDot */ "?.",
- /* QuestionLBracket */ "?[",
- /* Reserved slot 7 */ string.Empty,
- /* Reserved slot 8 */ string.Empty,
- /* Reserved slot 9 */ string.Empty,
- /* Reserved slot 10 */ string.Empty,
- /* Reserved slot 11 */ string.Empty,
- /* Reserved slot 12 */ string.Empty,
- /* Reserved slot 13 */ string.Empty,
- /* Reserved slot 14 */ string.Empty,
- /* Reserved slot 15 */ string.Empty,
- /* Reserved slot 16 */ string.Empty,
- /* Reserved slot 17 */ string.Empty,
- /* Reserved slot 18 */ string.Empty,
- /* Reserved slot 19 */ string.Empty,
- /* Reserved slot 20 */ string.Empty,
-
- #endregion Text for operators
-
- #region Text for keywords
-
- /* Begin */ "begin",
- /* Break */ "break",
- /* Catch */ "catch",
- /* Class */ "class",
- /* Continue */ "continue",
- /* Data */ "data",
- /* Define */ "define",
- /* Do */ "do",
- /* Dynamicparam */ "dynamicparam",
- /* Else */ "else",
- /* ElseIf */ "elseif",
- /* End */ "end",
- /* Exit */ "exit",
- /* Filter */ "filter",
- /* Finally */ "finally",
- /* For */ "for",
- /* Foreach */ "foreach",
- /* From */ "from",
- /* Function */ "function",
- /* If */ "if",
- /* In */ "in",
- /* Param */ "param",
- /* Process */ "process",
- /* Return */ "return",
- /* Switch */ "switch",
- /* Throw */ "throw",
- /* Trap */ "trap",
- /* Try */ "try",
- /* Until */ "until",
- /* Using */ "using",
- /* Var */ "var",
- /* While */ "while",
- /* Workflow */ "workflow",
- /* Parallel */ "parallel",
- /* Sequence */ "sequence",
- /* InlineScript */ "inlinescript",
- /* Configuration */ "configuration",
- /* */ "",
- /* Public */ "public",
- /* Private */ "private",
- /* Static */ "static",
- /* Interface */ "interface",
- /* Enum */ "enum",
- /* Namespace */ "namespace",
- /* Module */ "module",
- /* Type */ "type",
- /* Assembly */ "assembly",
- /* Command */ "command",
- /* Hidden */ "hidden",
- /* Base */ "base",
- /* Default */ "default",
- /* Clean */ "clean",
-
- #endregion Text for keywords
- };
-
-#if DEBUG
- static TokenTraits()
- {
- Diagnostics.Assert(
- s_staticTokenFlags.Length == ((int)TokenKind.Clean + 1),
- "Table size out of sync with enum - _staticTokenFlags");
- Diagnostics.Assert(
- s_tokenText.Length == ((int)TokenKind.Clean + 1),
- "Table size out of sync with enum - _tokenText");
- // Some random assertions to make sure the enum and the traits are in sync
- Diagnostics.Assert(GetTraits(TokenKind.Begin) == (TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName),
- "Table out of sync with enum - flags Begin");
- Diagnostics.Assert(GetTraits(TokenKind.Workflow) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes),
- "Table out of sync with enum - flags Workflow");
- Diagnostics.Assert(GetTraits(TokenKind.Sequence) == (TokenFlags.Keyword | TokenFlags.StatementDoesntSupportAttributes),
- "Table out of sync with enum - flags Sequence");
- Diagnostics.Assert(GetTraits(TokenKind.Shr) == (TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold),
- "Table out of sync with enum - flags Shr");
- Diagnostics.Assert(s_tokenText[(int)TokenKind.Shr].Equals("-shr", StringComparison.OrdinalIgnoreCase),
- "Table out of sync with enum - text Shr");
- }
-#endif
-
- ///
- /// Return all the flags for a given .
- ///
- public static TokenFlags GetTraits(this TokenKind kind)
- {
- return s_staticTokenFlags[(int)kind];
- }
-
- ///
- /// Return true if the has the given trait.
- ///
- public static bool HasTrait(this TokenKind kind, TokenFlags flag)
- {
- return (GetTraits(kind) & flag) != TokenFlags.None;
- }
-
- internal static int GetBinaryPrecedence(this TokenKind kind)
- {
- Diagnostics.Assert(HasTrait(kind, TokenFlags.BinaryOperator), "Token doesn't have binary precedence.");
- return (int)(s_staticTokenFlags[(int)kind] & TokenFlags.BinaryPrecedenceMask);
- }
-
- ///
- /// Return the text for a given .
- ///
- public static string Text(this TokenKind kind)
- {
- return s_tokenText[(int)kind];
- }
- }
-
- ///
- /// Represents many of the various PowerShell tokens, and is the base class for all PowerShell tokens.
- ///
- public class Token
- {
- private TokenKind _kind;
- private TokenFlags _tokenFlags;
- private readonly InternalScriptExtent _scriptExtent;
-
- internal Token(InternalScriptExtent scriptExtent, TokenKind kind, TokenFlags tokenFlags)
- {
- _scriptExtent = scriptExtent;
- _kind = kind;
- _tokenFlags = tokenFlags | kind.GetTraits();
- }
-
- internal void SetIsCommandArgument()
- {
- // Rather than expose the setter, we have an explicit method to change a token so that it is
- // considered a command argument. This prevent arbitrary changes to the kind which should be safer.
- if (_kind != TokenKind.Identifier)
- {
- _kind = TokenKind.Generic;
- }
- }
-
- ///
- /// Return the text of the token as it appeared in the script.
- ///
- public string Text { get { return _scriptExtent.Text; } }
-
- ///
- /// Return the flags for the token.
- ///
- public TokenFlags TokenFlags { get { return _tokenFlags; } internal set { _tokenFlags = value; } }
-
- ///
- /// Return the kind of token.
- ///
- public TokenKind Kind { get { return _kind; } }
-
- ///
- /// Returns true if the token is in error somehow, such as missing a closing quote.
- ///
- public bool HasError { get { return (_tokenFlags & TokenFlags.TokenInError) != 0; } }
-
- ///
- /// Return the extent in the script of the token.
- ///
- public IScriptExtent Extent { get { return _scriptExtent; } }
-
- ///
- /// Return the text of the token as it appeared in the script.
- ///
- public override string ToString()
- {
- return (_kind == TokenKind.EndOfInput) ? "" : Text;
- }
-
- internal virtual string ToDebugString(int indent)
- {
- return string.Create(CultureInfo.InvariantCulture, $"{StringUtil.Padding(indent)}{_kind}: <{Text}>");
- }
- }
-
- ///
- /// A constant number token. The value may be any numeric type including int, long, double, or decimal.
- ///
- public class NumberToken : Token
- {
- private readonly object _value;
-
- internal NumberToken(InternalScriptExtent scriptExtent, object value, TokenFlags tokenFlags)
- : base(scriptExtent, TokenKind.Number, tokenFlags)
- {
- _value = value;
- }
-
- internal override string ToDebugString(int indent)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0}{1}: <{2}> Value:<{3}> Type:<{4}>",
- StringUtil.Padding(indent),
- Kind,
- Text,
- _value,
- _value.GetType().Name);
- }
-
- ///
- /// The numeric value of this token.
- ///
- public object Value { get { return _value; } }
- }
-
- ///
- /// A parameter to a cmdlet (always starts with a dash, like -Path).
- ///
- public class ParameterToken : Token
- {
- private readonly string _parameterName;
- private readonly bool _usedColon;
-
- internal ParameterToken(InternalScriptExtent scriptExtent, string parameterName, bool usedColon)
- : base(scriptExtent, TokenKind.Parameter, TokenFlags.None)
- {
- Diagnostics.Assert(!string.IsNullOrEmpty(parameterName), "parameterName can't be null or empty");
- _parameterName = parameterName;
- _usedColon = usedColon;
- }
-
- ///
- /// The parameter name without the leading dash. It is never
- /// null or an empty string.
- ///
- public string ParameterName { get { return _parameterName; } }
-
- ///
- /// When passing an parameter with argument in the form:
- /// dir -Path:*
- /// The colon is part of the ParameterToken. This property
- /// returns true when this form is used, false otherwise.
- ///
- public bool UsedColon { get { return _usedColon; } }
-
- internal override string ToDebugString(int indent)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0}{1}: <-{2}{3}>",
- StringUtil.Padding(indent),
- Kind,
- _parameterName,
- _usedColon ? ":" : string.Empty);
- }
- }
-
- ///
- /// A variable token - either a regular variable, such as $_, or a splatted variable like @PSBoundParameters.
- ///
- public class VariableToken : Token
- {
- internal VariableToken(InternalScriptExtent scriptExtent, VariablePath path, TokenFlags tokenFlags, bool splatted)
- : base(scriptExtent, splatted ? TokenKind.SplattedVariable : TokenKind.Variable, tokenFlags)
- {
- VariablePath = path;
- }
-
- ///
- /// The simple name of the variable, without any scope or drive qualification.
- ///
- public string Name { get { return VariablePath.UnqualifiedPath; } }
-
- ///
- /// The full details of the variable path.
- ///
- public VariablePath VariablePath { get; }
-
- internal override string ToDebugString(int indent)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0}{1}: <{2}> Name:<{3}>",
- StringUtil.Padding(indent),
- Kind,
- Text,
- Name);
- }
- }
-
- ///
- /// The base class for any string token, including single quoted string, double quoted strings, and here strings.
- ///
- public abstract class StringToken : Token
- {
- internal StringToken(InternalScriptExtent scriptExtent, TokenKind kind, TokenFlags tokenFlags, string value)
- : base(scriptExtent, kind, tokenFlags)
- {
- Value = value;
- }
-
- ///
- /// The string value without quotes or leading newlines in the case of a here string.
- ///
- public string Value { get; }
-
- internal override string ToDebugString(int indent)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0}{1}: <{2}> Value:<{3}>",
- StringUtil.Padding(indent),
- Kind,
- Text,
- Value);
- }
- }
-
- ///
- /// A single quoted string, or a single quoted here string.
- ///
- public class StringLiteralToken : StringToken
- {
- internal StringLiteralToken(InternalScriptExtent scriptExtent, TokenFlags flags, TokenKind tokenKind, string value)
- : base(scriptExtent, tokenKind, flags, value)
- {
- }
- }
-
- ///
- /// A double quoted string, or a double quoted here string.
- ///
- public class StringExpandableToken : StringToken
- {
- private ReadOnlyCollection _nestedTokens;
-
- internal StringExpandableToken(InternalScriptExtent scriptExtent, TokenKind tokenKind, string value, string formatString, List nestedTokens, TokenFlags flags)
- : base(scriptExtent, tokenKind, flags, value)
- {
- if (nestedTokens != null && nestedTokens.Count > 0)
- {
- _nestedTokens = new ReadOnlyCollection(nestedTokens.ToArray());
- }
-
- FormatString = formatString;
- }
-
- internal static void ToDebugString(ReadOnlyCollection nestedTokens,
- StringBuilder sb, int indent)
- {
- Diagnostics.Assert(nestedTokens != null, "caller to verify");
-
- foreach (Token token in nestedTokens)
- {
- sb.Append(Environment.NewLine);
- sb.Append(token.ToDebugString(indent + 4));
- }
- }
-
- ///
- /// This collection holds any tokens from variable references and sub-expressions within the string.
- /// For example:
- /// "In $([DateTime]::Now.Year - $age), $name was born"
- /// has a nested expression with a sequence of tokens, plus the variable reference $name.
- ///
- public ReadOnlyCollection NestedTokens
- {
- get { return _nestedTokens; }
-
- internal set { _nestedTokens = value; }
- }
-
- internal string FormatString { get; }
-
- internal override string ToDebugString(int indent)
- {
- StringBuilder sb = new StringBuilder();
-
- sb.Append(base.ToDebugString(indent));
- if (_nestedTokens != null)
- {
- ToDebugString(_nestedTokens, sb, indent);
- }
-
- return sb.ToString();
- }
- }
-
- ///
- ///
- public class LabelToken : Token
- {
- internal LabelToken(InternalScriptExtent scriptExtent, TokenFlags tokenFlags, string labelText)
- : base(scriptExtent, TokenKind.Label, tokenFlags)
- {
- LabelText = labelText;
- }
-
- ///
- ///
- public string LabelText { get; }
- }
-
- ///
- /// An abstract base class for merging and file redirections.
- ///
- public abstract class RedirectionToken : Token
- {
- internal RedirectionToken(InternalScriptExtent scriptExtent, TokenKind kind)
- : base(scriptExtent, kind, TokenFlags.None)
- {
- }
- }
-
- ///
- /// The (currently unimplemented) input redirection.
- ///
- public class InputRedirectionToken : RedirectionToken
- {
- internal InputRedirectionToken(InternalScriptExtent scriptExtent)
- : base(scriptExtent, TokenKind.RedirectInStd)
- {
- }
- }
-
- ///
- /// A merging redirection.
- ///
- public class MergingRedirectionToken : RedirectionToken
- {
- internal MergingRedirectionToken(InternalScriptExtent scriptExtent, RedirectionStream from, RedirectionStream to)
- : base(scriptExtent, TokenKind.Redirection)
- {
- this.FromStream = from;
- this.ToStream = to;
- }
-
- ///
- /// The stream being redirected.
- ///
- public RedirectionStream FromStream { get; }
-
- ///
- /// The stream being written to.
- ///
- public RedirectionStream ToStream { get; }
- }
-
- ///
- /// A file redirection.
- ///
- public class FileRedirectionToken : RedirectionToken
- {
- internal FileRedirectionToken(InternalScriptExtent scriptExtent, RedirectionStream from, bool append)
- : base(scriptExtent, TokenKind.Redirection)
- {
- this.FromStream = from;
- this.Append = append;
- }
-
- ///
- /// The stream being redirected.
- ///
- public RedirectionStream FromStream { get; }
-
- ///
- /// True if the redirection should append the file rather than create a new file.
- ///
- public bool Append { get; }
- }
-
- internal class UnscannedSubExprToken : StringLiteralToken
- {
- internal UnscannedSubExprToken(InternalScriptExtent scriptExtent, TokenFlags tokenFlags, string value, BitArray skippedCharOffsets)
- : base(scriptExtent, tokenFlags, TokenKind.StringLiteral, value)
- {
- this.SkippedCharOffsets = skippedCharOffsets;
- }
-
- internal BitArray SkippedCharOffsets { get; }
- }
-}
From eeddffe7dd3a389a5266cedf544fdc93b6cd3fed Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 04:08:31 +0000
Subject: [PATCH 10/24] Fix parser error with &! operator - set flags correctly
- Added backgroundThreadJob boolean variable to track ThreadJob intent
- Changed AmpersandExclaim case to set backgroundThreadJob variable instead of property
- Set BackgroundThreadJob property in default case alongside Background property
- Fixes issue where "1+1 &!" caused parser error because property was set on wrong pipeline
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
src/System.Management.Automation/engine/parser/Parser.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs
index 298ee664af3..af44906a9c0 100644
--- a/src/System.Management.Automation/engine/parser/Parser.cs
+++ b/src/System.Management.Automation/engine/parser/Parser.cs
@@ -5849,6 +5849,7 @@ private PipelineBaseAst PipelineChainRule()
Token currentChainOperatorToken = null;
Token nextToken = null;
bool background = false;
+ bool backgroundThreadJob = false;
while (true)
{
// Look for the next pipeline in the chain,
@@ -5953,7 +5954,7 @@ private PipelineBaseAst PipelineChainRule()
}
background = true;
- nextPipeline.BackgroundThreadJob = true;
+ backgroundThreadJob = true;
goto default;
// No more chain operators -- return
@@ -5969,6 +5970,7 @@ private PipelineBaseAst PipelineChainRule()
// Set background on the pipeline AST
nextPipeline.Background = true;
+ nextPipeline.BackgroundThreadJob = backgroundThreadJob;
return nextPipeline;
}
From 3852bba4d74d9f06efefac032f85e35b028db101 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 05:16:08 +0000
Subject: [PATCH 11/24] Fix &! operator recognition in PipelineRule switch
statement
- Added case for TokenKind.AmpersandExclaim in PipelineRule switch
- Sets both background and backgroundThreadJob flags when &! is encountered
- Fixes "Unexpected token '&!'" error when using {1+1} &! syntax
- Follows same pattern as TokenKind.Ampersand case with allowBackground check
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/parser/Parser.cs | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs
index af44906a9c0..b62e855f7a5 100644
--- a/src/System.Management.Automation/engine/parser/Parser.cs
+++ b/src/System.Management.Automation/engine/parser/Parser.cs
@@ -6145,6 +6145,20 @@ private PipelineBaseAst PipelineRule(
background = true;
break;
+ case TokenKind.AmpersandExclaim:
+ if (!allowBackground)
+ {
+ // Handled by invoking rule
+ scanning = false;
+ continue;
+ }
+
+ SkipToken();
+ scanning = false;
+ background = true;
+ backgroundThreadJob = true;
+ break;
+
case TokenKind.Pipe:
SkipToken();
SkipNewlines();
From eeb5e2629c4d69268fb56cffce9771641acbf6d9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 05:41:36 +0000
Subject: [PATCH 12/24] Fix missing backgroundThreadJob variable declaration in
PipelineRule
- Added backgroundThreadJob variable declaration in PipelineRule method
- Set BackgroundThreadJob property on PipelineAst when backgroundThreadJob flag is true
- Fixes CS0103 compilation error: "The name 'backgroundThreadJob' does not exist"
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
src/System.Management.Automation/engine/parser/Parser.cs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs
index b62e855f7a5..bec5199bf81 100644
--- a/src/System.Management.Automation/engine/parser/Parser.cs
+++ b/src/System.Management.Automation/engine/parser/Parser.cs
@@ -6028,6 +6028,7 @@ private PipelineBaseAst PipelineRule(
Token nextToken = null;
bool scanning = true;
bool background = false;
+ bool backgroundThreadJob = false;
ExpressionAst expr = startExpression;
while (scanning)
{
@@ -6190,7 +6191,12 @@ private PipelineBaseAst PipelineRule(
return null;
}
- return new PipelineAst(ExtentOf(startExtent, pipelineElements[pipelineElements.Count - 1]), pipelineElements, background);
+ var pipeline = new PipelineAst(ExtentOf(startExtent, pipelineElements[pipelineElements.Count - 1]), pipelineElements, background);
+ if (backgroundThreadJob)
+ {
+ pipeline.BackgroundThreadJob = true;
+ }
+ return pipeline;
}
private RedirectionAst RedirectionRule(RedirectionToken redirectionToken, RedirectionAst[] redirections, ref IScriptExtent extent)
From efb46ab8657d3bc43d7670e4d86116444561435f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 06:08:40 +0000
Subject: [PATCH 13/24] Fix ThreadJob detection by using GetCommand instead of
GetCmdlet
- Changed from GetCmdlet to GetCommand with CommandTypes.Cmdlet | CommandTypes.Function
- GetCommand will auto-import the ThreadJob module if it's available
- Properly handles CmdletInfo vs FunctionInfo by checking type and creating appropriate command info
- Fixes issue where {1+1} &! was creating BackgroundJob instead of ThreadJob
- Now correctly creates ThreadJob when ThreadJob module is available
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index e5719f9e41c..a7705522a46 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -590,10 +590,17 @@ internal static void InvokePipelineInBackground(
CmdletInfo commandInfo;
if (pipelineAst.BackgroundThreadJob)
{
- // GetCmdlet returns null when the cmdlet isn't found (it doesn't throw in common case)
- commandInfo = context.SessionState.InvokeCommand.GetCmdlet("Start-ThreadJob");
- // Fall back to Start-Job if Start-ThreadJob is not available
- commandInfo ??= new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ // Try to get Start-ThreadJob - GetCommand will auto-import the ThreadJob module if available
+ var threadJobCommand = context.SessionState.InvokeCommand.GetCommand("Start-ThreadJob", CommandTypes.Cmdlet | CommandTypes.Function);
+ if (threadJobCommand != null)
+ {
+ commandInfo = threadJobCommand as CmdletInfo ?? new CmdletInfo(threadJobCommand.Name, threadJobCommand.ImplementingType);
+ }
+ else
+ {
+ // Fall back to Start-Job if Start-ThreadJob is not available
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
}
else
{
From 5098cabebb6a292a29e9617b7269608c002b43d1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 06:19:31 +0000
Subject: [PATCH 14/24] Fix compilation error: properly handle CommandInfo type
checking
- Use pattern matching to check if threadJobCommand is CmdletInfo
- Handle FunctionInfo case by falling back to Start-Job (functions can't be invoked in this context)
- Fixes CS1061 error: CommandInfo doesn't have ImplementingType property
- Only CmdletInfo has ImplementingType, not base CommandInfo class
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index a7705522a46..d4493ff9364 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -594,7 +594,22 @@ internal static void InvokePipelineInBackground(
var threadJobCommand = context.SessionState.InvokeCommand.GetCommand("Start-ThreadJob", CommandTypes.Cmdlet | CommandTypes.Function);
if (threadJobCommand != null)
{
- commandInfo = threadJobCommand as CmdletInfo ?? new CmdletInfo(threadJobCommand.Name, threadJobCommand.ImplementingType);
+ // Check if it's a CmdletInfo (cmdlet) or FunctionInfo (function)
+ if (threadJobCommand is CmdletInfo cmdletInfo)
+ {
+ commandInfo = cmdletInfo;
+ }
+ else if (threadJobCommand is FunctionInfo functionInfo)
+ {
+ // For functions, we need to use the function's script block
+ // Fall back to Start-Job since we can't easily invoke a function here
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
+ else
+ {
+ // Unknown command type, fall back to Start-Job
+ commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand));
+ }
}
else
{
From 13e61be90021e416f543257e18a4961fd2d4ede6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 11 Mar 2026 06:37:09 +0000
Subject: [PATCH 15/24] Fix WorkingDirectory parameter error with
Start-ThreadJob
- Added usingThreadJob boolean flag to track if Start-ThreadJob is actually being used
- Only add WorkingDirectory parameter for Start-Job, not Start-ThreadJob
- Start-ThreadJob doesn't support the WorkingDirectory parameter
- Fixes error: "A parameter cannot be found that matches parameter name 'WorkingDirectory'"
Co-authored-by: kilasuit <6355225+kilasuit@users.noreply.github.com>
---
.../engine/runtime/Operations/MiscOps.cs | 23 ++++++++++++-------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
index d4493ff9364..1a9a4d0fa44 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs
@@ -588,6 +588,7 @@ internal static void InvokePipelineInBackground(
// Use Start-ThreadJob if BackgroundThreadJob is set, otherwise use Start-Job
CmdletInfo commandInfo;
+ bool usingThreadJob = false;
if (pipelineAst.BackgroundThreadJob)
{
// Try to get Start-ThreadJob - GetCommand will auto-import the ThreadJob module if available
@@ -598,6 +599,7 @@ internal static void InvokePipelineInBackground(
if (threadJobCommand is CmdletInfo cmdletInfo)
{
commandInfo = cmdletInfo;
+ usingThreadJob = true;
}
else if (threadJobCommand is FunctionInfo functionInfo)
{
@@ -624,13 +626,19 @@ internal static void InvokePipelineInBackground(
commandProcessor = context.CommandDiscovery.LookupCommandProcessor(commandInfo, CommandOrigin.Internal, false, context.EngineSessionState);
- var workingDirectoryParameter = CommandParameterInternal.CreateParameterWithArgument(
- parameterAst: pipelineAst,
- parameterName: "WorkingDirectory",
- parameterText: null,
- argumentAst: pipelineAst,
- value: context.SessionState.Path.CurrentLocation.Path,
- spaceAfterParameter: false);
+ // Only add WorkingDirectory parameter for Start-Job, not for Start-ThreadJob
+ // Start-ThreadJob doesn't support the WorkingDirectory parameter
+ if (!usingThreadJob)
+ {
+ var workingDirectoryParameter = CommandParameterInternal.CreateParameterWithArgument(
+ parameterAst: pipelineAst,
+ parameterName: "WorkingDirectory",
+ parameterText: null,
+ argumentAst: pipelineAst,
+ value: context.SessionState.Path.CurrentLocation.Path,
+ spaceAfterParameter: false);
+ commandProcessor.AddParameter(workingDirectoryParameter);
+ }
var scriptBlockParameter = CommandParameterInternal.CreateParameterWithArgument(
parameterAst: pipelineAst,
@@ -640,7 +648,6 @@ internal static void InvokePipelineInBackground(
value: sb,
spaceAfterParameter: false);
- commandProcessor.AddParameter(workingDirectoryParameter);
commandProcessor.AddParameter(scriptBlockParameter);
pipelineProcessor.Add(commandProcessor);
pipelineProcessor.LinkPipelineSuccessOutput(outputPipe ?? new Pipe(new List