예제 #1
0
        public static bool Try_Parse_Integer(DataConsumer <char> Stream, out long outValue)
        {/* Docs: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#signed-integers */
            if (Stream is null)
            {
                throw new ArgumentNullException(nameof(Stream));
            }
            Contract.EndContractBlock();

            bool sign = true;

            /* SKip ASCII whitespace */
            Stream.Consume_While(Is_Ascii_Whitespace);

            if (Stream.atEnd)
            {
                outValue = long.MaxValue;
                return(false);
            }

            if (Stream.Next == CHAR_HYPHEN_MINUS)
            {
                sign = false;
                Stream.Consume();
            }
            else if (Stream.Next == CHAR_PLUS_SIGN)
            {
                Stream.Consume();
            }

            if (!Stream.atEnd && !Is_Ascii_Digit(Stream.Next))
            {
                outValue = long.MaxValue;
                return(false);
            }

            /* Collect sequence of ASCII digit codepoints */
            Stream.Consume_While(Is_Ascii_Digit, out ReadOnlySpan <char> outDigits);

            var parsed = ParsingCommon.Digits_To_Base10(outDigits);

            outValue = sign ? parsed : 0 - parsed;
            return(true);
        }
예제 #2
0
 public void Digits_To_Base10Test(long expected, string input)
 {
     Assert.Equal(expected, ParsingCommon.Digits_To_Base10(input));
 }
예제 #3
0
        public static bool Try_Parse_FloatingPoint(DataConsumer <char> Stream, out double outValue)
        {/* Docs: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values */
            if (Stream is null)
            {
                throw new ArgumentNullException(nameof(Stream));
            }
            Contract.EndContractBlock();

            double value    = 1;
            double divisor  = 1;
            double exponent = 1;

            /* Skip ASCII whitespace */
            Stream.Consume_While(Is_Ascii_Whitespace);

            if (Stream.atEnd)
            {
                outValue = double.NaN;
                return(false);
            }

            switch (Stream.Next)
            {
            case CHAR_HYPHEN_MINUS:
            {
                value = divisor = -1;
                Stream.Consume();
            }
            break;

            case CHAR_PLUS_SIGN:
            {
                Stream.Consume();
            }
            break;
            }

            if (Stream.atEnd)
            {
                outValue = double.NaN;
                return(false);
            }

            /* 9) If the character indicated by position is a U+002E FULL STOP (.),
             * and that is not the last character in input,
             * and the character after the character indicated by position is an ASCII digit,
             * then set value to zero and jump to the step labeled fraction. */
            if (Stream.Remaining > 1 && Stream.Next == CHAR_FULL_STOP && Is_Ascii_Digit(Stream.NextNext))
            {
                value = 0;
            }
            else if (!Is_Ascii_Digit(Stream.Next))
            {
                outValue = double.NaN;
                return(false);
            }
            else
            {
                /* 11) Collect a sequence of code points that are ASCII digits from input given position, and interpret the resulting sequence as a base-ten integer. Multiply value by that integer. */
                Stream.Consume_While(Is_Ascii_Digit, out ReadOnlySpan <char> outDigits);
                value *= ParsingCommon.Digits_To_Base10(outDigits);
            }

            /* 12) If position is past the end of input, jump to the step labeled conversion. */
            if (!Stream.atEnd)
            {
                /* 13) Fraction: If the character indicated by position is a U+002E FULL STOP (.), run these substeps: */
                if (Stream.Next == CHAR_FULL_STOP)
                {
                    Stream.Consume();
                    /* 2) If position is past the end of input, or if the character indicated by position is not an ASCII digit, U+0065 LATIN SMALL LETTER E (e), or U+0045 LATIN CAPITAL LETTER E (E), then jump to the step labeled conversion. */
                    if (!Stream.atEnd && (Is_Ascii_Digit(Stream.Next) || Stream.Next == CHAR_E_LOWER || Stream.Next == CHAR_E_UPPER))
                    {
                        /* 3) If the character indicated by position is a U+0065 LATIN SMALL LETTER E character (e) or a U+0045 LATIN CAPITAL LETTER E character (E), skip the remainder of these substeps. */
                        if (Is_Ascii_Digit(Stream.Next))
                        {
                            while (Is_Ascii_Digit(Stream.Next))
                            {
                                /* 4) Fraction loop: Multiply divisor by ten. */
                                divisor *= 10;
                                /* 5) Add the value of the character indicated by position, interpreted as a base-ten digit (0..9) and divided by divisor, to value. */
                                double n = Ascii_Digit_To_Value(Stream.Next) / divisor;
                                value += n;
                                /* 6) Advance position to the next character. */
                                Stream.Consume();
                                /* 7) If position is past the end of input, then jump to the step labeled conversion. */

                                if (Stream.atEnd)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }

                /* 14) If the character indicated by position is U+0065 (e) or a U+0045 (E), then: */
                if (Stream.Next == CHAR_E_LOWER || Stream.Next == CHAR_E_UPPER)
                {
                    Stream.Consume();
                    /* 2) If position is past the end of input, then jump to the step labeled conversion. */
                    /* 3) If the character indicated by position is a U+002D HYPHEN-MINUS character (-): */
                    if (Stream.Next == CHAR_HYPHEN_MINUS)
                    {
                        exponent = -1;
                        Stream.Consume();
                    }
                    else if (Stream.Next == CHAR_PLUS_SIGN)
                    {
                        Stream.Consume();
                    }

                    /* 4) If the character indicated by position is not an ASCII digit, then jump to the step labeled conversion. */
                    if (Is_Ascii_Digit(Stream.Next))
                    {
                        /* 5) Collect a sequence of code points that are ASCII digits from input given position, and interpret the resulting sequence as a base-ten integer. Multiply exponent by that integer. */
                        Stream.Consume_While(Is_Ascii_Digit, out ReadOnlySpan <char> outDigits);
                        exponent *= ParsingCommon.Digits_To_Base10(outDigits);
                        /* 6) Multiply value by ten raised to the exponentth power. */
                        value *= Math.Pow(10, exponent);
                    }
                }
            }

            /* 15) Conversion: Let S be the set of finite IEEE 754 double-precision floating-point values except −0, but with two special values added: 21024 and −21024. */

            /* 16) Let rounded-value be the number in S that is closest to value,
             * selecting the number with an even significand if there are two equally close values.
             * (The two special values 21024 and −21024 are considered to have even significands for this purpose.) */
            var roundedValue = Math.Round(value, MidpointRounding.ToEven);

            /* 17) If rounded-value is 21024 or −21024, return an error. */
            if ((roundedValue == double.MinValue) || (roundedValue == double.MaxValue))
            {
                outValue = double.NaN;
                return(false);
            }

            outValue = roundedValue;
            return(true);
        }