Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6991,6 +6991,13 @@ public object VisitPipeline(PipelineAst pipelineAst)
return expr != null && (bool)expr.Accept(this);
}

public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)
{
return (bool)ternaryExpressionAst.Condition.Accept(this) &&
(bool)ternaryExpressionAst.IfTrue.Accept(this) &&
(bool)ternaryExpressionAst.IfFalse.Accept(this);
}

public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
{
return (bool)binaryExpressionAst.Left.Accept(this) && (bool)binaryExpressionAst.Right.Accept(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ static ExperimentalFeature()
description: "Recommend potential commands based on fuzzy search on a CommandNotFoundException"),
new ExperimentalFeature(
name: "PSForEachObjectParallel",
description: "New parameter set for ForEach-Object to run script blocks in parallel")
description: "New parameter set for ForEach-Object to run script blocks in parallel"),
new ExperimentalFeature(
name: "PSTernaryOperator",
description: "Support the ternary operator in PowerShell langauge.")
};
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ public override AstVisitAction VisitAssignmentStatement(AssignmentStatementAst a

public override AstVisitAction VisitSwitchStatement(SwitchStatementAst switchStatementAst) { return AstVisitAction.SkipChildren; }

public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst) { return AstVisitAction.SkipChildren; }

// Visit one the other variations:
// - Dotting scripts
// - Setting aliases
Expand Down
12 changes: 11 additions & 1 deletion src/System.Management.Automation/engine/parser/AstVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace System.Management.Automation.Language
Expand Down Expand Up @@ -151,6 +150,8 @@ public interface ICustomAstVisitor
/// <summary/>
public interface ICustomAstVisitor2 : ICustomAstVisitor
{
private object DefaultVisit(Ast ast) => null;

/// <summary/>
object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst);

Expand All @@ -171,6 +172,9 @@ public interface ICustomAstVisitor2 : ICustomAstVisitor

/// <summary/>
object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst);

/// <summary/>
object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst) => DefaultVisit(ternaryExpressionAst);
}

#if DEBUG
Expand Down Expand Up @@ -312,6 +316,8 @@ internal AstVisitAction CheckParent(Ast ast)
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst ast) { return CheckParent(ast); }

public override AstVisitAction VisitDynamicKeywordStatement(DynamicKeywordStatementAst ast) { return CheckParent(ast); }

public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ast) => CheckParent(ast);
}

/// <summary>
Expand Down Expand Up @@ -544,6 +550,8 @@ protected AstVisitAction CheckScriptBlock(Ast ast)
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst ast) { return Check(ast); }

public override AstVisitAction VisitDynamicKeywordStatement(DynamicKeywordStatementAst ast) { return Check(ast); }

public override AstVisitAction VisitTernaryExpression(TernaryExpressionAst ast) { return Check(ast); }
}

/// <summary>
Expand Down Expand Up @@ -680,5 +688,7 @@ public abstract class DefaultCustomAstVisitor2 : DefaultCustomAstVisitor, ICusto
public virtual object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return null; }
/// <summary/>
public virtual object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return null; }
/// <summary/>
public virtual object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst) { return null; }
}
}
21 changes: 17 additions & 4 deletions src/System.Management.Automation/engine/parser/CharTraits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,26 @@ internal static bool ForceStartNewToken(this char c)
return c.IsWhitespace();
}

// Return true if the character ends the current number token. This allows the tokenizer
// to scan '7z' as a single token, but '7+' as 2 tokens.
internal static bool ForceStartNewTokenAfterNumber(this char c)
/// <summary>
/// Check if the current character forces to end scanning a number token.
/// This allows the tokenizer to scan '7z' as a single token, but '7+' as 2 tokens.
/// </summary>
/// <param name="c">The character to check.</param>
/// <param name="forceEndNumberOnTernaryOperatorChars">
/// In some cases, we want '?' and ':' to end a number token too, so they can be
/// treated as the ternary operator tokens.
/// </param>
/// <returns>Return true if the character ends the current number token.</returns>
internal static bool ForceStartNewTokenAfterNumber(this char c, bool forceEndNumberOnTernaryOperatorChars)
{
if (c < 128)
{
return (s_traits[c] & CharTraits.ForceStartNewTokenAfterNumber) != 0;
if ((s_traits[c] & CharTraits.ForceStartNewTokenAfterNumber) != 0)
{
return true;
}

return forceEndNumberOnTernaryOperatorChars && (c == '?' || c == ':');
Copy link
Copy Markdown
Contributor

@msftrncs msftrncs Aug 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why cannot ? and : just be added to CharTraits.ForceStartNewTokenAfterNumber? This trait is only used while the tokenizer is in expression mode.

Copy link
Copy Markdown
Contributor

@msftrncs msftrncs Aug 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might answer my own question … because it needs to be hidden behind an experimental feature flag at this time?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the PR context explained clearly enough what this is for:

(if the following are entire command lines)
1?2:3 is a command
1-lt2?3:4 is a ternary expression.

It will be even harder for EditorSyntax (PowerShell/EditorSyntax#184) to correctly syntax scope this via TextMate. This will require an extra set of numericConstant rules to handle the 'first possible operand of an expression' vs 'a numeric value clearly in expression mode' vs 'a numeric constant in command mode'. However, it is an interesting challenge to see if I can simplify.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments were added for the parameter forceEndNumberOnTernaryOperatorChars.

        /// <param name="forceEndNumberOnTernaryOperatorChars">
        /// In some cases, we want '?' and ':' to end a number token too, so they can be
        /// treated as the ternary operator tokens.

Also, comments were added in Parser.cs to the places where the parameter endNumberOnTernaryOpChars was added.

}

return c.IsDash();
Expand Down
10 changes: 10 additions & 0 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4974,6 +4974,16 @@ public Expression GenerateCallContains(Expression lhs, Expression rhs, bool igno
rhs.Cast(typeof(object)));
}

public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)
{
var expr = Expression.Condition(
Compile(ternaryExpressionAst.Condition).Convert(typeof(bool)),
Compile(ternaryExpressionAst.IfTrue).Convert(typeof(object)),
Compile(ternaryExpressionAst.IfFalse).Convert(typeof(object)));

return expr;
}

public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
{
object constantValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace System.Management.Automation.Language
* There is a number of similarities between these two classes, and changes (fixes) in this code
* may need to be reflected in that class and vice versa
*/
internal class IsConstantValueVisitor : ICustomAstVisitor
internal class IsConstantValueVisitor : ICustomAstVisitor2
{
public static bool IsConstant(Ast ast, out object constantValue, bool forAttribute = false, bool forRequires = false)
{
Expand Down Expand Up @@ -130,6 +130,20 @@ public static bool IsConstant(Ast ast, out object constantValue, bool forAttribu

public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return false; }

public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return false; }

public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return false; }

public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return false; }

public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return false; }

public object VisitUsingStatement(UsingStatementAst usingStatement) { return false; }

public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return false; }

public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return false; }

public object VisitStatementBlock(StatementBlockAst statementBlockAst)
{
if (statementBlockAst.Traps != null) return false;
Expand Down Expand Up @@ -172,6 +186,13 @@ private static bool IsNullDivisor(ExpressionAst operand)
return false;
}

public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)
{
return (bool)ternaryExpressionAst.Condition.Accept(this) &&
(bool)ternaryExpressionAst.IfTrue.Accept(this) &&
(bool)ternaryExpressionAst.IfFalse.Accept(this);
}

public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
{
return binaryExpressionAst.Operator.HasTrait(TokenFlags.CanConstantFold) &&
Expand Down Expand Up @@ -300,7 +321,7 @@ public object VisitParenExpression(ParenExpressionAst parenExpressionAst)
}
}

internal class ConstantValueVisitor : ICustomAstVisitor
internal class ConstantValueVisitor : ICustomAstVisitor2
{
internal bool AttributeArgument { get; set; }
internal bool RequiresArgument { get; set; }
Expand Down Expand Up @@ -400,6 +421,21 @@ private static object CompileAndInvoke(Ast ast)

public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst) { return AutomationNull.Value; }

public object VisitTypeDefinition(TypeDefinitionAst typeDefinitionAst) { return AutomationNull.Value; }

public object VisitPropertyMember(PropertyMemberAst propertyMemberAst) { return AutomationNull.Value; }

public object VisitFunctionMember(FunctionMemberAst functionMemberAst) { return AutomationNull.Value; }

public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst) { return AutomationNull.Value; }

public object VisitUsingStatement(UsingStatementAst usingStatement) { return AutomationNull.Value; }

public object VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst) { return AutomationNull.Value; }

public object VisitDynamicKeywordStatement(DynamicKeywordStatementAst dynamicKeywordAst) { return AutomationNull.Value; }


public object VisitStatementBlock(StatementBlockAst statementBlockAst)
{
CheckIsConstant(statementBlockAst, "Caller to verify ast is constant");
Expand All @@ -412,6 +448,16 @@ public object VisitPipeline(PipelineAst pipelineAst)
return pipelineAst.GetPureExpression().Accept(this);
}

public object VisitTernaryExpression(TernaryExpressionAst ternaryExpressionAst)
{
CheckIsConstant(ternaryExpressionAst, "Caller to verify ast is constant");

object condition = ternaryExpressionAst.Condition.Accept(this);
return LanguagePrimitives.IsTrue(condition)
? ternaryExpressionAst.IfTrue.Accept(this)
: ternaryExpressionAst.IfFalse.Accept(this);
}

public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
{
CheckIsConstant(binaryExpressionAst, "Caller to verify ast is constant");
Expand Down
Loading