From f2686b99b3612f55212b03c647c70848234775d5 Mon Sep 17 00:00:00 2001 From: Stephan Tolksdorf Date: Sun, 5 Mar 2017 21:34:14 +0100 Subject: [PATCH] Implement NumberLiteralOption.AllowDigitSeparator It's super annoying that one can't factor out the the digit separator skip loops without negatively affecting the generated code. --- FParsec/CharParsers.fs | 216 +++++++++++++++++++++++++++++---------- FParsec/CharParsers.fsi | 68 ++++++------ Test/CharParsersTests.fs | 85 ++++++++++++++- 3 files changed, 283 insertions(+), 86 deletions(-) diff --git a/FParsec/CharParsers.fs b/FParsec/CharParsers.fs index 6d627db..cb29113 100644 --- a/FParsec/CharParsers.fs +++ b/FParsec/CharParsers.fs @@ -344,6 +344,10 @@ let inline isHex (c: char) = let inline isOctal (c: char) = uint32 c - uint32 '0' <= uint32 '7' - uint32 '0' +let inline private isBinaryDigit (c: char) = + uint32 c - uint32 '0' <= uint32 '1' - uint32 '0' + + let asciiUpper stream = fastInlineSatisfyE isAsciiUpper Errors.ExpectedAsciiUppercaseLetter stream let asciiLower stream = fastInlineSatisfyE isAsciiLower Errors.ExpectedAsciiLowercaseLetter stream let asciiLetter stream = fastInlineSatisfyE isAsciiLetter Errors.ExpectedAsciiLetter stream @@ -929,42 +933,46 @@ let withSkippedString (f: string -> 'a -> 'b) (p: Parser<'a,'u>) : Parser<'b,'u> [] type NumberLiteralOptions = | None = 0 - | AllowSuffix = 0b000000000001 - | AllowMinusSign = 0b000000000010 - | AllowPlusSign = 0b000000000100 - | AllowFraction = 0b000000001000 - | AllowFractionWOIntegerPart = 0b000000010000 - | AllowExponent = 0b000000100000 - | AllowHexadecimal = 0b000001000000 - | AllowBinary = 0b000010000000 - | AllowOctal = 0b000100000000 - | AllowInfinity = 0b001000000000 - | AllowNaN = 0b010000000000 - - | IncludeSuffixCharsInString = 0b100000000000 - - | DefaultInteger = 0b000111000110 - | DefaultUnsignedInteger = 0b000111000000 - | DefaultFloat = 0b011001101110 + | AllowSuffix = 0b0000000000001 + | AllowMinusSign = 0b0000000000010 + | AllowPlusSign = 0b0000000000100 + | AllowFraction = 0b0000000001000 + | AllowFractionWOIntegerPart = 0b0000000010000 + | AllowExponent = 0b0000000100000 + | AllowHexadecimal = 0b0000001000000 + | AllowBinary = 0b0000010000000 + | AllowOctal = 0b0000100000000 + | AllowInfinity = 0b0001000000000 + | AllowNaN = 0b0010000000000 + + | DefaultInteger = 0b0000111000110 + | DefaultUnsignedInteger = 0b0000111000000 + | DefaultFloat = 0b0011001101110 + + | IncludeSuffixCharsInString = 0b0100000000000 + + | AllowDigitSeparator = 0b1000000000000 + type internal NLO = NumberLiteralOptions [] type NumberLiteralResultFlags = - | None = 0 - | SuffixLengthMask = 0b0000000000001111 - | HasMinusSign = 0b0000000000010000 - | HasPlusSign = 0b0000000000100000 - | HasIntegerPart = 0b0000000001000000 - | HasFraction = 0b0000000010000000 - | HasExponent = 0b0000000100000000 - | IsDecimal = 0b0000001000000000 - | IsHexadecimal = 0b0000010000000000 - | IsBinary = 0b0000100000000000 - | IsOctal = 0b0001000000000000 - | BaseMask = 0b0001111000000000 - | IsInfinity = 0b0010000000000000 - | IsNaN = 0b0100000000000000 + | None = 0 + | SuffixLengthMask = 0b0000000000001111 + | HasMinusSign = 0b0000000000010000 + | HasPlusSign = 0b0000000000100000 + | HasIntegerPart = 0b0000000001000000 + | HasDigitSeparator = 0b1000000000000000 + | HasFraction = 0b0000000010000000 + | HasExponent = 0b0000000100000000 + | IsDecimal = 0b0000001000000000 + | IsHexadecimal = 0b0000010000000000 + | IsBinary = 0b0000100000000000 + | IsOctal = 0b0001000000000000 + | BaseMask = 0b0001111000000000 + | IsInfinity = 0b0010000000000000 + | IsNaN = 0b0100000000000000 type internal NLF = NumberLiteralResultFlags @@ -979,18 +987,19 @@ type NumberLiteral(string, info, suffixChar1, suffixChar2, suffixChar3, suffixCh member t.Info = info - member t.HasMinusSign = int (info &&& NLF.HasMinusSign) <> 0 - member t.HasPlusSign = int (info &&& NLF.HasPlusSign) <> 0 - member t.HasIntegerPart = int (info &&& NLF.HasIntegerPart) <> 0 - member t.HasFraction = int (info &&& NLF.HasFraction) <> 0 - member t.HasExponent = int (info &&& NLF.HasExponent) <> 0 - member t.IsInteger = int (info &&& (NLF.HasFraction ||| NLF.HasExponent)) = 0 // HasIntegerPart must be set if HasFraction and HasExponent both aren't - member t.IsDecimal = int (info &&& NLF.IsDecimal) <> 0 - member t.IsHexadecimal = int (info &&& NLF.IsHexadecimal) <> 0 - member t.IsBinary = int (info &&& NLF.IsBinary) <> 0 - member t.IsOctal = int (info &&& NLF.IsOctal) <> 0 - member t.IsNaN = int (info &&& NLF.IsNaN) <> 0 - member t.IsInfinity = int (info &&& NLF.IsInfinity) <> 0 + member t.HasMinusSign = int (info &&& NLF.HasMinusSign) <> 0 + member t.HasPlusSign = int (info &&& NLF.HasPlusSign) <> 0 + member t.HasIntegerPart = int (info &&& NLF.HasIntegerPart) <> 0 + member t.HasDigitSeparator = int (info &&& NLF.HasDigitSeparator) <> 0 + member t.HasFraction = int (info &&& NLF.HasFraction) <> 0 + member t.HasExponent = int (info &&& NLF.HasExponent) <> 0 + member t.IsInteger = int (info &&& (NLF.HasFraction ||| NLF.HasExponent)) = 0 // HasIntegerPart must be set if HasFraction and HasExponent both aren't + member t.IsDecimal = int (info &&& NLF.IsDecimal) <> 0 + member t.IsHexadecimal = int (info &&& NLF.IsHexadecimal) <> 0 + member t.IsBinary = int (info &&& NLF.IsBinary) <> 0 + member t.IsOctal = int (info &&& NLF.IsOctal) <> 0 + member t.IsNaN = int (info &&& NLF.IsNaN) <> 0 + member t.IsInfinity = int (info &&& NLF.IsInfinity) <> 0 override t.Equals(other: obj) = match other with @@ -1013,6 +1022,11 @@ let numberLiteralE (opt: NumberLiteralOptions) (errorInCaseNoLiteralFound: Error let mutable error = NoErrorMessages let mutable flags = NLF.None + // Note: Limitations of the F# compiler's inliner prevent us from refactoring out the common + // parts of this function if we don't want to compromise on performance. :-( + + let digitSeparator = '_' + if c = '-' && (opt &&& NLO.AllowMinusSign) <> NLO.None then flags <- NLF.HasMinusSign c <- stream.SkipAndPeek() @@ -1027,6 +1041,7 @@ let numberLiteralE (opt: NumberLiteralOptions) (errorInCaseNoLiteralFound: Error if c <> '0' || (c1 <- stream.SkipAndPeek(); c1 <= '9' + || c1 = digitSeparator || (opt &&& (NLO.AllowBinary ||| NLO.AllowOctal ||| NLO.AllowHexadecimal)) = NLO.None || ((int c1 ||| int ' ') = int 'e')) then @@ -1039,25 +1054,59 @@ let numberLiteralE (opt: NumberLiteralOptions) (errorInCaseNoLiteralFound: Error c <- c1 while isDigit c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isDigit c then + c <- stream.SkipAndPeek() + while isDigit c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedDecimalDigit + c <- '!' if c = '.' && (opt &&& NLO.AllowFraction) <> NLO.None then flags <- flags ||| NLF.HasFraction c <- stream.SkipAndPeek() if isDigit c then c <- stream.SkipAndPeek() - elif (flags &&& NLF.HasIntegerPart) = NLF.None then - // at least one digit before or after the . is required + elif c = digitSeparator || (flags &&& NLF.HasIntegerPart) = NLF.None then error <- Errors.ExpectedDecimalDigit + c <- '!' while isDigit c do c <- stream.SkipAndPeek() - if (int c ||| int ' ') = int 'e' && isNull error && (opt &&& NLO.AllowExponent) <> NLO.None then + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isDigit c then + c <- stream.SkipAndPeek() + while isDigit c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedDecimalDigit + c <- '!' + if (int c ||| int ' ') = int 'e' && (opt &&& NLO.AllowExponent) <> NLO.None then flags <- flags ||| NLF.HasExponent c <- stream.SkipAndPeek() if c = '-' || c = '+' then c <- stream.SkipAndPeek() if not (isDigit c) then error <- Errors.ExpectedDecimalDigit + c <- '!' while isDigit c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isDigit c then + c <- stream.SkipAndPeek() + while isDigit c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedDecimalDigit + c <- '!' else match int c1 ||| int ' ' with | 0x78 (* 'x' *) when (opt &&& NLO.AllowHexadecimal) <> NLO.None -> @@ -1066,33 +1115,68 @@ let numberLiteralE (opt: NumberLiteralOptions) (errorInCaseNoLiteralFound: Error if isHex c then flags <- flags ||| NLF.HasIntegerPart c <- stream.SkipAndPeek() - elif (opt &&& NLO.AllowFractionWOIntegerPart) = NLO.None then - // integer part required + elif c = digitSeparator || (opt &&& NLO.AllowFractionWOIntegerPart) = NLO.None then error <- Errors.ExpectedHexadecimalDigit + c <- '!' while isHex c do c <- stream.SkipAndPeek() - if c = '.' && isNull error && (opt &&& NLO.AllowFraction) <> NLO.None then + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isHex c then + c <- stream.SkipAndPeek() + while isHex c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedHexadecimalDigit + c <- '!' + if c = '.' && (opt &&& NLO.AllowFraction) <> NLO.None then flags <- flags ||| NLF.HasFraction c <- stream.SkipAndPeek() if isHex c then c <- stream.SkipAndPeek() - elif (flags &&& NLF.HasIntegerPart) = NLF.None then - // at least one digit before or after the . is required + elif c = digitSeparator || (flags &&& NLF.HasIntegerPart) = NLF.None then error <- Errors.ExpectedHexadecimalDigit + c <- '!' while isHex c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isHex c then + c <- stream.SkipAndPeek() + while isHex c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedHexadecimalDigit + c <- '!' elif (flags &&& NLF.HasIntegerPart) = NLF.None then // we neither have an integer part nor a fraction error <- Errors.ExpectedHexadecimalDigit - if (int c ||| int ' ') = int 'p' && isNull error && (opt &&& NLO.AllowExponent) <> NLO.None then + c <- '!' + if (int c ||| int ' ') = int 'p' && (opt &&& NLO.AllowExponent) <> NLO.None then flags <- flags ||| NLF.HasExponent c <- stream.SkipAndPeek() if c = '-' || c = '+' then c <- stream.SkipAndPeek() if not (isDigit c) then error <- Errors.ExpectedDecimalDigit + c <- '!' while isDigit c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isDigit c then + c <- stream.SkipAndPeek() + while isDigit c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedDecimalDigit + c <- '!' | 0x6f (* 'o' *) when (opt &&& NLO.AllowOctal) <> NLO.None -> flags <- flags ||| NLF.IsOctal c <- stream.SkipAndPeek() @@ -1101,18 +1185,42 @@ let numberLiteralE (opt: NumberLiteralOptions) (errorInCaseNoLiteralFound: Error c <- stream.SkipAndPeek() else error <- Errors.ExpectedOctalDigit + c <- '!' while isOctal c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isOctal c then + c <- stream.SkipAndPeek() + while isOctal c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedOctalDigit + c <- '!' | 0x62 (* 'b' *) when (opt &&& NLO.AllowBinary) <> NLO.None -> flags <- flags ||| NLF.IsBinary c <- stream.SkipAndPeek() - if c = '0' || c = '1' then + if isBinaryDigit c then flags <- flags ||| NLF.HasIntegerPart c <- stream.SkipAndPeek() else error <- Errors.ExpectedBinaryDigit - while c = '0' || c = '1' do + c <- '!' + while isBinaryDigit c do c <- stream.SkipAndPeek() + if (opt &&& NLO.AllowDigitSeparator) <> NLO.None then + while c = digitSeparator do + flags <- flags ||| NLF.HasDigitSeparator + c <- stream.SkipAndPeek() + if isBinaryDigit c then + c <- stream.SkipAndPeek() + while isBinaryDigit c do + c <- stream.SkipAndPeek() + else + error <- Errors.ExpectedBinaryDigit + c <- '!' | _ -> flags <- flags ||| (NLF.IsDecimal ||| NLF.HasIntegerPart) c <- c1 diff --git a/FParsec/CharParsers.fsi b/FParsec/CharParsers.fsi index ebeea75..589b6a0 100644 --- a/FParsec/CharParsers.fsi +++ b/FParsec/CharParsers.fsi @@ -507,23 +507,27 @@ val withSkippedString: (string -> 'a -> 'b) -> Parser<'a,'u> -> Parser<'b,'u> [] type NumberLiteralOptions = | None = 0 - | AllowSuffix = 0b000000000001 - | AllowMinusSign = 0b000000000010 - | AllowPlusSign = 0b000000000100 - | AllowFraction = 0b000000001000 - | AllowFractionWOIntegerPart = 0b000000010000 - | AllowExponent = 0b000000100000 - | AllowHexadecimal = 0b000001000000 - | AllowBinary = 0b000010000000 - | AllowOctal = 0b000100000000 - | AllowInfinity = 0b001000000000 - | AllowNaN = 0b010000000000 - - | IncludeSuffixCharsInString = 0b100000000000 - - | DefaultInteger = 0b000111000110 - | DefaultUnsignedInteger = 0b000111000000 - | DefaultFloat = 0b011001101110 + | AllowSuffix = 0b0000000000001 + | AllowMinusSign = 0b0000000000010 + | AllowPlusSign = 0b0000000000100 + | AllowFraction = 0b0000000001000 + | AllowFractionWOIntegerPart = 0b0000000010000 + | AllowExponent = 0b0000000100000 + | AllowHexadecimal = 0b0000001000000 + | AllowBinary = 0b0000010000000 + | AllowOctal = 0b0000100000000 + | AllowInfinity = 0b0001000000000 + | AllowNaN = 0b0010000000000 + + | DefaultInteger = 0b0000111000110 + | DefaultUnsignedInteger = 0b0000111000000 + | DefaultFloat = 0b0011001101110 + + | IncludeSuffixCharsInString = 0b0100000000000 + + /// Allows an underscore char ('_') as a digit separator. + /// Each underscore must be surrounded by digits on both sides. + | AllowDigitSeparator = 0b1000000000000 /// The return type of the `numberLiteral` parser. An instance contains the parsed /// number literal and various bits of information about it. @@ -554,6 +558,7 @@ type NumberLiteral = member HasMinusSign: bool member HasPlusSign: bool member HasIntegerPart: bool + member HasDigitSeparator: bool member HasFraction: bool member HasExponent: bool member IsInteger: bool @@ -570,20 +575,21 @@ type NumberLiteral = and /// Encodes various bits of information about a parsed number literal. [] NumberLiteralResultFlags = - | None = 0 - | SuffixLengthMask = 0b0000000000001111 - | HasMinusSign = 0b0000000000010000 - | HasPlusSign = 0b0000000000100000 - | HasIntegerPart = 0b0000000001000000 - | HasFraction = 0b0000000010000000 - | HasExponent = 0b0000000100000000 - | IsDecimal = 0b0000001000000000 - | IsHexadecimal = 0b0000010000000000 - | IsBinary = 0b0000100000000000 - | IsOctal = 0b0001000000000000 - | BaseMask = 0b0001111000000000 - | IsInfinity = 0b0010000000000000 - | IsNaN = 0b0100000000000000 + | None = 0 + | SuffixLengthMask = 0b0000000000001111 + | HasMinusSign = 0b0000000000010000 + | HasPlusSign = 0b0000000000100000 + | HasIntegerPart = 0b0000000001000000 + | HasDigitSeparator = 0b1000000000000000 + | HasFraction = 0b0000000010000000 + | HasExponent = 0b0000000100000000 + | IsDecimal = 0b0000001000000000 + | IsHexadecimal = 0b0000010000000000 + | IsBinary = 0b0000100000000000 + | IsOctal = 0b0001000000000000 + | BaseMask = 0b0001111000000000 + | IsInfinity = 0b0010000000000000 + | IsNaN = 0b0100000000000000 /// `numberLiteral options label` parses a number literal and returns the result in form diff --git a/Test/CharParsersTests.fs b/Test/CharParsersTests.fs index 72de12d..c9a2f0e 100644 --- a/Test/CharParsersTests.fs +++ b/Test/CharParsersTests.fs @@ -653,7 +653,7 @@ let testNumberParsers() = ||| NLO.AllowOctal ||| NLO.AllowInfinity ||| NLO.AllowNaN - + ||| NLO.AllowDigitSeparator numberLiteral all "nl" |> RError "|" 0 (expected "nl") numberLiteral all "nl" |> RError "+|" 0 (expected "nl") @@ -775,6 +775,89 @@ let testNumberParsers() = numberLiteral (all ^^^ NLO.AllowNaN) "nl" |> RError "NaN|" 0 (expected "nl") numberLiteral (all ^^^ NLO.AllowNaN) "nl" |> ROk "Infinity|" (NumberLiteral("Infinity", NLF.IsInfinity, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0_0|" (NumberLiteral("0_0", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk ".0_0|" (NumberLiteral(".0_0", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0e-0_0|" (NumberLiteral("0e-0_0", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> ROk "0_000_0_00|" (NumberLiteral("0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk ".0_000_0_00|" (NumberLiteral(".0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0e-0_000_0_00|" (NumberLiteral("0e-0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsDecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> ROk "0x0_0|" (NumberLiteral("0x0_0", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0x.0_0|" (NumberLiteral("0x.0_0", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0x0p-0_0|" (NumberLiteral("0x0p-0_0", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> ROk "0x0_000_0_00|" (NumberLiteral("0x0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0x.0_000_0_00|" (NumberLiteral("0x.0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0x0p-0_000_0_00|" (NumberLiteral("0x0p-0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsHexadecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> ROk "0o0_0|" (NumberLiteral("0o0_0", NLF.HasDigitSeparator ||| NLF.IsOctal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0o0_000_0_00|" (NumberLiteral("0o0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsOctal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> ROk "0b0_0|" (NumberLiteral("0b0_0", NLF.HasDigitSeparator ||| NLF.IsBinary ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral all "nl" |> ROk "0b0_000_0_00|" (NumberLiteral("0b0_000_0_00", NLF.HasDigitSeparator ||| NLF.IsBinary ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0_0|" 1 (NumberLiteral("0", NLF.IsDecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI ".0_0|" 2 (NumberLiteral(".0", NLF.IsDecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0e0_0|" 3 (NumberLiteral("0e0", NLF.IsDecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0x0_0|" 3 (NumberLiteral("0x0", NLF.IsHexadecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0x.0_0|" 4 (NumberLiteral("0x.0", NLF.IsHexadecimal ||| NLF.HasFraction, EOS, EOS, EOS, EOS)) + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0x0p0_0|" 5 (NumberLiteral("0x0p0", NLF.IsHexadecimal ||| NLF.HasIntegerPart ||| NLF.HasExponent, EOS, EOS, EOS, EOS)) + + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0o0_0|" 3 (NumberLiteral("0o0", NLF.IsOctal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "0b0_0|" 3 (NumberLiteral("0b0", NLF.IsBinary ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> RError "_1" 0 (expected "nl") + numberLiteral (all ^^^ NLO.AllowFractionWOIntegerPart) "nl" |> RError "._0" 0 (expected "nl") + + numberLiteral (all ^^^ NLO.AllowDigitSeparator) "nl" |> ROkI "1_." 1 (NumberLiteral("1", NLF.IsDecimal ||| NLF.HasIntegerPart, EOS, EOS, EOS, EOS)) + + numberLiteral all "nl" |> RError "1_." 2 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1__1" 2 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1._e" 2 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_e" 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e_0" 3 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e1_f" 5 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "1_1_." 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_e" 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_1_e" 6 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e1_1_f" 7 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "1_." 2 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1._e" 2 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_e" 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e_0" 3 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e1_f" 5 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "1_1_." 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_e" 4 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.1_1_e" 6 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "1.e1_1_f" 7 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "0x_1" 2 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1_." 4 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1__1" 4 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1._p" 4 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1.1_p" 6 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1.p_0" 5 Errors.ExpectedDecimalDigit + numberLiteral all "nl" |> RError "0x1.p1_f" 7 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "0x1_1_." 6 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1.1_p" 6 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1.1_1_p" 8 Errors.ExpectedHexadecimalDigit + numberLiteral all "nl" |> RError "0x1.p1_1_f" 9 Errors.ExpectedDecimalDigit + + numberLiteral all "nl" |> RError "0o_1" 2 Errors.ExpectedOctalDigit + numberLiteral all "nl" |> RError "0o1__1" 4 Errors.ExpectedOctalDigit + numberLiteral all "nl" |> RError "0o1_2__1" 6 Errors.ExpectedOctalDigit + + numberLiteral all "nl" |> RError "0b_1" 2 Errors.ExpectedBinaryDigit + numberLiteral all "nl" |> RError "0b1__1" 4 Errors.ExpectedBinaryDigit + numberLiteral all "nl" |> RError "0b1_0__1" 6 Errors.ExpectedBinaryDigit + + testNumberLiteral() let testPfloat() =