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 @@ -161,7 +161,7 @@ static CharExtensions()
/* V */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* W */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* X */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* Y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* Y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst | CharTraits.TypeSuffix,
/* Z */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* [ */ CharTraits.None,
/* \ */ CharTraits.None,
Expand Down Expand Up @@ -193,7 +193,7 @@ static CharExtensions()
/* v */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* w */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* x */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst | CharTraits.TypeSuffix,
/* z */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
/* { */ CharTraits.ForceStartNewToken,
/* | */ CharTraits.ForceStartNewToken,
Expand Down
94 changes: 87 additions & 7 deletions src/System.Management.Automation/engine/parser/tokenizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,59 @@ internal enum TokenizerMode
Signature, // i.e. class or method declaration
}

/// <summary>
/// Indicates which suffix character(s) are present in the numeric literal being parsed by TryGetNumberValue.
/// </summary>
[Flags]
internal enum NumberSuffixFlags
{
/// <summary>
/// Indicates no suffix, a raw numeric literal. May be parsed as Int32, Int64, or Double.
/// </summary>
None = 0x0,

/// <summary>
/// Indicates 'u' suffix for unsigned integers. May be parsed as UInt32 or UInt64, depending on the value.
/// </summary>
Unsigned = 0x1,
Short = 0x2,
Long = 0x4,
Decimal = 0x8

/// <summary>
/// Indicates 'y' suffix for signed byte (sbyte) values.
/// </summary>
SignedByte = 0x2,

/// <summary>
/// Indicates 'uy' suffix for unsigned byte values.
/// This is a compound value, representing both SignedByte and Unsigned flags being set.
/// </summary>
UnsignedByte = 0x3,

/// <summary>
/// Indicates 's' suffix for short (Int16) integers.
/// </summary>
Short = 0x4,

/// <summary>
/// Indicates 'us' suffix for ushort (UInt16) integers.
/// This is a compound flag value, representing both Unsigned and Short flags being set.
/// </summary>
UnsignedShort = 0x5,

/// <summary>
/// Indicates 'l' suffix for long (Int64) integers.
/// </summary>
Long = 0x8,

/// <summary>
/// Indicates 'ul' suffix for ulong (UInt64) integers.
/// This is a compound flag value, representing both Unsigned and Long flags being set.
/// </summary>
UnsignedLong = 0x9,

/// <summary>
/// Indicates 'd' suffix for decimal (128-bit) real numbers.
/// </summary>
Decimal = 0x10
}

//
Expand Down Expand Up @@ -3292,12 +3337,18 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number
case NumberSuffixFlags.Short:
result = (short)((short)Convert.ChangeType(doubleValue, typeof(short), CultureInfo.InvariantCulture) * multiplier);
break;
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Long:
case NumberSuffixFlags.SignedByte:
result = (sbyte)((sbyte)Convert.ChangeType(doubleValue, typeof(sbyte), CultureInfo.InvariantCulture) * multiplier);
break;
case NumberSuffixFlags.UnsignedLong:
result = (ulong)Convert.ChangeType(doubleValue, typeof(ulong), CultureInfo.InvariantCulture) * (ulong)multiplier;
break;
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Short:
case NumberSuffixFlags.UnsignedShort:
result = (ushort)((ushort)Convert.ChangeType(doubleValue, typeof(ushort), CultureInfo.InvariantCulture) * multiplier);
break;
case NumberSuffixFlags.UnsignedByte:
result = (byte)((byte)Convert.ChangeType(doubleValue, typeof(byte), CultureInfo.InvariantCulture) * multiplier);
break;
case NumberSuffixFlags.Unsigned:
ulong testresult = (ulong)Convert.ChangeType(doubleValue, typeof(ulong), CultureInfo.InvariantCulture) * (ulong)multiplier;
if (testresult < uint.MaxValue)
Expand Down Expand Up @@ -3355,6 +3406,16 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number
return true;
}

result = null;
return false;
case NumberSuffixFlags.SignedByte:
// Multiplier for hex-parsed values can be negative to permit - prefix for hex values
if (Math.Abs(multiplier) == 1 && sbyte.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out sbyte sb))
{
result = (sbyte)(sb * multiplier);
return true;
}

result = null;
return false;
case NumberSuffixFlags.Unsigned:
Expand All @@ -3376,7 +3437,7 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number

result = null;
return false;
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Long:
case NumberSuffixFlags.UnsignedLong:
if (ulong.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out ulong ul))
{
result = (ulong)(ul * (ulong)multiplier);
Expand All @@ -3385,13 +3446,24 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number

result = null;
return false;
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Short:
case NumberSuffixFlags.UnsignedShort:
if (ushort.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out ushort us))
{
result = (ushort)(us * (ushort)multiplier);
return true;
}

result = null;
return false;
case NumberSuffixFlags.UnsignedByte:
// If multiplier is negative or greater than 1, we can assume it will fail since the
// minimum multiplier is 1024 (already exceeds byte.MaxValue), and byte is unsigned
if (multiplier == 1 && byte.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out byte b))
{
result = b;
return true;
}

result = null;
return false;
default:
Expand Down Expand Up @@ -3603,6 +3675,10 @@ private string ScanNumberHelper(char firstChar, out bool hex, out bool real, out
case 'D':
suffix |= NumberSuffixFlags.Decimal;
break;
case 'y':
case 'Y':
suffix |= NumberSuffixFlags.SignedByte;
break;
default:
notNumber = true;
break;
Expand All @@ -3625,6 +3701,10 @@ private string ScanNumberHelper(char firstChar, out bool hex, out bool real, out
case 'S':
suffix |= NumberSuffixFlags.Short;
break;
case 'y':
case 'Y':
suffix |= NumberSuffixFlags.SignedByte;
break;
default:
notNumber = true;
break;
Expand Down
37 changes: 37 additions & 0 deletions test/powershell/Language/Parser/Parser.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,27 @@ foo``u{2195}abc
@{ Script = "1dtb"; ExpectedValue = "1099511627776"; ExpectedType = [decimal] }
@{ Script = "1dpb"; ExpectedValue = "1125899906842624"; ExpectedType = [decimal] }

#SByte Integer notation
#Standard
@{ Script = "0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
@{ Script = "10y"; ExpectedValue = "10"; ExpectedType = [sbyte] }
@{ Script = "-10y"; ExpectedValue = "-10"; ExpectedType = [sbyte] }
@{ Script = "+10y"; ExpectedValue = "10"; ExpectedType = [sbyte] }
#Conversion from <Real>
@{ Script = "0.0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
@{ Script = "3.72y"; ExpectedValue = "4"; ExpectedType = [sbyte] }
@{ Script = "-3.72y"; ExpectedValue = "-4"; ExpectedType = [sbyte] }
#Exponential
@{ Script = "0e0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
@{ Script = "3e0y"; ExpectedValue = "3"; ExpectedType = [sbyte] }
@{ Script = "-3e0y"; ExpectedValue = "-3"; ExpectedType = [sbyte] }
@{ Script = "3e1y"; ExpectedValue = "30"; ExpectedType = [sbyte] }
@{ Script = "-3e1y"; ExpectedValue = "-30"; ExpectedType = [sbyte] }
#Hexadecimal
@{ Script = "0x0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
@{ Script = "0x41y"; ExpectedValue = "65"; ExpectedType = [sbyte] }
@{ Script = "-0x41y"; ExpectedValue = "-65"; ExpectedType = [sbyte] }

#Short Integer notation
#Standard
@{ Script = "0s"; ExpectedValue = "0"; ExpectedType = [short] }
Expand Down Expand Up @@ -778,6 +799,22 @@ foo``u{2195}abc
@{ Script = "1utb"; ExpectedValue = "1099511627776"; ExpectedType = [ulong] }
@{ Script = "1upb"; ExpectedValue = "1125899906842624"; ExpectedType = [ulong] }

#Byte Integer notation
#Standard
@{ Script = "0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
@{ Script = "10uy"; ExpectedValue = "10"; ExpectedType = [byte] }
@{ Script = "+10uy"; ExpectedValue = "10"; ExpectedType = [byte] }
#Conversion from <Real>
@{ Script = "0.0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
@{ Script = "3.72uy"; ExpectedValue = "4"; ExpectedType = [byte] }
#Exponential
@{ Script = "0e0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
@{ Script = "3e0uy"; ExpectedValue = "3"; ExpectedType = [byte] }
@{ Script = "3e1uy"; ExpectedValue = "30"; ExpectedType = [byte] }
#Hexadecimal
@{ Script = "0x0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
@{ Script = "0x41uy"; ExpectedValue = "65"; ExpectedType = [byte] }

#Unsigned-Short Integer Notation
#Standard
@{ Script = "0us"; ExpectedValue = "0"; ExpectedType = [ushort] }
Expand Down