Skip to content

Commit 31e90ac

Browse files
vexx32iSazonov
authored andcommitted
Add support for Byte Literals (PowerShell#7901)
- Adds y suffix that is used to specify a numeric literal as the sbyte data type. - Can be combined with the existing u suffix as uy to specify the byte data type.
1 parent 7c70077 commit 31e90ac

File tree

3 files changed

+126
-9
lines changed

3 files changed

+126
-9
lines changed

src/System.Management.Automation/engine/parser/CharTraits.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ static CharExtensions()
161161
/* V */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
162162
/* W */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
163163
/* X */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
164-
/* Y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
164+
/* Y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst | CharTraits.TypeSuffix,
165165
/* Z */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
166166
/* [ */ CharTraits.None,
167167
/* \ */ CharTraits.None,
@@ -193,7 +193,7 @@ static CharExtensions()
193193
/* v */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
194194
/* w */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
195195
/* x */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
196-
/* y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
196+
/* y */ CharTraits.IdentifierStart | CharTraits.VarNameFirst | CharTraits.TypeSuffix,
197197
/* z */ CharTraits.IdentifierStart | CharTraits.VarNameFirst,
198198
/* { */ CharTraits.ForceStartNewToken,
199199
/* | */ CharTraits.ForceStartNewToken,

src/System.Management.Automation/engine/parser/tokenizer.cs

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -477,14 +477,59 @@ internal enum TokenizerMode
477477
Signature, // i.e. class or method declaration
478478
}
479479

480+
/// <summary>
481+
/// Indicates which suffix character(s) are present in the numeric literal being parsed by TryGetNumberValue.
482+
/// </summary>
480483
[Flags]
481484
internal enum NumberSuffixFlags
482485
{
486+
/// <summary>
487+
/// Indicates no suffix, a raw numeric literal. May be parsed as Int32, Int64, or Double.
488+
/// </summary>
483489
None = 0x0,
490+
491+
/// <summary>
492+
/// Indicates 'u' suffix for unsigned integers. May be parsed as UInt32 or UInt64, depending on the value.
493+
/// </summary>
484494
Unsigned = 0x1,
485-
Short = 0x2,
486-
Long = 0x4,
487-
Decimal = 0x8
495+
496+
/// <summary>
497+
/// Indicates 'y' suffix for signed byte (sbyte) values.
498+
/// </summary>
499+
SignedByte = 0x2,
500+
501+
/// <summary>
502+
/// Indicates 'uy' suffix for unsigned byte values.
503+
/// This is a compound value, representing both SignedByte and Unsigned flags being set.
504+
/// </summary>
505+
UnsignedByte = 0x3,
506+
507+
/// <summary>
508+
/// Indicates 's' suffix for short (Int16) integers.
509+
/// </summary>
510+
Short = 0x4,
511+
512+
/// <summary>
513+
/// Indicates 'us' suffix for ushort (UInt16) integers.
514+
/// This is a compound flag value, representing both Unsigned and Short flags being set.
515+
/// </summary>
516+
UnsignedShort = 0x5,
517+
518+
/// <summary>
519+
/// Indicates 'l' suffix for long (Int64) integers.
520+
/// </summary>
521+
Long = 0x8,
522+
523+
/// <summary>
524+
/// Indicates 'ul' suffix for ulong (UInt64) integers.
525+
/// This is a compound flag value, representing both Unsigned and Long flags being set.
526+
/// </summary>
527+
UnsignedLong = 0x9,
528+
529+
/// <summary>
530+
/// Indicates 'd' suffix for decimal (128-bit) real numbers.
531+
/// </summary>
532+
Decimal = 0x10
488533
}
489534

490535
//
@@ -3292,12 +3337,18 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number
32923337
case NumberSuffixFlags.Short:
32933338
result = (short)((short)Convert.ChangeType(doubleValue, typeof(short), CultureInfo.InvariantCulture) * multiplier);
32943339
break;
3295-
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Long:
3340+
case NumberSuffixFlags.SignedByte:
3341+
result = (sbyte)((sbyte)Convert.ChangeType(doubleValue, typeof(sbyte), CultureInfo.InvariantCulture) * multiplier);
3342+
break;
3343+
case NumberSuffixFlags.UnsignedLong:
32963344
result = (ulong)Convert.ChangeType(doubleValue, typeof(ulong), CultureInfo.InvariantCulture) * (ulong)multiplier;
32973345
break;
3298-
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Short:
3346+
case NumberSuffixFlags.UnsignedShort:
32993347
result = (ushort)((ushort)Convert.ChangeType(doubleValue, typeof(ushort), CultureInfo.InvariantCulture) * multiplier);
33003348
break;
3349+
case NumberSuffixFlags.UnsignedByte:
3350+
result = (byte)((byte)Convert.ChangeType(doubleValue, typeof(byte), CultureInfo.InvariantCulture) * multiplier);
3351+
break;
33013352
case NumberSuffixFlags.Unsigned:
33023353
ulong testresult = (ulong)Convert.ChangeType(doubleValue, typeof(ulong), CultureInfo.InvariantCulture) * (ulong)multiplier;
33033354
if (testresult < uint.MaxValue)
@@ -3355,6 +3406,16 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number
33553406
return true;
33563407
}
33573408

3409+
result = null;
3410+
return false;
3411+
case NumberSuffixFlags.SignedByte:
3412+
// Multiplier for hex-parsed values can be negative to permit - prefix for hex values
3413+
if (Math.Abs(multiplier) == 1 && sbyte.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out sbyte sb))
3414+
{
3415+
result = (sbyte)(sb * multiplier);
3416+
return true;
3417+
}
3418+
33583419
result = null;
33593420
return false;
33603421
case NumberSuffixFlags.Unsigned:
@@ -3376,7 +3437,7 @@ private static bool TryGetNumberValue(string strNum, bool hex, bool real, Number
33763437

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

33863447
result = null;
33873448
return false;
3388-
case NumberSuffixFlags.Unsigned | NumberSuffixFlags.Short:
3449+
case NumberSuffixFlags.UnsignedShort:
33893450
if (ushort.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out ushort us))
33903451
{
33913452
result = (ushort)(us * (ushort)multiplier);
33923453
return true;
33933454
}
33943455

3456+
result = null;
3457+
return false;
3458+
case NumberSuffixFlags.UnsignedByte:
3459+
// If multiplier is negative or greater than 1, we can assume it will fail since the
3460+
// minimum multiplier is 1024 (already exceeds byte.MaxValue), and byte is unsigned
3461+
if (multiplier == 1 && byte.TryParse(strNum, style, NumberFormatInfo.InvariantInfo, out byte b))
3462+
{
3463+
result = b;
3464+
return true;
3465+
}
3466+
33953467
result = null;
33963468
return false;
33973469
default:
@@ -3603,6 +3675,10 @@ private string ScanNumberHelper(char firstChar, out bool hex, out bool real, out
36033675
case 'D':
36043676
suffix |= NumberSuffixFlags.Decimal;
36053677
break;
3678+
case 'y':
3679+
case 'Y':
3680+
suffix |= NumberSuffixFlags.SignedByte;
3681+
break;
36063682
default:
36073683
notNumber = true;
36083684
break;
@@ -3625,6 +3701,10 @@ private string ScanNumberHelper(char firstChar, out bool hex, out bool real, out
36253701
case 'S':
36263702
suffix |= NumberSuffixFlags.Short;
36273703
break;
3704+
case 'y':
3705+
case 'Y':
3706+
suffix |= NumberSuffixFlags.SignedByte;
3707+
break;
36283708
default:
36293709
notNumber = true;
36303710
break;

test/powershell/Language/Parser/Parser.Tests.ps1

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,27 @@ foo``u{2195}abc
709709
@{ Script = "1dtb"; ExpectedValue = "1099511627776"; ExpectedType = [decimal] }
710710
@{ Script = "1dpb"; ExpectedValue = "1125899906842624"; ExpectedType = [decimal] }
711711

712+
#SByte Integer notation
713+
#Standard
714+
@{ Script = "0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
715+
@{ Script = "10y"; ExpectedValue = "10"; ExpectedType = [sbyte] }
716+
@{ Script = "-10y"; ExpectedValue = "-10"; ExpectedType = [sbyte] }
717+
@{ Script = "+10y"; ExpectedValue = "10"; ExpectedType = [sbyte] }
718+
#Conversion from <Real>
719+
@{ Script = "0.0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
720+
@{ Script = "3.72y"; ExpectedValue = "4"; ExpectedType = [sbyte] }
721+
@{ Script = "-3.72y"; ExpectedValue = "-4"; ExpectedType = [sbyte] }
722+
#Exponential
723+
@{ Script = "0e0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
724+
@{ Script = "3e0y"; ExpectedValue = "3"; ExpectedType = [sbyte] }
725+
@{ Script = "-3e0y"; ExpectedValue = "-3"; ExpectedType = [sbyte] }
726+
@{ Script = "3e1y"; ExpectedValue = "30"; ExpectedType = [sbyte] }
727+
@{ Script = "-3e1y"; ExpectedValue = "-30"; ExpectedType = [sbyte] }
728+
#Hexadecimal
729+
@{ Script = "0x0y"; ExpectedValue = "0"; ExpectedType = [sbyte] }
730+
@{ Script = "0x41y"; ExpectedValue = "65"; ExpectedType = [sbyte] }
731+
@{ Script = "-0x41y"; ExpectedValue = "-65"; ExpectedType = [sbyte] }
732+
712733
#Short Integer notation
713734
#Standard
714735
@{ Script = "0s"; ExpectedValue = "0"; ExpectedType = [short] }
@@ -778,6 +799,22 @@ foo``u{2195}abc
778799
@{ Script = "1utb"; ExpectedValue = "1099511627776"; ExpectedType = [ulong] }
779800
@{ Script = "1upb"; ExpectedValue = "1125899906842624"; ExpectedType = [ulong] }
780801

802+
#Byte Integer notation
803+
#Standard
804+
@{ Script = "0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
805+
@{ Script = "10uy"; ExpectedValue = "10"; ExpectedType = [byte] }
806+
@{ Script = "+10uy"; ExpectedValue = "10"; ExpectedType = [byte] }
807+
#Conversion from <Real>
808+
@{ Script = "0.0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
809+
@{ Script = "3.72uy"; ExpectedValue = "4"; ExpectedType = [byte] }
810+
#Exponential
811+
@{ Script = "0e0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
812+
@{ Script = "3e0uy"; ExpectedValue = "3"; ExpectedType = [byte] }
813+
@{ Script = "3e1uy"; ExpectedValue = "30"; ExpectedType = [byte] }
814+
#Hexadecimal
815+
@{ Script = "0x0uy"; ExpectedValue = "0"; ExpectedType = [byte] }
816+
@{ Script = "0x41uy"; ExpectedValue = "65"; ExpectedType = [byte] }
817+
781818
#Unsigned-Short Integer Notation
782819
#Standard
783820
@{ Script = "0us"; ExpectedValue = "0"; ExpectedType = [ushort] }

0 commit comments

Comments
 (0)