123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341 |
- /*
- * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
- * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/CharacterTypes.h>
- #include <AK/GenericLexer.h>
- #include <LibJS/Runtime/Temporal/DateEquations.h>
- #include <LibJS/Runtime/Temporal/ISO8601.h>
- #include <LibJS/Runtime/Value.h>
- namespace JS::Temporal {
- enum class Extended {
- No,
- Yes,
- };
- enum class Separator {
- No,
- Yes,
- };
- enum class TimeRequired {
- No,
- Yes,
- };
- enum class ZDesignator {
- No,
- Yes,
- };
- enum class Zoned {
- No,
- Yes,
- };
- // 13.30.1 Static Semantics: IsValidMonthDay, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar-static-semantics-isvalidmonthday
- static bool is_valid_month_day(ParseResult const& result)
- {
- // 1. If DateDay is "31" and DateMonth is "02", "04", "06", "09", "11", return false.
- if (result.date_day == "31"sv && result.date_month->is_one_of("02"sv, "04"sv, "06"sv, "09"sv, "11"sv))
- return false;
- // 2. If DateMonth is "02" and DateDay is "30", return false.
- if (result.date_month == "02"sv && result.date_day == "30"sv)
- return false;
- // 3. Return true.
- return true;
- }
- // 13.30.2 Static Semantics: IsValidDate, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar-static-semantics-isvaliddate
- static bool is_valid_date(ParseResult const& result)
- {
- // 1. If IsValidMonthDay of DateSpec is false, return false.
- if (!is_valid_month_day(result))
- return false;
- // 2. Let year be ℝ(StringToNumber(CodePointsToString(DateYear))).
- auto year = string_to_number(*result.date_year);
- // 3. If DateMonth is "02" and DateDay is "29" and MathematicalInLeapYear(EpochTimeForYear(year)) = 0, return false.
- if (result.date_month == "02"sv && result.date_day == "29"sv && mathematical_in_leap_year(epoch_time_for_year(year)) == 0)
- return false;
- // 4. Return true.
- return true;
- }
- // 13.30 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
- class ISO8601Parser {
- public:
- explicit ISO8601Parser(StringView input)
- : m_input(input)
- , m_state({
- .lexer = GenericLexer { input },
- .parse_result = {},
- })
- {
- }
- [[nodiscard]] GenericLexer const& lexer() const { return m_state.lexer; }
- [[nodiscard]] ParseResult const& parse_result() const { return m_state.parse_result; }
- // https://tc39.es/proposal-temporal/#prod-TemporalDateTimeString
- [[nodiscard]] bool parse_temporal_date_time_string()
- {
- // TemporalDateTimeString[Zoned] :::
- // AnnotatedDateTime[?Zoned, ~TimeRequired]
- return parse_annotated_date_time(Zoned::No, TimeRequired::No);
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalDateTimeString
- [[nodiscard]] bool parse_temporal_zoned_date_time_string()
- {
- // TemporalDateTimeString[Zoned] :::
- // AnnotatedDateTime[?Zoned, ~TimeRequired]
- return parse_annotated_date_time(Zoned::Yes, TimeRequired::No);
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalDurationString
- [[nodiscard]] bool parse_temporal_duration_string()
- {
- // TemporalDurationString :::
- // Duration
- return parse_duration();
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalInstantString
- [[nodiscard]] bool parse_temporal_instant_string()
- {
- // TemporalInstantString :::
- // Date DateTimeSeparator Time DateTimeUTCOffset[+Z] TimeZoneAnnotation[opt] Annotations[opt]
- if (!parse_date())
- return false;
- if (!parse_date_time_separator())
- return false;
- if (!parse_time())
- return false;
- if (!parse_date_time_utc_offset(ZDesignator::Yes))
- return false;
- (void)parse_time_zone_annotation();
- (void)parse_annotations();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalMonthDayString
- [[nodiscard]] bool parse_temporal_month_day_string()
- {
- // TemporalMonthDayString :::
- // AnnotatedMonthDay
- // AnnotatedDateTime[~Zoned, ~TimeRequired]
- // NOTE: Reverse order here because `AnnotatedMonthDay` can be a subset of `AnnotatedDateTime`.
- return parse_annotated_date_time(Zoned::No, TimeRequired::No) || parse_annotated_month_day();
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalTimeString
- [[nodiscard]] bool parse_temporal_time_string()
- {
- // TemporalTimeString :::
- // AnnotatedTime
- // AnnotatedDateTime[~Zoned, +TimeRequired]
- // NOTE: Reverse order here because `AnnotatedTime` can be a subset of `AnnotatedDateTime`.
- return parse_annotated_date_time(Zoned::No, TimeRequired::Yes) || parse_annotated_time();
- }
- // https://tc39.es/proposal-temporal/#prod-TemporalYearMonthString
- [[nodiscard]] bool parse_temporal_year_month_string()
- {
- // TemporalYearMonthString :::
- // AnnotatedYearMonth
- // AnnotatedDateTime[~Zoned, ~TimeRequired]
- // NOTE: Reverse order here because `AnnotatedYearMonth` can be a subset of `AnnotatedDateTime`.
- return parse_annotated_date_time(Zoned::No, TimeRequired::No) || parse_annotated_year_month();
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotatedDateTime
- [[nodiscard]] bool parse_annotated_date_time(Zoned zoned, TimeRequired time_required)
- {
- // AnnotatedDateTime[Zoned, TimeRequired] :::
- // [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation[opt] Annotations[opt]
- // [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations[opt]
- if (!parse_date_time(zoned == Zoned::Yes ? ZDesignator::Yes : ZDesignator::No, time_required))
- return false;
- if (!parse_time_zone_annotation()) {
- if (zoned == Zoned::Yes)
- return false;
- }
- (void)parse_annotations();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotatedMonthDay
- [[nodiscard]] bool parse_annotated_month_day()
- {
- // AnnotatedMonthDay :::
- // DateSpecMonthDay TimeZoneAnnotation[opt] Annotations[opt]
- if (!parse_date_spec_month_day())
- return false;
- (void)parse_time_zone_annotation();
- (void)parse_annotations();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotatedTime
- [[nodiscard]] bool parse_annotated_time()
- {
- // AnnotatedTime :::
- // TimeDesignator Time DateTimeUTCOffset[~Z][opt] TimeZoneAnnotation[opt] Annotations[opt]
- // Time DateTimeUTCOffset[~Z][opt] TimeZoneAnnotation[opt] Annotations[opt]
- (void)parse_time_designator();
- if (!parse_time())
- return false;
- (void)parse_date_time_utc_offset(ZDesignator::No);
- (void)parse_time_zone_annotation();
- (void)parse_annotations();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotatedYearMonth
- [[nodiscard]] bool parse_annotated_year_month()
- {
- // AnnotatedYearMonth :::
- // DateSpecYearMonth TimeZoneAnnotation[opt] Annotations[opt]
- if (!parse_date_spec_year_month())
- return false;
- (void)parse_time_zone_annotation();
- (void)parse_annotations();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DateTime
- [[nodiscard]] bool parse_date_time(ZDesignator z_designator, TimeRequired time_required)
- {
- StateTransaction transaction { *this };
- // DateTime[Z, TimeRequired] :::
- // [~TimeRequired] Date
- // Date DateTimeSeparator Time DateTimeUTCOffset[?Z][opt]
- if (!parse_date())
- return false;
- if (parse_date_time_separator()) {
- if (!parse_time())
- return false;
- (void)parse_date_time_utc_offset(z_designator);
- } else if (time_required == TimeRequired::Yes) {
- return false;
- }
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-Date
- [[nodiscard]] bool parse_date()
- {
- // Date :::
- // DateSpec[+Extended]
- // DateSpec[~Extended]
- return parse_date_spec(Extended::Yes) || parse_date_spec(Extended::No);
- }
- // https://tc39.es/proposal-temporal/#prod-DateSpec
- [[nodiscard]] bool parse_date_spec(Extended extended)
- {
- StateTransaction transaction { *this };
- // DateSpec[Extended] :::
- // DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay
- if (!parse_date_year())
- return false;
- if (!parse_date_separator(extended))
- return false;
- if (!parse_date_month())
- return false;
- if (!parse_date_separator(extended))
- return false;
- if (!parse_date_day())
- return false;
- // It is a Syntax Error if IsValidDate of DateSpec is false.
- if (!is_valid_date(m_state.parse_result))
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DateSpecMonthDay
- [[nodiscard]] bool parse_date_spec_month_day()
- {
- StateTransaction transaction { *this };
- // DateSpecMonthDay :::
- // --[opt] DateMonth DateSeparator[+Extended] DateDay
- // --[opt] DateMonth DateSeparator[~Extended] DateDay
- (void)m_state.lexer.consume_specific("--"sv);
- if (!parse_date_month())
- return false;
- if (!parse_date_separator(Extended::Yes) && !parse_date_separator(Extended::No))
- return false;
- if (!parse_date_day())
- return false;
- // It is a Syntax Error if IsValidMonthDay of DateSpecMonthDay is false.
- if (!is_valid_month_day(m_state.parse_result))
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DateSpecYearMonth
- [[nodiscard]] bool parse_date_spec_year_month()
- {
- StateTransaction transaction { *this };
- // DateSpecYearMonth :::
- // DateYear DateSeparator[+Extended] DateMonth
- // DateYear DateSeparator[~Extended] DateMonth
- if (!parse_date_year())
- return false;
- if (!parse_date_separator(Extended::Yes) && !parse_date_separator(Extended::No))
- return false;
- if (!parse_date_month())
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DateYear
- [[nodiscard]] bool parse_date_year()
- {
- StateTransaction transaction { *this };
- // DateYear :::
- // DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // ASCIISign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- size_t digit_count = parse_ascii_sign() ? 6 : 4;
- for (size_t i = 0; i < digit_count; ++i) {
- if (!parse_decimal_digit())
- return false;
- }
- // It is a Syntax Error if DateYear is "-000000".
- if (transaction.parsed_string_view() == "-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
- [[nodiscard]] bool parse_date_month()
- {
- StateTransaction transaction { *this };
- // DateMonth :::
- // 0 NonZeroDigit
- // 10
- // 11
- // 12
- 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
- [[nodiscard]] bool parse_date_day()
- {
- StateTransaction transaction { *this };
- // DateDay :::
- // 0 NonZeroDigit
- // 1 DecimalDigit
- // 2 DecimalDigit
- // 30
- // 31
- 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-DateTimeUTCOffset
- [[nodiscard]] bool parse_date_time_utc_offset(ZDesignator z_designator)
- {
- // DateTimeUTCOffset[Z] :::
- // [+Z] UTCDesignator
- // UTCOffset[+SubMinutePrecision]
- if (z_designator == ZDesignator::Yes) {
- if (parse_utc_designator())
- return true;
- }
- return parse_utc_offset(SubMinutePrecision::Yes, m_state.parse_result.date_time_offset);
- }
- // https://tc39.es/proposal-temporal/#prod-Time
- [[nodiscard]] bool parse_time()
- {
- // Time :::
- // TimeSpec[+Extended]
- // TimeSpec[~Extended]
- return parse_time_spec();
- }
- // https://tc39.es/proposal-temporal/#prod-TimeSpec
- [[nodiscard]] bool parse_time_spec()
- {
- StateTransaction transaction { *this };
- auto parse_time_hour = [&]() {
- return scoped_parse(m_state.parse_result.time_hour, [&]() { return parse_hour(); });
- };
- auto parse_time_minute = [&]() {
- return scoped_parse(m_state.parse_result.time_minute, [&]() { return parse_minute_second(); });
- };
- auto parse_time_fraction = [&]() {
- return scoped_parse(m_state.parse_result.time_fraction, [&]() { return parse_temporal_decimal_fraction(); });
- };
- // TimeSpec[Extended] :::
- // Hour
- // Hour TimeSeparator[?Extended] MinuteSecond
- // Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] TimeSecond TemporalDecimalFraction[opt]
- if (!parse_time_hour())
- return false;
- if (parse_time_separator(Extended::Yes)) {
- if (!parse_time_minute())
- return false;
- if (parse_time_separator(Extended::Yes)) {
- if (!parse_time_second())
- return false;
- (void)parse_time_fraction();
- }
- } else if (parse_time_minute() && parse_time_second()) {
- (void)parse_time_fraction();
- }
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TimeSecond
- [[nodiscard]] bool parse_time_second()
- {
- StateTransaction transaction { *this };
- // TimeSecond :::
- // MinuteSecond
- // 60
- 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-TimeZoneAnnotation
- [[nodiscard]] bool parse_time_zone_annotation()
- {
- StateTransaction transaction { *this };
- // TimeZoneAnnotation :::
- // [ AnnotationCriticalFlag[opt] TimeZoneIdentifier ]
- if (!m_state.lexer.consume_specific('['))
- return false;
- (void)parse_annotation_critical_flag();
- if (!parse_time_zone_identifier())
- return false;
- if (!m_state.lexer.consume_specific(']'))
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TimeZoneIdentifier
- [[nodiscard]] bool parse_time_zone_identifier()
- {
- StateTransaction transaction { *this };
- // TimeZoneIdentifier :::
- // UTCOffset[~SubMinutePrecision]
- // TimeZoneIANAName
- auto success = parse_utc_offset(SubMinutePrecision::No, m_state.parse_result.time_zone_offset) || parse_time_zone_iana_name();
- if (!success)
- return false;
- m_state.parse_result.time_zone_identifier = transaction.parsed_string_view();
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TimeZoneIANAName
- [[nodiscard]] bool parse_time_zone_iana_name()
- {
- StateTransaction transaction { *this };
- // TimeZoneIANAName :::
- // TimeZoneIANANameComponent
- // TimeZoneIANAName / TimeZoneIANANameComponent
- if (!parse_time_zone_iana_name_component())
- return false;
- while (m_state.lexer.consume_specific('/')) {
- if (!parse_time_zone_iana_name_component())
- return false;
- }
- m_state.parse_result.time_zone_iana_name = transaction.parsed_string_view();
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TimeZoneIANANameComponent
- [[nodiscard]] bool parse_time_zone_iana_name_component()
- {
- // TimeZoneIANANameComponent :::
- // TZLeadingChar
- // TimeZoneIANANameComponent TZChar
- if (!parse_tz_leading_char())
- return false;
- while (parse_tz_leading_char())
- ;
- while (parse_tz_char())
- ;
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TZLeadingChar
- [[nodiscard]] bool parse_tz_leading_char()
- {
- // TZLeadingChar :::
- // Alpha
- // .
- // _
- return parse_alpha() || m_state.lexer.consume_specific('.') || m_state.lexer.consume_specific('_');
- }
- // https://tc39.es/proposal-temporal/#prod-TZChar
- [[nodiscard]] bool parse_tz_char()
- {
- // TZChar :::
- // TZLeadingChar
- // DecimalDigit
- // -
- // +
- return parse_tz_leading_char() || parse_decimal_digit() || m_state.lexer.consume_specific('.') || m_state.lexer.consume_specific('+');
- }
- // https://tc39.es/proposal-temporal/#prod-Annotations
- [[nodiscard]] bool parse_annotations()
- {
- // Annotations :::
- // Annotation Annotationsopt
- if (!parse_annotation())
- return false;
- while (parse_annotation())
- ;
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-Annotation
- [[nodiscard]] bool parse_annotation()
- {
- StateTransaction transaction { *this };
- Optional<StringView> key;
- Optional<StringView> value;
- // Annotation :::
- // [ AnnotationCriticalFlag[opt] AnnotationKey = AnnotationValue ]
- if (!m_state.lexer.consume_specific('['))
- return false;
- auto critical = parse_annotation_critical_flag();
- if (!scoped_parse(key, [&]() { return parse_annotation_key(); }))
- return false;
- if (!m_state.lexer.consume_specific('='))
- return false;
- if (!scoped_parse(value, [&]() { return parse_annotation_value(); }))
- return false;
- if (!m_state.lexer.consume_specific(']'))
- return false;
- m_state.parse_result.annotations.empend(critical, *key, *value);
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotationKey
- [[nodiscard]] bool parse_annotation_key()
- {
- // AnnotationKey :::
- // AKeyLeadingChar
- // AnnotationKey AKeyChar
- if (!parse_annotation_key_leading_char())
- return false;
- while (parse_annotation_key_leading_char())
- ;
- while (parse_annotation_key_char())
- ;
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AKeyLeadingChar
- [[nodiscard]] bool parse_annotation_key_leading_char()
- {
- // AKeyLeadingChar :::
- // LowercaseAlpha
- // _
- return parse_lowercase_alpha() || m_state.lexer.consume_specific('_');
- }
- // https://tc39.es/proposal-temporal/#prod-AKeyChar
- [[nodiscard]] bool parse_annotation_key_char()
- {
- // AKeyChar :::
- // AKeyLeadingChar
- // DecimalDigit
- // -
- return parse_annotation_key_leading_char() || parse_decimal_digit() || m_state.lexer.consume_specific('-');
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotationValue
- [[nodiscard]] bool parse_annotation_value()
- {
- // AnnotationValue :::
- // AnnotationValueComponent
- // AnnotationValueComponent - AnnotationValue
- if (!parse_annotation_value_component())
- return false;
- while (m_state.lexer.consume_specific('-')) {
- if (!parse_annotation_value_component())
- return false;
- }
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-AnnotationValueComponent
- [[nodiscard]] bool parse_annotation_value_component()
- {
- // AnnotationValueComponent :::
- // Alpha AnnotationValueComponent[opt]
- // DecimalDigit AnnotationValueComponent[opt]
- auto parse_component = [&]() { return parse_alpha() || parse_decimal_digit(); };
- if (!parse_component())
- return false;
- while (parse_component())
- ;
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-UTCOffset
- [[nodiscard]] bool parse_utc_offset(SubMinutePrecision sub_minute_precision, Optional<TimeZoneOffset>& result)
- {
- StateTransaction transaction { *this };
- TimeZoneOffset time_zone_offset;
- auto parse_utc_sign = [&]() {
- return scoped_parse(time_zone_offset.sign, [&]() { return parse_ascii_sign(); });
- };
- auto parse_utc_hours = [&]() {
- return scoped_parse(time_zone_offset.hours, [&]() { return parse_hour(); });
- };
- auto parse_utc_minutes = [&]() {
- return scoped_parse(time_zone_offset.minutes, [&]() { return parse_minute_second(); });
- };
- auto parse_utc_seconds = [&]() {
- return scoped_parse(time_zone_offset.seconds, [&]() { return parse_minute_second(); });
- };
- auto parse_utc_fraction = [&]() {
- return scoped_parse(time_zone_offset.fraction, [&]() { return parse_temporal_decimal_fraction(); });
- };
- // UTCOffset[SubMinutePrecision] :::
- // ASCIISign Hour
- // ASCIISign Hour TimeSeparator[+Extended] MinuteSecond
- // ASCIISign Hour TimeSeparator[~Extended] MinuteSecond
- // [+SubMinutePrecision] ASCIISign Hour TimeSeparator[+Extended] MinuteSecond TimeSeparator[+Extended] MinuteSecond TemporalDecimalFraction[opt]
- // [+SubMinutePrecision] ASCIISign Hour TimeSeparator[~Extended] MinuteSecond TimeSeparator[~Extended] MinuteSecond TemporalDecimalFraction[opt]
- if (!parse_utc_sign())
- return false;
- if (!parse_utc_hours())
- return false;
- if (parse_time_separator(Extended::Yes)) {
- if (!parse_utc_minutes())
- return false;
- if (sub_minute_precision == SubMinutePrecision::Yes && parse_time_separator(Extended::Yes)) {
- if (!parse_utc_seconds())
- return false;
- (void)parse_utc_fraction();
- }
- } else if (parse_utc_minutes()) {
- if (sub_minute_precision == SubMinutePrecision::Yes && parse_utc_seconds())
- (void)parse_utc_fraction();
- }
- time_zone_offset.source_text = transaction.parsed_string_view();
- result = move(time_zone_offset);
- transaction.commit();
- return true;
- }
- // https://tc39.es/ecma262/#prod-Hour
- [[nodiscard]] bool parse_hour()
- {
- // Hour :::
- // 0 DecimalDigit
- // 1 DecimalDigit
- // 20
- // 21
- // 22
- // 23
- 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;
- }
- return true;
- }
- // https://tc39.es/ecma262/#prod-MinuteSecond
- [[nodiscard]] bool parse_minute_second()
- {
- // MinuteSecond :::
- // 0 DecimalDigit
- // 1 DecimalDigit
- // 2 DecimalDigit
- // 3 DecimalDigit
- // 4 DecimalDigit
- // 5 DecimalDigit
- 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;
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DurationDate
- [[nodiscard]] bool 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
- [[nodiscard]] bool parse_duration()
- {
- StateTransaction transaction { *this };
- // Duration :::
- // ASCIISign[opt] DurationDesignator DurationDate
- // ASCIISign[opt] DurationDesignator DurationTime
- (void)scoped_parse(m_state.parse_result.sign, [&]() { return parse_ascii_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-DurationYearsPart
- [[nodiscard]] bool parse_duration_years_part()
- {
- StateTransaction transaction { *this };
- // DurationYearsPart :::
- // DecimalDigits[~Sep] YearsDesignator DurationMonthsPart
- // DecimalDigits[~Sep] YearsDesignator DurationWeeksPart
- // DecimalDigits[~Sep] YearsDesignator DurationDaysPart[opt]
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.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-DurationMonthsPart
- [[nodiscard]] bool parse_duration_months_part()
- {
- StateTransaction transaction { *this };
- // DurationMonthsPart :::
- // DecimalDigits[~Sep] MonthsDesignator DurationWeeksPart
- // DecimalDigits[~Sep] MonthsDesignator DurationDaysPart[opt]
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.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-DurationWeeksPart
- [[nodiscard]] bool parse_duration_weeks_part()
- {
- StateTransaction transaction { *this };
- // DurationWeeksPart :::
- // DecimalDigits[~Sep] WeeksDesignator DurationDaysPart[opt]
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.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-DurationDaysPart
- [[nodiscard]] bool parse_duration_days_part()
- {
- StateTransaction transaction { *this };
- // DurationDaysPart :::
- // DecimalDigits[~Sep] DaysDesignator
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_days))
- return false;
- if (!parse_days_designator())
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DurationTime
- [[nodiscard]] bool parse_duration_time()
- {
- StateTransaction transaction { *this };
- // DurationTime :::
- // TimeDesignator DurationHoursPart
- // TimeDesignator DurationMinutesPart
- // TimeDesignator DurationSecondsPart
- 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-DurationHoursPart
- [[nodiscard]] bool parse_duration_hours_part()
- {
- StateTransaction transaction { *this };
- // DurationHoursPart :::
- // DecimalDigits[~Sep] TemporalDecimalFraction HoursDesignator
- // DecimalDigits[~Sep] HoursDesignator DurationMinutesPart
- // DecimalDigits[~Sep] HoursDesignator DurationSecondsPart[opt]
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_hours))
- return false;
- auto is_fractional = scoped_parse(m_state.parse_result.duration_hours_fraction, [&]() { return parse_temporal_decimal_fraction(); });
- if (!parse_hours_designator())
- return false;
- if (!is_fractional)
- (void)(parse_duration_minutes_part() || parse_duration_seconds_part());
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DurationMinutesPart
- [[nodiscard]] bool parse_duration_minutes_part()
- {
- StateTransaction transaction { *this };
- // DurationMinutesPart :::
- // DecimalDigits[~Sep] TemporalDecimalFraction MinutesDesignator
- // DecimalDigits[~Sep] MinutesDesignator DurationSecondsPart[opt]
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_minutes))
- return false;
- auto is_fractional = scoped_parse(m_state.parse_result.duration_minutes_fraction, [&]() { return parse_temporal_decimal_fraction(); });
- if (!parse_minutes_designator())
- return false;
- if (!is_fractional)
- (void)parse_duration_seconds_part();
- transaction.commit();
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-DurationSecondsPart
- [[nodiscard]] bool parse_duration_seconds_part()
- {
- StateTransaction transaction { *this };
- // DurationSecondsPart :::
- // DecimalDigits[~Sep] TemporalDecimalFraction[opt] SecondsDesignator
- if (!parse_decimal_digits(Separator::No, m_state.parse_result.duration_seconds))
- return false;
- (void)scoped_parse(m_state.parse_result.duration_seconds_fraction, [&]() { return parse_temporal_decimal_fraction(); });
- if (!parse_seconds_designator())
- return false;
- transaction.commit();
- return true;
- }
- // https://tc39.es/ecma262/#prod-TemporalDecimalFraction
- [[nodiscard]] bool parse_temporal_decimal_fraction()
- {
- // TemporalDecimalFraction :::
- // TemporalDecimalSeparator DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- // TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
- if (!parse_temporal_decimal_separator())
- return false;
- 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-Alpha
- [[nodiscard]] bool parse_alpha()
- {
- // Alpha ::: one of
- // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
- if (m_state.lexer.next_is(is_ascii_alpha)) {
- m_state.lexer.consume();
- return true;
- }
- return false;
- }
- // https://tc39.es/proposal-temporal/#prod-LowercaseAlpha
- [[nodiscard]] bool parse_lowercase_alpha()
- {
- // LowercaseAlpha ::: one of
- // a b c d e f g h i j k l m n o p q r s t u v w x y z
- if (m_state.lexer.next_is(is_ascii_lower_alpha)) {
- m_state.lexer.consume();
- return true;
- }
- return false;
- }
- // https://tc39.es/ecma262/#prod-DecimalDigit
- [[nodiscard]] bool 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/ecma262/#prod-DecimalDigits
- [[nodiscard]] bool parse_decimal_digits(Separator separator, Optional<StringView>& result)
- {
- StateTransaction transaction { *this };
- // FIXME: Implement [+Sep] if it's ever needed.
- VERIFY(separator == Separator::No);
- // DecimalDigits[Sep] ::
- // DecimalDigit
- // DecimalDigits[?Sep] DecimalDigit
- // [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator DecimalDigit
- if (!parse_decimal_digit())
- return {};
- while (parse_decimal_digit())
- ;
- result = transaction.parsed_string_view();
- transaction.commit();
- return true;
- }
- // https://tc39.es/ecma262/#prod-NonZeroDigit
- [[nodiscard]] bool 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/ecma262/#prod-ASCIISign
- [[nodiscard]] bool parse_ascii_sign()
- {
- // ASCIISign : one of
- // + -
- return m_state.lexer.consume_specific('+') || m_state.lexer.consume_specific('-');
- }
- // https://tc39.es/proposal-temporal/#prod-DateSeparator
- [[nodiscard]] bool parse_date_separator(Extended extended)
- {
- // DateSeparator[Extended] :::
- // [+Extended] -
- // [~Extended] [empty]
- if (extended == Extended::Yes)
- return m_state.lexer.consume_specific('-');
- return true;
- }
- // https://tc39.es/ecma262/#prod-TimeSeparator
- [[nodiscard]] bool parse_time_separator(Extended extended)
- {
- // TimeSeparator[Extended] :::
- // [+Extended] :
- // [~Extended] [empty]
- if (extended == Extended::Yes)
- return m_state.lexer.consume_specific(':');
- return true;
- }
- // https://tc39.es/proposal-temporal/#prod-TimeDesignator
- [[nodiscard]] bool 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-DateTimeSeparator
- [[nodiscard]] bool 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/ecma262/#prod-TemporalDecimalSeparator
- [[nodiscard]] bool parse_temporal_decimal_separator()
- {
- // TemporalDecimalSeparator ::: one of
- // . ,
- return m_state.lexer.consume_specific('.') || m_state.lexer.consume_specific(',');
- }
- // https://tc39.es/proposal-temporal/#prod-DurationDesignator
- [[nodiscard]] bool 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-YearsDesignator
- [[nodiscard]] bool 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-MonthsDesignator
- [[nodiscard]] bool 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-WeeksDesignator
- [[nodiscard]] bool 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-DaysDesignator
- [[nodiscard]] bool 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
- [[nodiscard]] bool 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
- [[nodiscard]] bool 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-SecondsDesignator
- [[nodiscard]] bool 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-UTCDesignator
- [[nodiscard]] bool parse_utc_designator()
- {
- StateTransaction transaction { *this };
- // UTCDesignator : one of
- // Z z
- 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-AnnotationCriticalFlag
- [[nodiscard]] bool parse_annotation_critical_flag()
- {
- // AnnotationCriticalFlag :::
- // !
- return m_state.lexer.consume_specific('!');
- }
- private:
- template<typename Parser, typename T>
- [[nodiscard]] bool scoped_parse(Optional<T>& storage, Parser&& parser)
- {
- StateTransaction transaction { *this };
- if (!parser())
- return false;
- if constexpr (IsSame<T, char>)
- storage = transaction.parsed_string_view()[0];
- else
- storage = transaction.parsed_string_view();
- transaction.commit();
- return true;
- }
- struct State {
- GenericLexer lexer;
- ParseResult parse_result;
- };
- struct StateTransaction {
- explicit StateTransaction(ISO8601Parser& parser)
- : m_parser(parser)
- , m_saved_state(parser.m_state)
- , m_start_index(parser.m_state.lexer.tell())
- {
- }
- ~StateTransaction()
- {
- if (!m_commit)
- m_parser.m_state = move(m_saved_state);
- }
- void commit() { m_commit = true; }
- StringView parsed_string_view() const
- {
- return m_parser.m_input.substring_view(m_start_index, m_parser.m_state.lexer.tell() - m_start_index);
- }
- private:
- ISO8601Parser& m_parser;
- State m_saved_state;
- size_t m_start_index { 0 };
- bool m_commit { false };
- };
- StringView m_input;
- State m_state;
- };
- #define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS \
- __JS_ENUMERATE(AnnotationValue, parse_annotation_value) \
- __JS_ENUMERATE(DateMonth, parse_date_month) \
- __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string) \
- __JS_ENUMERATE(TemporalDurationString, parse_temporal_duration_string) \
- __JS_ENUMERATE(TemporalInstantString, parse_temporal_instant_string) \
- __JS_ENUMERATE(TemporalMonthDayString, parse_temporal_month_day_string) \
- __JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string) \
- __JS_ENUMERATE(TemporalYearMonthString, parse_temporal_year_month_string) \
- __JS_ENUMERATE(TemporalZonedDateTimeString, parse_temporal_zoned_date_time_string) \
- __JS_ENUMERATE(TimeZoneIdentifier, parse_time_zone_identifier)
- Optional<ParseResult> parse_iso8601(Production production, StringView input)
- {
- ISO8601Parser parser { 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();
- }
- Optional<TimeZoneOffset> parse_utc_offset(StringView input, SubMinutePrecision sub_minute_precision)
- {
- ISO8601Parser parser { input };
- Optional<TimeZoneOffset> utc_offset;
- if (!parser.parse_utc_offset(sub_minute_precision, utc_offset))
- return {};
- // 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 utc_offset;
- }
- }
|