/*
 * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/CharacterTypes.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>

namespace JS::Temporal {

namespace Detail {

// https://tc39.es/proposal-temporal/#prod-DecimalDigits
bool ISO8601Parser::parse_decimal_digits()
{
    // DecimalDigits[Sep] ::
    //     DecimalDigit
    //     DecimalDigits[?Sep] DecimalDigit
    //     [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator DecimalDigit
    // NOTE: Temporal exclusively uses the variant without a separator ([~Sep])
    if (!parse_decimal_digit())
        return false;
    while (parse_decimal_digit())
        ;
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DecimalDigit
bool ISO8601Parser::parse_decimal_digit()
{
    // DecimalDigit : one of
    //     0 1 2 3 4 5 6 7 8 9
    if (m_state.lexer.next_is(is_ascii_digit)) {
        m_state.lexer.consume();
        return true;
    }
    return false;
}

// https://tc39.es/proposal-temporal/#prod-NonZeroDigit
bool ISO8601Parser::parse_non_zero_digit()
{
    // NonZeroDigit : one of
    //     1 2 3 4 5 6 7 8 9
    if (m_state.lexer.next_is(is_ascii_digit) && !m_state.lexer.next_is('0')) {
        m_state.lexer.consume();
        return true;
    }
    return false;
}

// https://tc39.es/proposal-temporal/#prod-ASCIISign
bool ISO8601Parser::parse_ascii_sign()
{
    // ASCIISign : one of
    //     + -
    return m_state.lexer.consume_specific('+')
        || m_state.lexer.consume_specific('-');
}

// https://tc39.es/proposal-temporal/#prod-Sign
bool ISO8601Parser::parse_sign()
{
    // Sign :
    //     ASCIISign
    //     U+2212
    StateTransaction transaction { *this };
    auto success = parse_ascii_sign()
        || m_state.lexer.consume_specific("\xE2\x88\x92"sv);
    if (!success)
        return false;
    m_state.parse_result.sign = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-UnpaddedHour
bool ISO8601Parser::parse_unpadded_hour()
{
    // UnpaddedHour :
    //     DecimalDigit
    //     1 DecimalDigit
    //     20
    //     21
    //     22
    //     23
    StateTransaction transaction { *this };
    auto success = m_state.lexer.consume_specific("20"sv)
        || m_state.lexer.consume_specific("21"sv)
        || m_state.lexer.consume_specific("22"sv)
        || m_state.lexer.consume_specific("23"sv);
    if (!success) {
        // This could be either of the first two productions.
        if (m_state.lexer.consume_specific('1'))
            (void)parse_decimal_digit();
        else if (!parse_decimal_digit())
            return false;
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-Hour
bool ISO8601Parser::parse_hour()
{
    // Hour :
    //     0 DecimalDigit
    //     1 DecimalDigit
    //     20
    //     21
    //     22
    //     23
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('0') || m_state.lexer.consume_specific('1')) {
        if (!parse_decimal_digit())
            return false;
    } else {
        auto success = m_state.lexer.consume_specific("20"sv)
            || m_state.lexer.consume_specific("21"sv)
            || m_state.lexer.consume_specific("22"sv)
            || m_state.lexer.consume_specific("23"sv);
        if (!success)
            return false;
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-MinuteSecond
bool ISO8601Parser::parse_minute_second()
{
    // MinuteSecond :
    //     0 DecimalDigit
    //     1 DecimalDigit
    //     2 DecimalDigit
    //     3 DecimalDigit
    //     4 DecimalDigit
    //     5 DecimalDigit
    StateTransaction transaction { *this };
    auto success = m_state.lexer.consume_specific('0')
        || m_state.lexer.consume_specific('1')
        || m_state.lexer.consume_specific('2')
        || m_state.lexer.consume_specific('3')
        || m_state.lexer.consume_specific('4')
        || m_state.lexer.consume_specific('5');
    if (!success)
        return false;
    if (!parse_decimal_digit())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DecimalSeparator
bool ISO8601Parser::parse_decimal_separator()
{
    // DecimalSeparator : one of
    //     . ,
    return m_state.lexer.consume_specific('.')
        || m_state.lexer.consume_specific(',');
}

// https://tc39.es/proposal-temporal/#prod-DaysDesignator
bool ISO8601Parser::parse_days_designator()
{
    // DaysDesignator : one of
    //     D d
    return m_state.lexer.consume_specific('D')
        || m_state.lexer.consume_specific('d');
}

// https://tc39.es/proposal-temporal/#prod-HoursDesignator
bool ISO8601Parser::parse_hours_designator()
{
    // HoursDesignator : one of
    //     H h
    return m_state.lexer.consume_specific('H')
        || m_state.lexer.consume_specific('h');
}

// https://tc39.es/proposal-temporal/#prod-MinutesDesignator
bool ISO8601Parser::parse_minutes_designator()
{
    // MinutesDesignator : one of
    //     M m
    return m_state.lexer.consume_specific('M')
        || m_state.lexer.consume_specific('m');
}

// https://tc39.es/proposal-temporal/#prod-MonthsDesignator
bool ISO8601Parser::parse_months_designator()
{
    // MonthsDesignator : one of
    //     M m
    return m_state.lexer.consume_specific('M')
        || m_state.lexer.consume_specific('m');
}

// https://tc39.es/proposal-temporal/#prod-DurationDesignator
bool ISO8601Parser::parse_duration_designator()
{
    // DurationDesignator : one of
    //     P p
    return m_state.lexer.consume_specific('P')
        || m_state.lexer.consume_specific('p');
}

// https://tc39.es/proposal-temporal/#prod-SecondsDesignator
bool ISO8601Parser::parse_seconds_designator()
{
    // SecondsDesignator : one of
    //     S s
    return m_state.lexer.consume_specific('S')
        || m_state.lexer.consume_specific('s');
}

// https://tc39.es/proposal-temporal/#prod-DateTimeSeparator
bool ISO8601Parser::parse_date_time_separator()
{
    // DateTimeSeparator :
    //     <SP>
    //     T
    //     t
    return m_state.lexer.consume_specific(' ')
        || m_state.lexer.consume_specific('T')
        || m_state.lexer.consume_specific('t');
}

// https://tc39.es/proposal-temporal/#prod-TimeDesignator
bool ISO8601Parser::parse_time_designator()
{
    // TimeDesignator : one of
    //     T t
    return m_state.lexer.consume_specific('T')
        || m_state.lexer.consume_specific('t');
}

// https://tc39.es/proposal-temporal/#prod-WeeksDesignator
bool ISO8601Parser::parse_weeks_designator()
{
    // WeeksDesignator : one of
    //     W w
    return m_state.lexer.consume_specific('W')
        || m_state.lexer.consume_specific('w');
}

// https://tc39.es/proposal-temporal/#prod-YearsDesignator
bool ISO8601Parser::parse_years_designator()
{
    // YearsDesignator : one of
    //     Y y
    return m_state.lexer.consume_specific('Y')
        || m_state.lexer.consume_specific('y');
}

// https://tc39.es/proposal-temporal/#prod-UTCDesignator
bool ISO8601Parser::parse_utc_designator()
{
    // UTCDesignator : one of
    //     Z z
    StateTransaction transaction { *this };
    auto success = m_state.lexer.consume_specific('Z')
        || m_state.lexer.consume_specific('z');
    if (!success)
        return false;
    m_state.parse_result.utc_designator = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateYear
bool ISO8601Parser::parse_date_year()
{
    // DateFourDigitYear :
    //     DecimalDigit DecimalDigit DecimalDigit DecimalDigit
    // DateExtendedYear :
    //     Sign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
    // DateYear :
    //     DateFourDigitYear
    //     DateExtendedYear
    StateTransaction transaction { *this };
    if (parse_sign()) {
        for (size_t i = 0; i < 6; ++i) {
            if (!parse_decimal_digit())
                return false;
        }
    } else {
        for (size_t i = 0; i < 4; ++i) {
            if (!parse_decimal_digit())
                return false;
        }
    }
    // It is a Syntax Error if DateExtendedYear is "-000000" or "−000000" (U+2212 MINUS SIGN followed by 000000).
    if (transaction.parsed_string_view().is_one_of("-000000"sv, "−000000"sv))
        return false;
    m_state.parse_result.date_year = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateMonth
bool ISO8601Parser::parse_date_month()
{
    // DateMonth :
    //     0 NonZeroDigit
    //     10
    //     11
    //     12
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('0')) {
        if (!parse_non_zero_digit())
            return false;
    } else {
        auto success = m_state.lexer.consume_specific("10"sv)
            || m_state.lexer.consume_specific("11"sv)
            || m_state.lexer.consume_specific("12"sv);
        if (!success)
            return false;
    }
    m_state.parse_result.date_month = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateDay
bool ISO8601Parser::parse_date_day()
{
    // DateDay :
    //     0 NonZeroDigit
    //     1 DecimalDigit
    //     2 DecimalDigit
    //     30
    //     31
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('0')) {
        if (!parse_non_zero_digit())
            return false;
    } else if (m_state.lexer.consume_specific('1') || m_state.lexer.consume_specific('2')) {
        if (!parse_decimal_digit())
            return false;
    } else {
        auto success = m_state.lexer.consume_specific("30"sv)
            || m_state.lexer.consume_specific("31"sv);
        if (!success)
            return false;
    }
    m_state.parse_result.date_day = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateSpecYearMonth
bool ISO8601Parser::parse_date_spec_year_month()
{
    // DateSpecYearMonth :
    //     DateYear -[opt] DateMonth
    StateTransaction transaction { *this };
    if (!parse_date_year())
        return false;
    m_state.lexer.consume_specific('-');
    if (!parse_date_month())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateSpecMonthDay
bool ISO8601Parser::parse_date_spec_month_day()
{
    // TwoDashes :
    //     --
    // DateSpecMonthDay :
    //     TwoDashes[opt] DateMonth -[opt] DateDay
    StateTransaction transaction { *this };
    m_state.lexer.consume_specific("--"sv);
    if (!parse_date_month())
        return false;
    m_state.lexer.consume_specific('-');
    if (!parse_date_day())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-Date
bool ISO8601Parser::parse_date()
{
    // Date :
    //     DateYear - DateMonth - DateDay
    //     DateYear DateMonth DateDay
    StateTransaction transaction { *this };
    if (!parse_date_year())
        return false;
    auto with_dashes = m_state.lexer.consume_specific('-');
    if (!parse_date_month())
        return false;
    if (with_dashes && !m_state.lexer.consume_specific('-'))
        return false;
    if (!parse_date_day())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeHour
bool ISO8601Parser::parse_time_hour()
{
    // TimeHour :
    //     Hour
    StateTransaction transaction { *this };
    if (!parse_hour())
        return false;
    m_state.parse_result.time_hour = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeMinute
bool ISO8601Parser::parse_time_minute()
{
    // TimeMinute :
    //     MinuteSecond
    StateTransaction transaction { *this };
    if (!parse_minute_second())
        return false;
    m_state.parse_result.time_minute = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeSecond
bool ISO8601Parser::parse_time_second()
{
    // TimeSecond :
    //     MinuteSecond
    //     60
    StateTransaction transaction { *this };
    auto success = parse_minute_second()
        || m_state.lexer.consume_specific("60"sv);
    if (!success)
        return false;
    m_state.parse_result.time_second = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeHourNotValidMonth
bool ISO8601Parser::parse_time_hour_not_valid_month()
{
    // TimeHourNotValidMonth : one of
    //     00 13 14 15 16 17 18 19 20 21 23
    StateTransaction transaction { *this };
    auto success = m_state.lexer.consume_specific("00"sv)
        || m_state.lexer.consume_specific("13"sv)
        || m_state.lexer.consume_specific("14"sv)
        || m_state.lexer.consume_specific("15"sv)
        || m_state.lexer.consume_specific("16"sv)
        || m_state.lexer.consume_specific("17"sv)
        || m_state.lexer.consume_specific("18"sv)
        || m_state.lexer.consume_specific("19"sv)
        || m_state.lexer.consume_specific("20"sv)
        || m_state.lexer.consume_specific("21"sv)
        || m_state.lexer.consume_specific("22"sv)
        || m_state.lexer.consume_specific("23"sv);
    if (!success)
        return false;
    m_state.parse_result.time_hour_not_valid_month = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeHourNotThirtyOneDayMonth
bool ISO8601Parser::parse_time_hour_not_thirty_one_day_month()
{
    // TimeHourNotThirtyOneDayMonth : one of
    //     02 04 06 09 11
    StateTransaction transaction { *this };
    auto success = m_state.lexer.consume_specific("02"sv)
        || m_state.lexer.consume_specific("04"sv)
        || m_state.lexer.consume_specific("06"sv)
        || m_state.lexer.consume_specific("09"sv)
        || m_state.lexer.consume_specific("11"sv);
    if (!success)
        return false;
    m_state.parse_result.time_hour_not_thirty_one_day_month = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeHourTwoOnly
bool ISO8601Parser::parse_time_hour_two_only()
{
    // TimeHourTwoOnly :
    //     02
    StateTransaction transaction { *this };
    if (!m_state.lexer.consume_specific("02"sv))
        return false;
    m_state.parse_result.time_hour_two_only = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeMinuteNotValidDay
bool ISO8601Parser::parse_time_minute_not_valid_day()
{
    // TimeMinuteNotValidDay :
    //     00
    //     32
    //     33
    //     34
    //     35
    //     36
    //     37
    //     38
    //     39
    //     4 DecimalDigit
    //     5 DecimalDigit
    //     60
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('4') || m_state.lexer.consume_specific('5')) {
        if (!parse_decimal_digit())
            return false;
    } else {
        auto success = m_state.lexer.consume_specific("00"sv)
            || m_state.lexer.consume_specific("32"sv)
            || m_state.lexer.consume_specific("33"sv)
            || m_state.lexer.consume_specific("34"sv)
            || m_state.lexer.consume_specific("35"sv)
            || m_state.lexer.consume_specific("36"sv)
            || m_state.lexer.consume_specific("37"sv)
            || m_state.lexer.consume_specific("38"sv)
            || m_state.lexer.consume_specific("39"sv)
            || m_state.lexer.consume_specific("60"sv);
        if (!success)
            return false;
    }
    m_state.parse_result.time_minute_not_valid_day = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeMinuteThirtyOnly
bool ISO8601Parser::parse_time_minute_thirty_only()
{
    // TimeMinuteThirtyOnly :
    //     30
    StateTransaction transaction { *this };
    if (!m_state.lexer.consume_specific("30"sv))
        return false;
    m_state.parse_result.time_minute_thirty_only = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeMinuteThirtyOneOnly
bool ISO8601Parser::parse_time_minute_thirty_one_only()
{
    // TimeMinuteThirtyOneOnly :
    //     31
    StateTransaction transaction { *this };
    if (!m_state.lexer.consume_specific("31"sv))
        return false;
    m_state.parse_result.time_minute_thirty_one_only = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeSecondNotValidMonth
bool ISO8601Parser::parse_time_second_not_valid_month()
{
    // TimeSecondNotValidMonth :
    //     00
    //     13
    //     14
    //     15
    //     16
    //     17
    //     18
    //     19
    //     2 DecimalDigit
    //     3 DecimalDigit
    //     4 DecimalDigit
    //     5 DecimalDigit
    //     60
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('2')
        || m_state.lexer.consume_specific('3')
        || m_state.lexer.consume_specific('4')
        || m_state.lexer.consume_specific('5')) {
        if (!parse_decimal_digit())
            return false;
    } else {
        auto success = m_state.lexer.consume_specific("00"sv)
            || m_state.lexer.consume_specific("13"sv)
            || m_state.lexer.consume_specific("14"sv)
            || m_state.lexer.consume_specific("15"sv)
            || m_state.lexer.consume_specific("16"sv)
            || m_state.lexer.consume_specific("17"sv)
            || m_state.lexer.consume_specific("18"sv)
            || m_state.lexer.consume_specific("19"sv)
            || m_state.lexer.consume_specific("60"sv);
        if (!success)
            return false;
    }
    m_state.parse_result.time_second_not_valid_month = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-FractionalPart
bool ISO8601Parser::parse_fractional_part()
{
    // FractionalPart :
    //     DecimalDigit DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt] DecimalDigit[opt]
    if (!parse_decimal_digit())
        return false;
    for (size_t i = 0; i < 8; ++i) {
        if (!parse_decimal_digit())
            break;
    }
    return true;
}

// https://tc39.es/proposal-temporal/#prod-Fraction
bool ISO8601Parser::parse_fraction()
{
    // Fraction :
    //     DecimalSeparator FractionalPart
    StateTransaction transaction { *this };
    if (!parse_decimal_separator())
        return false;
    if (!parse_fractional_part())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeFraction
bool ISO8601Parser::parse_time_fraction()
{
    // TimeFraction :
    //     Fraction
    StateTransaction transaction { *this };
    if (!parse_fraction())
        return false;
    m_state.parse_result.time_fraction = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSign
bool ISO8601Parser::parse_time_zone_utc_offset_sign()
{
    // TimeZoneUTCOffsetSign :
    //     Sign
    StateTransaction transaction { *this };
    if (!parse_sign())
        return false;
    m_state.parse_result.time_zone_utc_offset_sign = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetHour
bool ISO8601Parser::parse_time_zone_utc_offset_hour()
{
    // TimeZoneUTCOffsetHour :
    //     Hour
    StateTransaction transaction { *this };
    if (!parse_hour())
        return false;
    m_state.parse_result.time_zone_utc_offset_hour = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetMinute
bool ISO8601Parser::parse_time_zone_utc_offset_minute()
{
    // TimeZoneUTCOffsetMinute :
    //     MinuteSecond
    StateTransaction transaction { *this };
    if (!parse_minute_second())
        return false;
    m_state.parse_result.time_zone_utc_offset_minute = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSecond
bool ISO8601Parser::parse_time_zone_utc_offset_second()
{
    // TimeZoneUTCOffsetSecond :
    //     MinuteSecond
    StateTransaction transaction { *this };
    if (!parse_minute_second())
        return false;
    m_state.parse_result.time_zone_utc_offset_second = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetFractionalPart
bool ISO8601Parser::parse_time_zone_utc_offset_fractional_part()
{
    // TimeZoneUTCOffsetFractionalPart :
    //     FractionalPart
    StateTransaction transaction { *this };
    if (!parse_fractional_part())
        return false;
    m_state.parse_result.time_zone_utc_offset_fractional_part = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetFraction
bool ISO8601Parser::parse_time_zone_utc_offset_fraction()
{
    // TimeZoneUTCOffsetFraction :
    //     DecimalSeparator TimeZoneUTCOffsetFractionalPart
    StateTransaction transaction { *this };
    if (!parse_decimal_separator())
        return false;
    if (!parse_time_zone_utc_offset_fractional_part())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffset
bool ISO8601Parser::parse_time_zone_numeric_utc_offset()
{
    // TimeZoneNumericUTCOffset :
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
    StateTransaction transaction { *this };
    if (!parse_time_zone_utc_offset_sign())
        return false;
    if (!parse_time_zone_utc_offset_hour())
        return false;
    if (m_state.lexer.consume_specific(':')) {
        if (!parse_time_zone_utc_offset_minute())
            return false;
        if (m_state.lexer.consume_specific(':')) {
            if (!parse_time_zone_utc_offset_second())
                return false;
            (void)parse_time_zone_utc_offset_fraction();
        }
    } else if (parse_time_zone_utc_offset_minute()) {
        if (parse_time_zone_utc_offset_second())
            (void)parse_time_zone_utc_offset_fraction();
    }
    m_state.parse_result.time_zone_numeric_utc_offset = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffset
bool ISO8601Parser::parse_time_zone_utc_offset()
{
    // TimeZoneUTCOffset :
    //     TimeZoneNumericUTCOffset
    //     UTCDesignator
    return parse_time_zone_numeric_utc_offset()
        || parse_utc_designator();
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffsetNotAmbiguous
bool ISO8601Parser::parse_time_zone_numeric_utc_offset_not_ambiguous()
{
    // TimeZoneNumericUTCOffsetNotAmbiguous :
    //     + TimeZoneUTCOffsetHour
    //     U+2212 TimeZoneUTCOffsetHour
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
    StateTransaction transaction { *this };
    if (m_state.lexer.consume_specific('+') || m_state.lexer.consume_specific("\xE2\x88\x92"sv)) {
        if (!parse_time_zone_utc_offset_hour())
            return false;
    } else {
        if (!parse_time_zone_utc_offset_sign())
            return false;
        if (!parse_time_zone_utc_offset_hour())
            return false;
        if (m_state.lexer.consume_specific(':')) {
            if (!parse_time_zone_utc_offset_minute())
                return false;
            if (m_state.lexer.consume_specific(':')) {
                if (!parse_time_zone_utc_offset_second())
                    return false;
                (void)parse_time_zone_utc_offset_fraction();
            }
        } else {
            if (!parse_time_zone_utc_offset_minute())
                return false;
            if (parse_time_zone_utc_offset_second())
                (void)parse_time_zone_utc_offset_fraction();
        }
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour
bool ISO8601Parser::parse_time_zone_numeric_utc_offset_not_ambiguous_allowed_negative_hour()
{
    // TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour :
    //     TimeZoneNumericUTCOffsetNotAmbiguous
    //     - TimeHourNotValidMonth
    StateTransaction transaction { *this };
    if (!parse_time_zone_numeric_utc_offset_not_ambiguous()) {
        if (!m_state.lexer.consume_specific('-'))
            return false;
        if (!parse_time_hour_not_valid_month())
            return false;
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetName
bool ISO8601Parser::parse_time_zone_utc_offset_name()
{
    // TimeZoneUTCOffsetName :
    //     Sign Hour
    //     Sign Hour : MinuteSecond
    //     Sign Hour MinuteSecond
    //     Sign Hour : MinuteSecond : MinuteSecond Fraction[opt]
    //     Sign Hour MinuteSecond MinuteSecond Fraction[opt]
    StateTransaction transaction { *this };
    if (!parse_sign())
        return false;
    if (!parse_hour())
        return false;
    if (m_state.lexer.consume_specific(':')) {
        if (!parse_minute_second())
            return false;
        if (m_state.lexer.consume_specific(':')) {
            if (!parse_minute_second())
                return false;
            (void)parse_fraction();
        }
    } else if (parse_minute_second()) {
        if (parse_minute_second())
            (void)parse_fraction();
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TZLeadingChar
bool ISO8601Parser::parse_tz_leading_char()
{
    // TZLeadingChar :
    //     Alpha
    //     .
    //     _
    if (m_state.lexer.next_is(is_ascii_alpha)) {
        m_state.lexer.consume();
        return true;
    }
    return m_state.lexer.consume_specific('.')
        || m_state.lexer.consume_specific('_');
}

// https://tc39.es/proposal-temporal/#prod-TZChar
bool ISO8601Parser::parse_tz_char()
{
    // TZChar :
    //     Alpha
    //     .
    //     -
    //     _
    if (m_state.lexer.next_is(is_ascii_alpha)) {
        m_state.lexer.consume();
        return true;
    }
    return m_state.lexer.consume_specific('.')
        || m_state.lexer.consume_specific('-')
        || m_state.lexer.consume_specific('_');
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameComponent
bool ISO8601Parser::parse_time_zone_iana_component()
{
    // TimeZoneIANANameComponent :
    //     TZLeadingChar TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] but not one of . or ..
    StateTransaction transaction { *this };
    if (!parse_tz_leading_char())
        return false;
    for (size_t i = 0; i < 13; ++i) {
        if (!parse_tz_char())
            break;
    }
    if (transaction.parsed_string_view().is_one_of("."sv, ".."sv))
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameTail
bool ISO8601Parser::parse_time_zone_iana_name_tail()
{
    // TimeZoneIANANameTail :
    //     TimeZoneIANANameComponent
    //     TimeZoneIANANameComponent / TimeZoneIANANameTail
    StateTransaction transaction { *this };
    if (!parse_time_zone_iana_component())
        return false;
    while (m_state.lexer.next_is('/')) {
        m_state.lexer.consume();
        if (!parse_time_zone_iana_component())
            return false;
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneIANAName
bool ISO8601Parser::parse_time_zone_iana_name()
{
    // TimeZoneIANAName :
    //     Etc/GMT ASCIISign UnpaddedHour
    //     TimeZoneIANANameTail but not Etc/GMT ASCIISign UnpaddedHour
    auto parse_etc_gmt_with_offset = [this] {
        StateTransaction transaction { *this };
        if (!m_state.lexer.consume_specific("Etc/GMT"sv))
            return false;
        if (!parse_ascii_sign())
            return false;
        if (!parse_unpadded_hour())
            return false;
        transaction.commit();
        return true;
    };
    StateTransaction transaction { *this };
    if (parse_etc_gmt_with_offset()) {
        // no-op.
    } else if (!parse_time_zone_iana_name_tail()) {
        return false;
    }
    m_state.parse_result.time_zone_iana_name = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedName
bool ISO8601Parser::parse_time_zone_bracketed_name()
{
    // TimeZoneBracketedName :
    //     TimeZoneIANAName
    //     TimeZoneUTCOffsetName
    StateTransaction transaction { *this };
    if (parse_time_zone_iana_name()) {
        // no-op.
    } else if (!parse_time_zone_utc_offset_name()) {
        return false;
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedAnnotation
bool ISO8601Parser::parse_time_zone_bracketed_annotation()
{
    // TimeZoneBracketedAnnotation :
    //     [ TimeZoneBracketedName ]
    StateTransaction transaction { *this };
    if (!m_state.lexer.consume_specific('['))
        return false;
    if (!parse_time_zone_bracketed_name())
        return false;
    if (!m_state.lexer.consume_specific(']'))
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneOffsetRequired
bool ISO8601Parser::parse_time_zone_offset_required()
{
    // TimeZoneOffsetRequired :
    //     TimeZoneUTCOffset TimeZoneBracketedAnnotation[opt]
    StateTransaction transaction { *this };
    if (!parse_time_zone_utc_offset())
        return false;
    (void)parse_time_zone_bracketed_annotation();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZoneNameRequired
bool ISO8601Parser::parse_time_zone_name_required()
{
    // TimeZoneNameRequired :
    //     TimeZoneUTCOffset[opt] TimeZoneBracketedAnnotation
    StateTransaction transaction { *this };
    (void)parse_time_zone_utc_offset();
    if (!parse_time_zone_bracketed_annotation())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeZone
bool ISO8601Parser::parse_time_zone()
{
    // TimeZone :
    //     TimeZoneUTCOffset TimeZoneBracketedAnnotation[opt]
    //     TimeZoneBracketedAnnotation
    StateTransaction transaction { *this };
    if (parse_time_zone_utc_offset())
        (void)parse_time_zone_bracketed_annotation();
    else if (!parse_time_zone_bracketed_annotation())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-CalendarName
bool ISO8601Parser::parse_calendar_name()
{
    // CalChar :
    //     Alpha
    //     DecimalDigit
    // CalendarNameComponent :
    //     CalChar CalChar CalChar CalChar[opt] CalChar[opt] CalChar[opt] CalChar[opt] CalChar[opt]
    // CalendarNameTail :
    //     CalendarNameComponent
    //     CalendarNameComponent - CalendarNameTail
    // CalendarName :
    //     CalendarNameTail
    auto parse_calendar_name_component = [&] {
        for (size_t i = 0; i < 8; ++i) {
            if (!m_state.lexer.next_is(is_ascii_alphanumeric))
                return i > 2;
            m_state.lexer.consume();
        }
        return true;
    };
    StateTransaction transaction { *this };
    do {
        if (!parse_calendar_name_component())
            return false;
    } while (m_state.lexer.consume_specific('-'));
    m_state.parse_result.calendar_name = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-Calendar
bool ISO8601Parser::parse_calendar()
{
    // Calendar :
    //     [u-ca= CalendarName ]
    StateTransaction transaction { *this };
    if (!m_state.lexer.consume_specific("[u-ca="sv))
        return false;
    if (!parse_calendar_name())
        return false;
    if (!m_state.lexer.consume_specific(']'))
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeSpec
bool ISO8601Parser::parse_time_spec()
{
    // TimeSpec :
    //     TimeHour
    //     TimeHour : TimeMinute
    //     TimeHour TimeMinute
    //     TimeHour : TimeMinute : TimeSecond TimeFraction[opt]
    //     TimeHour TimeMinute TimeSecond TimeFraction[opt]
    StateTransaction transaction { *this };
    if (!parse_time_hour())
        return false;
    if (m_state.lexer.consume_specific(':')) {
        if (!parse_time_minute())
            return false;
        if (m_state.lexer.consume_specific(':')) {
            if (!parse_time_second())
                return false;
            (void)parse_time_fraction();
        }
    } else if (parse_time_minute()) {
        if (parse_time_second())
            (void)parse_time_fraction();
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TimeHourMinuteBasicFormatNotAmbiguous
bool ISO8601Parser::parse_time_hour_minute_basic_format_not_ambiguous()
{
    // TimeHourMinuteBasicFormatNotAmbiguous :
    //     TimeHourNotValidMonth TimeMinute
    //     TimeHour TimeMinuteNotValidDay
    //     TimeHourNotThirtyOneDayMonth TimeMinuteThirtyOneOnly
    //     TimeHourTwoOnly TimeMinuteThirtyOnly
    {
        StateTransaction transaction { *this };
        if (parse_time_hour_not_valid_month() && parse_time_minute()) {
            transaction.commit();
            return true;
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_hour() && parse_time_minute_not_valid_day()) {
            transaction.commit();
            return true;
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_hour_not_thirty_one_day_month() && parse_time_minute_thirty_one_only()) {
            transaction.commit();
            return true;
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_hour_two_only() && parse_time_minute_thirty_only()) {
            transaction.commit();
            return true;
        }
    }
    return false;
}

// https://tc39.es/proposal-temporal/#prod-TimeSpecWithOptionalTimeZoneNotAmbiguous
bool ISO8601Parser::parse_time_spec_with_optional_time_zone_not_ambiguous()
{
    // TimeSpecWithOptionalTimeZoneNotAmbiguous :
    //     TimeHour TimeZoneNumericUTCOffsetNotAmbiguous[opt] TimeZoneBracketedAnnotation[opt]
    //     TimeHourNotValidMonth TimeZone
    //     TimeHour : TimeMinute TimeZone[opt]
    //     TimeHourMinuteBasicFormatNotAmbiguous TimeZoneBracketedAnnotation[opt]
    //     TimeHour TimeMinute TimeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour TimeZoneBracketedAnnotation[opt]
    //     TimeHour : TimeMinute : TimeSecond TimeFraction[opt] TimeZone[opt]
    //     TimeHour TimeMinute TimeSecondNotValidMonth TimeZone[opt]
    //     TimeHour TimeMinute TimeSecond TimeFraction TimeZone[opt]
    {
        StateTransaction transaction { *this };
        if (parse_time_hour()) {
            if (m_state.lexer.consume_specific(':')) {
                if (parse_time_minute()) {
                    if (m_state.lexer.consume_specific(':')) {
                        if (parse_time_second()) {
                            (void)parse_time_fraction();
                            (void)parse_time_zone();
                            transaction.commit();
                            return true;
                        }
                    } else {
                        (void)parse_time_zone();
                        transaction.commit();
                        return true;
                    }
                }
            } else if (parse_time_minute()) {
                if (parse_time_zone_numeric_utc_offset_not_ambiguous_allowed_negative_hour()) {
                    (void)parse_time_zone_bracketed_annotation();
                    transaction.commit();
                    return true;
                }
                {
                    StateTransaction sub_transaction { *this };
                    if (parse_time_second() && parse_time_fraction()) {
                        (void)parse_time_zone();
                        transaction.commit();
                        return true;
                    }
                }
                if (parse_time_second_not_valid_month()) {
                    (void)parse_time_zone();
                    transaction.commit();
                    return true;
                }
            } else {
                (void)parse_time_zone_numeric_utc_offset_not_ambiguous();
                (void)parse_time_zone_bracketed_annotation();
                transaction.commit();
                return true;
            }
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_hour_not_valid_month() && parse_time_zone()) {
            transaction.commit();
            return true;
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_hour_minute_basic_format_not_ambiguous()) {
            (void)parse_time_zone_bracketed_annotation();
            transaction.commit();
            return true;
        }
    }
    return false;
}

// https://tc39.es/proposal-temporal/#prod-TimeSpecSeparator
bool ISO8601Parser::parse_time_spec_separator()
{
    // TimeSpecSeparator :
    //     DateTimeSeparator TimeSpec
    StateTransaction transaction { *this };
    if (!parse_date_time_separator())
        return false;
    if (!parse_time_spec())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DateTime
bool ISO8601Parser::parse_date_time()
{
    // DateTime :
    //     Date TimeSpecSeparator[opt] TimeZone[opt]
    if (!parse_date())
        return false;
    (void)parse_time_spec_separator();
    (void)parse_time_zone();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-CalendarTime
bool ISO8601Parser::parse_calendar_time()
{
    // CalendarTime :
    //     TimeDesignator TimeSpec TimeZone[opt] Calendar[opt]
    //     TimeSpec TimeZone[opt] Calendar
    //     TimeSpecWithOptionalTimeZoneNotAmbiguous
    {
        StateTransaction transaction { *this };
        if (parse_time_designator() && parse_time_spec()) {
            (void)parse_time_zone();
            (void)parse_calendar();
            transaction.commit();
            return true;
        }
    }
    {
        StateTransaction transaction { *this };
        if (parse_time_spec()) {
            (void)parse_time_zone();
            if (parse_calendar()) {
                transaction.commit();
                return true;
            }
        }
    }
    return parse_time_spec_with_optional_time_zone_not_ambiguous();
}

// https://tc39.es/proposal-temporal/#prod-CalendarDateTime
bool ISO8601Parser::parse_calendar_date_time()
{
    // CalendarDateTime :
    //     DateTime Calendar[opt]
    if (!parse_date_time())
        return false;
    (void)parse_calendar();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-CalendarDateTimeTimeRequired
bool ISO8601Parser::parse_calendar_date_time_time_required()
{
    // CalendarDateTimeTimeRequired :
    //     Date TimeSpecSeparator TimeZone[opt] Calendar[opt]
    StateTransaction transaction { *this };
    if (!parse_date())
        return false;
    if (!parse_time_spec_separator())
        return false;
    (void)parse_time_zone();
    (void)parse_calendar();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationWholeSeconds
bool ISO8601Parser::parse_duration_whole_seconds()
{
    // DurationWholeSeconds :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_whole_seconds = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationSecondsFraction
bool ISO8601Parser::parse_duration_seconds_fraction()
{
    // DurationSecondsFraction :
    //     TimeFraction
    StateTransaction transaction { *this };
    if (!parse_time_fraction())
        return false;
    m_state.parse_result.duration_seconds_fraction = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationSecondsPart
bool ISO8601Parser::parse_duration_seconds_part()
{
    // DurationSecondsPart :
    //     DurationWholeSeconds DurationSecondsFraction[opt] SecondsDesignator
    StateTransaction transaction { *this };
    if (!parse_duration_whole_seconds())
        return false;
    (void)parse_duration_seconds_fraction();
    if (!parse_seconds_designator())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationWholeMinutes
bool ISO8601Parser::parse_duration_whole_minutes()
{
    // DurationWholeMinutes :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_whole_minutes = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationMinutesFraction
bool ISO8601Parser::parse_duration_minutes_fraction()
{
    // DurationMinutesFraction :
    //     TimeFraction
    StateTransaction transaction { *this };
    if (!parse_time_fraction())
        return false;
    m_state.parse_result.duration_minutes_fraction = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationMinutesPart
bool ISO8601Parser::parse_duration_minutes_part()
{
    // DurationMinutesPart :
    //     DurationWholeMinutes DurationMinutesFraction[opt] MinutesDesignator DurationSecondsPart[opt]
    StateTransaction transaction { *this };
    if (!parse_duration_whole_minutes())
        return false;
    (void)parse_duration_minutes_fraction();
    if (!parse_minutes_designator())
        return false;
    (void)parse_duration_seconds_part();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationWholeHours
bool ISO8601Parser::parse_duration_whole_hours()
{
    // DurationWholeHours :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_whole_hours = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationHoursFraction
bool ISO8601Parser::parse_duration_hours_fraction()
{
    // DurationHoursFraction :
    //     TimeFraction
    StateTransaction transaction { *this };
    if (!parse_time_fraction())
        return false;
    m_state.parse_result.duration_hours_fraction = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationHoursPart
bool ISO8601Parser::parse_duration_hours_part()
{
    // DurationHoursPart :
    //     DurationWholeHours DurationHoursFraction[opt] HoursDesignator DurationMinutesPart
    //     DurationWholeHours DurationHoursFraction[opt] HoursDesignator DurationSecondsPart[opt]
    StateTransaction transaction { *this };
    if (!parse_duration_whole_hours())
        return false;
    (void)parse_duration_hours_fraction();
    if (!parse_hours_designator())
        return false;
    (void)(parse_duration_minutes_part()
        || parse_duration_seconds_part());
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationTime
bool ISO8601Parser::parse_duration_time()
{
    // DurationTime :
    //     TimeDesignator DurationHoursPart
    //     TimeDesignator DurationMinutesPart
    //     TimeDesignator DurationSecondsPart
    StateTransaction transaction { *this };
    if (!parse_time_designator())
        return false;
    auto success = parse_duration_hours_part()
        || parse_duration_minutes_part()
        || parse_duration_seconds_part();
    if (!success)
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationDays
bool ISO8601Parser::parse_duration_days()
{
    // DurationDays :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_days = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationDaysPart
bool ISO8601Parser::parse_duration_days_part()
{
    // DurationDaysPart :
    //     DurationDays DaysDesignator
    StateTransaction transaction { *this };
    if (!parse_duration_days())
        return false;
    if (!parse_days_designator())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationWeeks
bool ISO8601Parser::parse_duration_weeks()
{
    // DurationWeeks :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_weeks = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationWeeksPart
bool ISO8601Parser::parse_duration_weeks_part()
{
    // DurationWeeksPart :
    //     DurationWeeks WeeksDesignator DurationDaysPart[opt]
    StateTransaction transaction { *this };
    if (!parse_duration_weeks())
        return false;
    if (!parse_weeks_designator())
        return false;
    (void)parse_duration_days_part();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationMonths
bool ISO8601Parser::parse_duration_months()
{
    // DurationMonths :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_months = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationMonthsPart
bool ISO8601Parser::parse_duration_months_part()
{
    // DurationMonthsPart :
    //     DurationMonths MonthsDesignator DurationWeeksPart
    //     DurationMonths MonthsDesignator DurationDaysPart[opt]
    StateTransaction transaction { *this };
    if (!parse_duration_months())
        return false;
    if (!parse_months_designator())
        return false;
    (void)(parse_duration_weeks_part()
        || parse_duration_days_part());
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationYears
bool ISO8601Parser::parse_duration_years()
{
    // DurationYears :
    //     DecimalDigits[~Sep]
    StateTransaction transaction { *this };
    if (!parse_decimal_digits())
        return false;
    m_state.parse_result.duration_years = transaction.parsed_string_view();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationYearsPart
bool ISO8601Parser::parse_duration_years_part()
{
    // DurationYearsPart :
    //     DurationYears YearsDesignator DurationMonthsPart
    //     DurationYears YearsDesignator DurationWeeksPart
    //     DurationYears YearsDesignator DurationDaysPart[opt]
    StateTransaction transaction { *this };
    if (!parse_duration_years())
        return false;
    if (!parse_years_designator())
        return false;
    (void)(parse_duration_months_part()
        || parse_duration_weeks_part()
        || parse_duration_days_part());
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-DurationDate
bool ISO8601Parser::parse_duration_date()
{
    // DurationDate :
    //     DurationYearsPart DurationTime[opt]
    //     DurationMonthsPart DurationTime[opt]
    //     DurationWeeksPart DurationTime[opt]
    //     DurationDaysPart DurationTime[opt]
    auto success = parse_duration_years_part()
        || parse_duration_months_part()
        || parse_duration_weeks_part()
        || parse_duration_days_part();
    if (!success)
        return false;
    (void)parse_duration_time();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-Duration
bool ISO8601Parser::parse_duration()
{
    // Duration :
    //     Sign[opt] DurationDesignator DurationDate
    //     Sign[opt] DurationDesignator DurationTime
    StateTransaction transaction { *this };
    (void)parse_sign();
    if (!parse_duration_designator())
        return false;
    auto success = parse_duration_date()
        || parse_duration_time();
    if (!success)
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TemporalInstantString
bool ISO8601Parser::parse_temporal_instant_string()
{
    // TemporalInstantString :
    //     Date TimeSpecSeparator[opt] TimeZoneOffsetRequired
    StateTransaction transaction { *this };
    if (!parse_date())
        return false;
    (void)parse_time_spec_separator();
    if (!parse_time_zone_offset_required())
        return false;
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TemporalDateString
bool ISO8601Parser::parse_temporal_date_string()
{
    // TemporalDateString :
    //     CalendarDateTime
    return parse_calendar_date_time();
}

// https://tc39.es/proposal-temporal/#prod-TemporalDateTimeString
bool ISO8601Parser::parse_temporal_date_time_string()
{
    // TemporalDateTimeString :
    //     CalendarDateTime
    return parse_calendar_date_time();
}

// https://tc39.es/proposal-temporal/#prod-TemporalDurationString
bool ISO8601Parser::parse_temporal_duration_string()
{
    // TemporalDurationString :
    //     Duration
    return parse_duration();
}

// https://tc39.es/proposal-temporal/#prod-TemporalMonthDayString
bool ISO8601Parser::parse_temporal_month_day_string()
{
    // TemporalMonthDayString :
    //     DateSpecMonthDay
    //     CalendarDateTime
    // NOTE: Reverse order here because `DateSpecMonthDay` can be a subset of `CalendarDateTime`,
    // so we'd not attempt to parse that but may not exhaust the input string.
    return parse_calendar_date_time()
        || parse_date_spec_month_day();
}

// https://tc39.es/proposal-temporal/#prod-TemporalTimeString
bool ISO8601Parser::parse_temporal_time_string()
{
    // TemporalTimeString :
    //     CalendarTime
    //     CalendarDateTimeTimeRequired
    // NOTE: Reverse order here because `Time` can be a subset of `CalendarDateTimeTimeRequired`,
    // so we'd not attempt to parse that but may not exhaust the input string.
    return parse_calendar_date_time_time_required()
        || parse_calendar_time();
}

// https://tc39.es/proposal-temporal/#prod-TemporalTimeZoneIdentifier
bool ISO8601Parser::parse_temporal_time_zone_identifier()
{
    // TemporalTimeZoneIdentifier :
    //     TimeZoneNumericUTCOffset
    //     TimeZoneIANAName
    return parse_time_zone_numeric_utc_offset()
        || parse_time_zone_iana_name();
}

// https://tc39.es/proposal-temporal/#prod-TemporalTimeZoneString
bool ISO8601Parser::parse_temporal_time_zone_string()
{
    // TemporalTimeZoneString :
    //     TemporalTimeZoneIdentifier
    //     Date TimeSpecSeparator[opt] TimeZone Calendar[opt]
    StateTransaction transaction { *this };
    if (!parse_temporal_time_zone_identifier()) {
        if (!parse_date())
            return false;
        (void)parse_time_spec_separator();
        if (!parse_time_zone())
            return false;
        (void)parse_calendar();
    }
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TemporalYearMonthString
bool ISO8601Parser::parse_temporal_year_month_string()
{
    // TemporalYearMonthString :
    //     DateSpecYearMonth
    //     CalendarDateTime
    // NOTE: Reverse order here because `DateSpecYearMonth` can be a subset of `CalendarDateTime`,
    // so we'd not attempt to parse that but may not exhaust the input string.
    return parse_calendar_date_time()
        || parse_date_spec_year_month();
}

// https://tc39.es/proposal-temporal/#prod-TemporalZonedDateTimeString
bool ISO8601Parser::parse_temporal_zoned_date_time_string()
{
    // TemporalZonedDateTimeString :
    //     Date TimeSpecSeparator[opt] TimeZoneNameRequired Calendar[opt]
    StateTransaction transaction { *this };
    if (!parse_date())
        return false;
    (void)parse_time_spec_separator();
    if (!parse_time_zone_name_required())
        return false;
    (void)parse_calendar();
    transaction.commit();
    return true;
}

// https://tc39.es/proposal-temporal/#prod-TemporalCalendarString
bool ISO8601Parser::parse_temporal_calendar_string()
{
    // TemporalCalendarString :
    //     CalendarName
    //     TemporalInstantString
    //     CalendarDateTime
    //     CalendarTime
    //     DateSpecYearMonth
    //     DateSpecMonthDay
    return parse_calendar_name()
        || parse_temporal_instant_string()
        || parse_calendar_date_time()
        || parse_date_spec_year_month()
        || parse_date_spec_month_day()
        || parse_calendar_time();
}

// https://tc39.es/proposal-temporal/#prod-TemporalRelativeToString
bool ISO8601Parser::parse_temporal_relative_to_string()
{
    // TemporalRelativeToString :
    //     TemporalDateTimeString
    return parse_temporal_date_time_string();
}

}

#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS                                        \
    __JS_ENUMERATE(TemporalInstantString, parse_temporal_instant_string)               \
    __JS_ENUMERATE(TemporalDateString, parse_temporal_date_string)                     \
    __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string)            \
    __JS_ENUMERATE(TemporalDurationString, parse_temporal_duration_string)             \
    __JS_ENUMERATE(TemporalMonthDayString, parse_temporal_month_day_string)            \
    __JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string)                     \
    __JS_ENUMERATE(TemporalTimeZoneString, parse_temporal_time_zone_string)            \
    __JS_ENUMERATE(TemporalYearMonthString, parse_temporal_year_month_string)          \
    __JS_ENUMERATE(TemporalZonedDateTimeString, parse_temporal_zoned_date_time_string) \
    __JS_ENUMERATE(TemporalCalendarString, parse_temporal_calendar_string)             \
    __JS_ENUMERATE(TemporalRelativeToString, parse_temporal_relative_to_string)

Optional<ParseResult> parse_iso8601(Production production, StringView input)
{
    auto parser = Detail::ISO8601Parser { input };

    switch (production) {
#define __JS_ENUMERATE(ProductionName, parse_production) \
    case Production::ProductionName:                     \
        if (!parser.parse_production())                  \
            return {};                                   \
        break;
        JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS
#undef __JS_ENUMERATE
    default:
        VERIFY_NOT_REACHED();
    }

    // If we parsed successfully but didn't reach the end, the string doesn't match the given production.
    if (!parser.lexer().is_eof())
        return {};

    return parser.parse_result();
}

}