Skip to content

Null assignment #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ecb94dd
Implement null condtional assigment operator
adityapatwardhan Aug 15, 2019
15119cf
Implement ?? operator
adityapatwardhan Aug 22, 2019
0cb1592
Add QuestionDot token
adityapatwardhan Aug 28, 2019
cfd73f9
Fix issues after merge and test fix
adityapatwardhan Sep 25, 2019
137a37b
Change Null coalescing assigned operator to ??=
adityapatwardhan Sep 25, 2019
94ad01f
Make feature experimental
adityapatwardhan Sep 26, 2019
dee8092
Add logic for skipping tests if experimental feature is disabled
adityapatwardhan Sep 27, 2019
9826e69
Fix parsing tests
adityapatwardhan Sep 27, 2019
9e599c4
Address code review feedback
adityapatwardhan Oct 1, 2019
8fd9572
Remove parsing test as it interfers with ternary operator
adityapatwardhan Oct 2, 2019
02dc105
Add few more tests
adityapatwardhan Oct 2, 2019
4e8019e
Address Rob's feedback
adityapatwardhan Oct 4, 2019
a9af2ab
Refactor according to feedback
adityapatwardhan Oct 9, 2019
0e93e14
Add coalesce assignment
adityapatwardhan Oct 9, 2019
771a86d
Add precedence flag for null coalesce operator and add tests
adityapatwardhan Oct 10, 2019
1a4887e
Merge branch 'master' into NullAssignment
adityapatwardhan Oct 10, 2019
73b8982
Revert formatting changes in token.cs
adityapatwardhan Oct 10, 2019
36f4b85
Fix parsing test
adityapatwardhan Oct 10, 2019
d530fcd
More test fixes
adityapatwardhan Oct 10, 2019
3f0f3d1
Check for experimental feature in parsing.tests.ps1
adityapatwardhan Oct 11, 2019
8f39bbb
Updated to move Coalesce code out of Binder
adityapatwardhan Oct 11, 2019
dea5793
Update TokenFlag name
adityapatwardhan Oct 14, 2019
4cf6ba4
Address feedback
adityapatwardhan Oct 15, 2019
268a609
Address Ilya's feedback
adityapatwardhan Oct 15, 2019
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 @@ -120,7 +120,10 @@ static ExperimentalFeature()
description: "New formatting for ErrorRecord"),
new ExperimentalFeature(
name: "PSUpdatesNotification",
description: "Print notification message when new releases are available")
description: "Print notification message when new releases are available"),
new ExperimentalFeature(
name: "PSCoalescingOperators",
description: "Support the null coalescing operator and null coalescing assignment operator in PowerShell language")
};
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);

Expand Down
41 changes: 40 additions & 1 deletion src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ internal static class CachedReflectionInfo

internal static readonly MethodInfo LanguagePrimitives_GetInvalidCastMessages =
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.GetInvalidCastMessages), staticFlags);
internal static readonly MethodInfo LanguagePrimitives_IsNullLike =
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.IsNullLike), staticPublicFlags);
internal static readonly MethodInfo LanguagePrimitives_ThrowInvalidCastException =
typeof(LanguagePrimitives).GetMethod(nameof(LanguagePrimitives.ThrowInvalidCastException), staticFlags);

Expand Down Expand Up @@ -786,6 +788,7 @@ internal Expression ReduceAssignment(ISupportsAssignment left, TokenKind tokenKi
{
IAssignableValue av = left.GetAssignableValue();
ExpressionType et = ExpressionType.Extension;

switch (tokenKind)
{
case TokenKind.Equals: return av.SetValue(this, right);
Expand All @@ -794,15 +797,49 @@ internal Expression ReduceAssignment(ISupportsAssignment left, TokenKind tokenKi
case TokenKind.MultiplyEquals: et = ExpressionType.Multiply; break;
case TokenKind.DivideEquals: et = ExpressionType.Divide; break;
case TokenKind.RemainderEquals: et = ExpressionType.Modulo; break;
case TokenKind.QuestionQuestionEquals when ExperimentalFeature.IsEnabled("PSCoalescingOperators"): et = ExpressionType.Coalesce; break;
}

var exprs = new List<Expression>();
var temps = new List<ParameterExpression>();
var getExpr = av.GetValue(this, exprs, temps);
exprs.Add(av.SetValue(this, DynamicExpression.Dynamic(PSBinaryOperationBinder.Get(et), typeof(object), getExpr, right)));

if(et == ExpressionType.Coalesce)
{
exprs.Add(av.SetValue(this, Coalesce(getExpr, right)));
}
else
{
exprs.Add(av.SetValue(this, DynamicExpression.Dynamic(PSBinaryOperationBinder.Get(et), typeof(object), getExpr, right)));
}

return Expression.Block(temps, exprs);
}

private static Expression Coalesce(Expression left, Expression right)
{
Type leftType = left.Type;

if (leftType.IsValueType)
{
return left;
}
else if(leftType == typeof(DBNull) || leftType == typeof(NullString) || leftType == typeof(AutomationNull))
{
return right;
}
else
{
Expression lhs = left.Cast(typeof(object));
Expression rhs = right.Cast(typeof(object));

return Expression.Condition(
Expression.Call(CachedReflectionInfo.LanguagePrimitives_IsNullLike, lhs),
rhs,
lhs);
}
}

internal Expression GetLocal(int tupleIndex)
{
Expression result = LocalVariablesParameter;
Expand Down Expand Up @@ -5231,6 +5268,8 @@ public object VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst)
CachedReflectionInfo.ParserOps_SplitOperator,
_executionContextParameter, Expression.Constant(binaryExpressionAst.ErrorPosition), lhs.Cast(typeof(object)), rhs.Cast(typeof(object)),
ExpressionCache.Constant(false));
case TokenKind.QuestionQuestion when ExperimentalFeature.IsEnabled("PSCoalescingOperators"):
return Coalesce(lhs, rhs);
}

throw new InvalidOperationException("Unknown token in binary operator.");
Expand Down
6 changes: 5 additions & 1 deletion src/System.Management.Automation/engine/parser/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6555,8 +6555,12 @@ private ExpressionAst BinaryExpressionRule(bool endNumberOnTernaryOpChars = fals
// G bitwise-expression '-bxor' new-lines:opt comparison-expression
// G
// G comparison-expression:
// G nullcoalesce-expression
// G comparison-expression comparison-operator new-lines:opt nullcoalesce-expression
// G
// G nullcoalesce-expression:
// G additive-expression
// G comparison-expression comparison-operator new-lines:opt additive-expression
// G nullcoalesce-expression '??' new-lines:opt additive-expression
// G
// G additive-expression:
// G multiplicative-expression
Expand Down
37 changes: 24 additions & 13 deletions src/System.Management.Automation/engine/parser/token.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ public enum TokenKind
/// <summary>The ternary operator '?'.</summary>
QuestionMark = 100,

/// <summary>The null conditional assignment operator '??='.</summary>
QuestionQuestionEquals = 101,

/// <summary>The null coalesce operator '??'.</summary>
QuestionQuestion = 102,

#endregion Operators

#region Keywords
Expand Down Expand Up @@ -592,46 +598,51 @@ public enum TokenFlags
/// <summary>
/// The precedence of the logical operators '-and', '-or', and '-xor'.
/// </summary>
BinaryPrecedenceLogical = 1,
BinaryPrecedenceLogical = 0x1,

/// <summary>
/// The precedence of the bitwise operators '-band', '-bor', and '-bxor'
/// </summary>
BinaryPrecedenceBitwise = 2,
BinaryPrecedenceBitwise = 0x2,

/// <summary>
/// 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.
/// </summary>
BinaryPrecedenceComparison = 3,
BinaryPrecedenceComparison = 0x5,

/// <summary>
/// The precedence of null coalesce operator '??'.
/// </summary>
BinaryPrecedenceCoalesce = 0x7,

/// <summary>
/// The precedence of the binary operators '+' and '-'.
/// </summary>
BinaryPrecedenceAdd = 4,
BinaryPrecedenceAdd = 0x9,

/// <summary>
/// The precedence of the operators '*', '/', and '%'.
/// </summary>
BinaryPrecedenceMultiply = 5,
BinaryPrecedenceMultiply = 0xa,

/// <summary>
/// The precedence of the '-f' operator.
/// </summary>
BinaryPrecedenceFormat = 6,
BinaryPrecedenceFormat = 0xc,

/// <summary>
/// The precedence of the '..' operator.
/// </summary>
BinaryPrecedenceRange = 7,
BinaryPrecedenceRange = 0xd,

#endregion Precedence Values

/// <summary>
/// A bitmask to get the precedence of binary operators.
/// </summary>
BinaryPrecedenceMask = 0x00000007,
BinaryPrecedenceMask = 0x0000000f,

/// <summary>
/// The token is a keyword.
Expand Down Expand Up @@ -669,7 +680,7 @@ public enum TokenFlags
SpecialOperator = 0x00001000,

/// <summary>
/// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', or '%='
/// The token is one of the assignment operators: '=', '+=', '-=', '*=', '/=', '%=' or '??='
/// </summary>
AssignmentOperator = 0x00002000,

Expand Down Expand Up @@ -854,8 +865,8 @@ public static class TokenTraits
/* Shr */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceComparison | TokenFlags.CanConstantFold,
/* Colon */ TokenFlags.SpecialOperator | TokenFlags.DisallowedInRestrictedMode,
/* QuestionMark */ TokenFlags.TernaryOperator | TokenFlags.DisallowedInRestrictedMode,
/* Reserved slot 3 */ TokenFlags.None,
/* Reserved slot 4 */ TokenFlags.None,
/* QuestionQuestionEquals */ TokenFlags.AssignmentOperator,
/* QuestionQuestion */ TokenFlags.BinaryOperator | TokenFlags.BinaryPrecedenceCoalesce,
/* Reserved slot 5 */ TokenFlags.None,
/* Reserved slot 6 */ TokenFlags.None,
/* Reserved slot 7 */ TokenFlags.None,
Expand Down Expand Up @@ -1052,8 +1063,8 @@ public static class TokenTraits
/* Shr */ "-shr",
/* Colon */ ":",
/* QuestionMark */ "?",
/* Reserved slot 3 */ string.Empty,
/* Reserved slot 4 */ string.Empty,
/* QuestionQuestionEquals */ "??=",
/* QuestionQuestion */ "??",
/* Reserved slot 5 */ string.Empty,
/* Reserved slot 6 */ string.Empty,
/* Reserved slot 7 */ string.Empty,
Expand Down
19 changes: 19 additions & 0 deletions src/System.Management.Automation/engine/parser/tokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4994,6 +4994,25 @@ internal Token NextToken()
return this.NewToken(TokenKind.Colon);

case '?' when InExpressionMode():
if (ExperimentalFeature.IsEnabled("PSCoalescingOperators"))
{
c1 = PeekChar();

if (c1 == '?')
{
SkipChar();
c1 = PeekChar();

if (c1 == '=')
{
SkipChar();
return this.NewToken(TokenKind.QuestionQuestionEquals);
}

return this.NewToken(TokenKind.QuestionQuestion);
}
}

return this.NewToken(TokenKind.QuestionMark);

case '\0':
Expand Down
Loading