// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/temporal/temporal-parser.h" #include "src/base/bounds.h" #include "src/base/optional.h" #include "src/objects/string-inl.h" #include "src/strings/char-predicates-inl.h" namespace v8 { namespace internal { namespace { // Temporal #prod-NonzeroDigit inline constexpr bool IsNonZeroDecimalDigit(base::uc32 c) { return base::IsInRange(c, '1', '9'); } // Temporal #prod-TZLeadingChar inline constexpr bool IsTZLeadingChar(base::uc32 c) { return base::IsInRange(AsciiAlphaToLower(c), 'a', 'z') || c == '.' || c == '_'; } // Temporal #prod-TZChar inline constexpr bool IsTZChar(base::uc32 c) { return IsTZLeadingChar(c) || c == '-'; } // Temporal #prod-DecimalSeparator inline constexpr bool IsDecimalSeparator(base::uc32 c) { return c == '.' || c == ','; } // Temporal #prod-DateTimeSeparator inline constexpr bool IsDateTimeSeparator(base::uc32 c) { return c == ' ' || AsciiAlphaToLower(c) == 't'; } // Temporal #prod-ASCIISign inline constexpr bool IsAsciiSign(base::uc32 c) { return c == '-' || c == '+'; } // Temporal #prod-Sign inline constexpr bool IsSign(base::uc32 c) { return c == 0x2212 || IsAsciiSign(c); } inline constexpr base::uc32 CanonicalSign(base::uc32 c) { return c == 0x2212 ? '-' : c; } inline constexpr int32_t ToInt(base::uc32 c) { return c - '0'; } /** * The TemporalParser use two types of internal routine: * - Scan routines: Follow the function signature below: * template <typename Char> int32_t Scan$ProductionName( * base::Vector<Char> str, int32_t s, R* out) * * These routine scan the next item from position s in str and store the * parsed result into out if the expected string is successfully scanned. * It return the length of matched text from s or 0 to indicate no * expected item matched. * * - Satisfy routines: Follow the function sigature below: * template <typename Char> * bool Satisfy$ProductionName(base::Vector<Char> str, R* r); * It scan from the beginning of the str by calling Scan routines to put * parsed result into r and return true if the entire str satisfy the * production. It internally use Scan routines. * * TODO(ftang) investigate refactoring to class before shipping * Reference to RegExpParserImpl by encapsulating the cursor position and * only manipulating the current character and position with Next(), * Advance(), current(), etc */ // For Hour Production // Hour: // [0 1] Digit // 2 [0 1 2 3] template <typename Char> bool IsHour(base::Vector<Char> str, int32_t s) { return (str.length() >= (s + 2)) && ((base::IsInRange(str[s], '0', '1') && IsDecimalDigit(str[s + 1])) || ((str[s] == '2') && base::IsInRange(str[s + 1], '0', '3'))); } template <typename Char> int32_t ScanHour(base::Vector<Char> str, int32_t s, int32_t* out) { if (!IsHour(str, s)) return 0; *out = ToInt(str[s]) * 10 + ToInt(str[s + 1]); return 2; } // MinuteSecond: // [0 1 2 3 4 5] Digit template <typename Char> bool IsMinuteSecond(base::Vector<Char> str, int32_t s) { return (str.length() >= (s + 2)) && (base::IsInRange(str[s], '0', '5') && IsDecimalDigit(str[s + 1])); } template <typename Char> int32_t ScanMinuteSecond(base::Vector<Char> str, int32_t s, int32_t* out) { if (!IsMinuteSecond(str, s)) return 0; *out = ToInt(str[s]) * 10 + ToInt(str[s + 1]); return 2; } // For the forward production in the grammar such as // ProductionB: // ProductionT #define SCAN_FORWARD(B, T, R) \ template <typename Char> \ int32_t Scan##B(base::Vector<Char> str, int32_t s, R* r) { \ return Scan##T(str, s, r); \ } // Same as above but store the result into a particular field in R // For the forward production in the grammar such as // ProductionB: // ProductionT1 // ProductionT2 #define SCAN_EITHER_FORWARD(B, T1, T2, R) \ template <typename Char> \ int32_t Scan##B(base::Vector<Char> str, int32_t s, R* r) { \ int32_t len; \ if ((len = Scan##T1(str, s, r)) > 0) return len; \ return Scan##T2(str, s, r); \ } // TimeHour: Hour SCAN_FORWARD(TimeHour, Hour, int32_t) // TimeMinute: MinuteSecond SCAN_FORWARD(TimeMinute, MinuteSecond, int32_t) // TimeSecond: // MinuteSecond // 60 template <typename Char> int32_t ScanTimeSecond(base::Vector<Char> str, int32_t s, int32_t* out) { int32_t len = ScanMinuteSecond(str, s, out); // MinuteSecond if (len > 0) return len; if ((str.length() < (s + 2)) || (str[s] != '6') || (str[s + 1] != '0')) { return 0; } // 60 *out = 60; return 2; } constexpr int kPowerOfTen[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; // FractionalPart : Digit{1,9} template <typename Char> int32_t ScanFractionalPart(base::Vector<Char> str, int32_t s, int32_t* out) { int32_t cur = s; if ((str.length() < (cur + 1)) || !IsDecimalDigit(str[cur])) return 0; *out = ToInt(str[cur++]); while ((cur < str.length()) && ((cur - s) < 9) && IsDecimalDigit(str[cur])) { *out = 10 * (*out) + ToInt(str[cur++]); } *out *= kPowerOfTen[9 - (cur - s)]; return cur - s; } template <typename Char> int32_t ScanFractionalPart(base::Vector<Char> str, int32_t s, int64_t* out) { int32_t out32; int32_t len = ScanFractionalPart(str, s, &out32); *out = out32; return len; } // TimeFraction: FractionalPart SCAN_FORWARD(TimeFractionalPart, FractionalPart, int32_t) // Fraction: DecimalSeparator FractionalPart // DecimalSeparator: one of , . template <typename Char> int32_t ScanFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanFractionalPart(str, s + 1, out)) == 0) return 0; return len + 1; } // TimeFraction: DecimalSeparator TimeFractionalPart // DecimalSeparator: one of , . template <typename Char> int32_t ScanTimeFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanTimeFractionalPart(str, s + 1, out)) == 0) return 0; return len + 1; } template <typename Char> int32_t ScanTimeFraction(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { return ScanTimeFraction(str, s, &(r->time_nanosecond)); } // TimeSpec: // TimeHour // TimeHour : TimeMinute // TimeHour : TimeMinute : TimeSecond [TimeFraction] // TimeHour TimeMinute // TimeHour TimeMinute TimeSecond [TimeFraction] template <typename Char> int32_t ScanTimeSpec(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t time_hour, time_minute, time_second; int32_t len; int32_t cur = s; if ((len = ScanTimeHour(str, cur, &time_hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // TimeHour r->time_hour = time_hour; return cur - s; } if (str[cur] == ':') { cur++; if ((len = ScanTimeMinute(str, cur, &time_minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || (str[cur] != ':')) { // TimeHour : TimeMinute r->time_hour = time_hour; r->time_minute = time_minute; return cur - s; } cur++; if ((len = ScanTimeSecond(str, cur, &time_second)) == 0) return 0; } else { if ((len = ScanTimeMinute(str, cur, &time_minute)) == 0) { // TimeHour r->time_hour = time_hour; return cur - s; } cur += len; if ((len = ScanTimeSecond(str, cur, &time_second)) == 0) { // TimeHour TimeMinute r->time_hour = time_hour; r->time_minute = time_minute; return cur - s; } } cur += len; len = ScanTimeFraction(str, cur, r); r->time_hour = time_hour; r->time_minute = time_minute; r->time_second = time_second; return cur + len - s; } // TimeSpecSeparator: DateTimeSeparator TimeSpec // DateTimeSeparator: SPACE, 't', or 'T' template <typename Char> int32_t ScanTimeSpecSeparator(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (!(((s + 1) < str.length()) && IsDateTimeSeparator(str[s]))) return 0; int32_t len = ScanTimeSpec(str, s + 1, r); return (len == 0) ? 0 : len + 1; } // DateExtendedYear: Sign Digit Digit Digit Digit Digit Digit template <typename Char> int32_t ScanDateExtendedYear(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 7)) return 0; if (IsSign(str[s]) && IsDecimalDigit(str[s + 1]) && IsDecimalDigit(str[s + 2]) && IsDecimalDigit(str[s + 3]) && IsDecimalDigit(str[s + 4]) && IsDecimalDigit(str[s + 5]) && IsDecimalDigit(str[s + 6])) { int32_t sign = (CanonicalSign(str[s]) == '-') ? -1 : 1; *out = sign * (ToInt(str[s + 1]) * 100000 + ToInt(str[s + 2]) * 10000 + ToInt(str[s + 3]) * 1000 + ToInt(str[s + 4]) * 100 + ToInt(str[s + 5]) * 10 + ToInt(str[s + 6])); // In the end of #sec-temporal-iso8601grammar // It is a Syntax Error if DateExtendedYear is "-000000" or "−000000" // (U+2212 MINUS SIGN followed by 000000). if (sign == -1 && *out == 0) return 0; return 7; } return 0; } // DateFourDigitYear: Digit Digit Digit Digit template <typename Char> int32_t ScanDateFourDigitYear(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 4)) return 0; if (IsDecimalDigit(str[s]) && IsDecimalDigit(str[s + 1]) && IsDecimalDigit(str[s + 2]) && IsDecimalDigit(str[s + 3])) { *out = ToInt(str[s]) * 1000 + ToInt(str[s + 1]) * 100 + ToInt(str[s + 2]) * 10 + ToInt(str[s + 3]); return 4; } return 0; } // DateYear: // DateFourDigitYear // DateExtendedYear // The lookahead is at most 1 char. SCAN_EITHER_FORWARD(DateYear, DateFourDigitYear, DateExtendedYear, int32_t) // DateMonth: // 0 NonzeroDigit // 10 // 11 // 12 template <typename Char> int32_t ScanDateMonth(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 2)) return 0; if (((str[s] == '0') && IsNonZeroDecimalDigit(str[s + 1])) || ((str[s] == '1') && base::IsInRange(str[s + 1], '0', '2'))) { *out = ToInt(str[s]) * 10 + ToInt(str[s + 1]); return 2; } return 0; } // DateDay: // 0 NonzeroDigit // 1 Digit // 2 Digit // 30 // 31 template <typename Char> int32_t ScanDateDay(base::Vector<Char> str, int32_t s, int32_t* out) { if (str.length() < (s + 2)) return 0; if (((str[s] == '0') && IsNonZeroDecimalDigit(str[s + 1])) || (base::IsInRange(str[s], '1', '2') && IsDecimalDigit(str[s + 1])) || ((str[s] == '3') && base::IsInRange(str[s + 1], '0', '1'))) { *out = ToInt(str[s]) * 10 + ToInt(str[s + 1]); return 2; } return 0; } // Date: // DateYear - DateMonth - DateDay // DateYear DateMonth DateDay template <typename Char> int32_t ScanDate(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t date_year, date_month, date_day; int32_t cur = s; int32_t len; if ((len = ScanDateYear(str, cur, &date_year)) == 0) return 0; if (((cur += len) + 1) > str.length()) return 0; if (str[cur] == '-') { cur++; if ((len = ScanDateMonth(str, cur, &date_month)) == 0) return 0; cur += len; if (((cur + 1) > str.length()) || (str[cur++] != '-')) return 0; } else { if ((len = ScanDateMonth(str, cur, &date_month)) == 0) return 0; cur += len; } if ((len = ScanDateDay(str, cur, &date_day)) == 0) return 0; r->date_year = date_year; r->date_month = date_month; r->date_day = date_day; return cur + len - s; } // TimeZoneUTCOffsetHour: Hour SCAN_FORWARD(TimeZoneUTCOffsetHour, Hour, int32_t) // TimeZoneUTCOffsetMinute SCAN_FORWARD(TimeZoneUTCOffsetMinute, MinuteSecond, int32_t) // TimeZoneUTCOffsetSecond SCAN_FORWARD(TimeZoneUTCOffsetSecond, MinuteSecond, int32_t) // TimeZoneUTCOffsetFractionalPart: FractionalPart // See PR1796 SCAN_FORWARD(TimeZoneUTCOffsetFractionalPart, FractionalPart, int32_t) // TimeZoneUTCOffsetFraction: DecimalSeparator TimeZoneUTCOffsetFractionalPart // See PR1796 template <typename Char> int32_t ScanTimeZoneUTCOffsetFraction(base::Vector<Char> str, int32_t s, int32_t* out) { if ((str.length() < (s + 2)) || (!IsDecimalSeparator(str[s]))) return 0; int32_t len; if ((len = ScanTimeZoneUTCOffsetFractionalPart(str, s + 1, out)) > 0) { return len + 1; } return 0; } // Note: "TimeZoneUTCOffset" is abbreviated as "TZUO" below // TimeZoneNumericUTCOffset: // TZUOSign TZUOHour // TZUOSign TZUOHour : TZUOMinute // TZUOSign TZUOHour : TZUOMinute : TZUOSecond [TZUOFraction] // TZUOSign TZUOHour TZUOMinute // TZUOSign TZUOHour TZUOMinute TZUOSecond [TZUOFraction] template <typename Char> int32_t ScanTimeZoneNumericUTCOffset(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len, hour, minute, second, nanosecond; int32_t cur = s; if ((str.length() < (cur + 1)) || (!IsSign(str[cur]))) return 0; int32_t sign = (CanonicalSign(str[cur++]) == '-') ? -1 : 1; if ((len = ScanTimeZoneUTCOffsetHour(str, cur, &hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // TZUOSign TZUOHour r->tzuo_sign = sign; r->tzuo_hour = hour; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } if (str[cur] == ':') { cur++; if ((len = ScanTimeZoneUTCOffsetMinute(str, cur, &minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || str[cur] != ':') { // TZUOSign TZUOHour : TZUOMinute r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } cur++; if ((len = ScanTimeZoneUTCOffsetSecond(str, cur, &second)) == 0) return 0; } else { if ((len = ScanTimeZoneUTCOffsetMinute(str, cur, &minute)) == 0) { // TZUOSign TZUOHour r->tzuo_sign = sign; r->tzuo_hour = hour; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } cur += len; if ((len = ScanTimeZoneUTCOffsetSecond(str, cur, &second)) == 0) { // TZUOSign TZUOHour TZUOMinute r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->offset_string_start = s; r->offset_string_length = cur - s; return cur - s; } } cur += len; len = ScanTimeZoneUTCOffsetFraction(str, cur, &nanosecond); r->tzuo_sign = sign; r->tzuo_hour = hour; r->tzuo_minute = minute; r->tzuo_second = second; if (len > 0) r->tzuo_nanosecond = nanosecond; r->offset_string_start = s; r->offset_string_length = cur + len - s; return cur + len - s; } // TimeZoneUTCOffset: // TimeZoneNumericUTCOffset // UTCDesignator template <typename Char> int32_t ScanTimeZoneUTCOffset(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 1)) return 0; if (AsciiAlphaToLower(str[s]) == 'z') { // UTCDesignator r->utc_designator = true; return 1; } // TimeZoneNumericUTCOffset return ScanTimeZoneNumericUTCOffset(str, s, r); } // TimeZoneIANANameComponent : // TZLeadingChar TZChar{0,13} but not one of . or .. template <typename Char> int32_t ScanTimeZoneIANANameComponent(base::Vector<Char> str, int32_t s) { int32_t cur = s; if (str.length() < (cur + 1) || !IsTZLeadingChar(str[cur++])) return 0; while (((cur) < str.length()) && ((cur - s) < 14) && IsTZChar(str[cur])) { cur++; } if ((cur - s) == 1 && str[s] == '.') return 0; if ((cur - s) == 2 && str[s] == '.' && str[s + 1] == '.') return 0; return cur - s; } // TimeZoneIANANameTail : // TimeZoneIANANameComponent // TimeZoneIANANameComponent / TimeZoneIANANameTail // TimeZoneIANAName : // TimeZoneIANANameTail // The spec text use tail recusion with TimeZoneIANANameComponent and // TimeZoneIANANameTail. In our implementation, we use an iteration loop // instead. template <typename Char> int32_t ScanTimeZoneIANAName(base::Vector<Char> str, int32_t s) { int32_t cur = s; int32_t len; if ((len = ScanTimeZoneIANANameComponent(str, cur)) == 0) return 0; cur += len; while ((str.length() > (cur + 1)) && (str[cur] == '/')) { cur++; if ((len = ScanTimeZoneIANANameComponent(str, cur)) == 0) { return 0; } // TimeZoneIANANameComponent / TimeZoneIANAName cur += len; } return cur - s; } template <typename Char> int32_t ScanTimeZoneIANAName(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len; if ((len = ScanTimeZoneIANAName(str, s)) == 0) return 0; r->tzi_name_start = s; r->tzi_name_length = len; return len; } // TimeZoneUTCOffsetName // Sign Hour // Sign Hour : MinuteSecond // Sign Hour MinuteSecond // Sign Hour : MinuteSecond : MinuteSecond [Fraction] // Sign Hour MinuteSecond MinuteSecond [Fraction] // template <typename Char> int32_t ScanTimeZoneUTCOffsetName(base::Vector<Char> str, int32_t s) { int32_t cur = s; int32_t len; if ((str.length() < (s + 3)) || !IsSign(str[cur++])) return 0; int32_t hour, minute, second, fraction; if ((len = ScanHour(str, cur, &hour)) == 0) return 0; cur += len; if ((cur + 1) > str.length()) { // Sign Hour return cur - s; } if (str[cur] == ':') { // Sign Hour : cur++; if ((len = ScanMinuteSecond(str, cur, &minute)) == 0) return 0; cur += len; if ((cur + 1) > str.length() || (str[cur] != ':')) { // Sign Hour : MinuteSecond return cur - s; } cur++; // Sign Hour : MinuteSecond : if ((len = ScanMinuteSecond(str, cur, &second)) == 0) return 0; cur += len; len = ScanFraction(str, cur, &fraction); return cur + len - s; } else { if ((len = ScanMinuteSecond(str, cur, &minute)) == 0) { // Sign Hour return cur - s; } cur += len; if ((len = ScanMinuteSecond(str, cur, &second)) == 0) { // Sign Hour MinuteSecond return cur - s; } cur += len; len = ScanFraction(str, cur, &fraction); // Sign Hour MinuteSecond MinuteSecond [Fraction] return cur + len - s; } } // TimeZoneBracketedName // TimeZoneIANAName // "Etc/GMT" ASCIISign Hour // TimeZoneUTCOffsetName // Since "Etc/GMT" also fit TimeZoneIANAName so we need to try // "Etc/GMT" ASCIISign Hour first. template <typename Char> int32_t ScanEtcGMTAsciiSignHour(base::Vector<Char> str, int32_t s) { if ((s + 10) > str.length()) return 0; int32_t cur = s; if ((str[cur++] != 'E') || (str[cur++] != 't') || (str[cur++] != 'c') || (str[cur++] != '/') || (str[cur++] != 'G') || (str[cur++] != 'M') || (str[cur++] != 'T')) { return 0; } Char sign = str[cur++]; if (!IsAsciiSign(sign)) return 0; int32_t hour; int32_t len = ScanHour(str, cur, &hour); if (len == 0) return 0; // "Etc/GMT" ASCIISign Hour return 10; } template <typename Char> int32_t ScanTimeZoneBracketedName(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len; if ((len = ScanEtcGMTAsciiSignHour(str, s)) > 0) return len; if ((len = ScanTimeZoneIANAName(str, s)) > 0) { r->tzi_name_start = s; r->tzi_name_length = len; return len; } else { r->tzi_name_start = 0; r->tzi_name_length = 0; } return ScanTimeZoneUTCOffsetName(str, s); } // TimeZoneBracketedAnnotation: '[' TimeZoneBracketedName ']' template <typename Char> int32_t ScanTimeZoneBracketedAnnotation(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if ((str.length() < (s + 3)) || (str[s] != '[')) return 0; int32_t cur = s + 1; cur += ScanTimeZoneBracketedName(str, cur, r); if ((cur - s == 1) || str.length() < (cur + 1) || (str[cur++] != ']')) { // Reset value setted by ScanTimeZoneBracketedName r->tzi_name_start = 0; r->tzi_name_length = 0; return 0; } return cur - s; } // TimeZoneOffsetRequired: // TimeZoneUTCOffset [TimeZoneBracketedAnnotation] template <typename Char> int32_t ScanTimeZoneOffsetRequired(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanTimeZoneUTCOffset(str, cur, r); if (cur == s) return 0; return cur + ScanTimeZoneBracketedAnnotation(str, cur, r) - s; } // TimeZoneNameRequired: // [TimeZoneUTCOffset] TimeZoneBracketedAnnotation template <typename Char> int32_t ScanTimeZoneNameRequired(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanTimeZoneUTCOffset(str, cur, r); int32_t len = ScanTimeZoneBracketedAnnotation(str, cur, r); if (len == 0) return 0; return cur + len - s; } // TimeZone: // TimeZoneOffsetRequired // TimeZoneNameRequired // The lookahead is at most 1 char. SCAN_EITHER_FORWARD(TimeZone, TimeZoneOffsetRequired, TimeZoneNameRequired, ParsedISO8601Result) // CalendarNameComponent: // CalChar {3,8} template <typename Char> int32_t ScanCalendarNameComponent(base::Vector<Char> str, int32_t s) { int32_t cur = s; while ((cur < str.length()) && IsAlphaNumeric(str[cur])) cur++; if ((cur - s) < 3 || (cur - s) > 8) return 0; return (cur - s); } // CalendarNameTail : // CalendarNameComponent // CalendarNameComponent - CalendarNameTail // CalendarName : // CalendarNameTail // The spec text use tail recusion with CalendarNameComponent and // CalendarNameTail. In our implementation, we use an iteration loop instead. template <typename Char> int32_t ScanCalendarName(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; int32_t len; if ((len = ScanCalendarNameComponent(str, cur)) == 0) return 0; cur += len; while ((str.length() > (cur + 1)) && (str[cur++] == '-')) { if ((len = ScanCalendarNameComponent(str, cur)) == 0) return 0; // CalendarNameComponent - CalendarName cur += len; } r->calendar_name_start = s; r->calendar_name_length = cur - s; return cur - s; } // Calendar: '[u-ca=' CalendarName ']' template <typename Char> int32_t ScanCalendar(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 7)) return 0; int32_t cur = s; // "[u-ca=" if ((str[cur++] != '[') || (str[cur++] != 'u') || (str[cur++] != '-') || (str[cur++] != 'c') || (str[cur++] != 'a') || (str[cur++] != '=')) { return 0; } int32_t len = ScanCalendarName(str, cur, r); if (len == 0) return 0; if ((str.length() < (cur + len + 1)) || (str[cur + len] != ']')) { return 0; } return 6 + len + 1; } // CalendarTime: TimeSpec [TimeZone] [Calendar] template <typename Char> int32_t ScanCalendarTime(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanTimeSpec(str, cur, r); if (cur - s == 0) return 0; cur += ScanTimeZone(str, cur, r); cur += ScanCalendar(str, cur, r); return cur - s; } // DateTime: Date [TimeSpecSeparator][TimeZone] template <typename Char> int32_t ScanDateTime(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanDate(str, cur, r); if (cur == s) return 0; cur += ScanTimeSpecSeparator(str, cur, r); return cur + ScanTimeZone(str, cur, r) - s; } // DateSpecYearMonth: DateYear ['-'] DateMonth template <typename Char> int32_t ScanDateSpecYearMonth(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t date_year, date_month; int32_t cur = s; cur += ScanDateYear(str, cur, &date_year); if (cur == s) return 0; if (str.length() < (cur + 1)) return 0; if (str[cur] == '-') cur++; int32_t len = ScanDateMonth(str, cur, &date_month); if (len == 0) return 0; r->date_year = date_year; r->date_month = date_month; return cur + len - s; } // DateSpecMonthDay: // TwoDashopt DateMonth -opt DateDay template <typename Char> int32_t ScanDateSpecMonthDay(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { if (str.length() < (s + 4)) return 0; int32_t cur = s; if (str[cur] == '-') { // The first two dash are optional together if (str[++cur] != '-') return 0; // TwoDash cur++; } int32_t date_month, date_day; int32_t len = ScanDateMonth(str, cur, &date_month); if (len == 0) return 0; cur += len; if (str.length() < (cur + 1)) return 0; // '-' if (str[cur] == '-') cur++; len = ScanDateDay(str, cur, &date_day); if (len == 0) return 0; r->date_month = date_month; r->date_day = date_day; return cur + len - s; } // TemporalTimeZoneIdentifier: // TimeZoneNumericUTCOffset // TimeZoneIANAName template <typename Char> int32_t ScanTemporalTimeZoneIdentifier(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len; if ((len = ScanTimeZoneNumericUTCOffset(str, s, r)) > 0) return len; if ((len = ScanTimeZoneIANAName(str, s)) == 0) return 0; r->tzi_name_start = s; r->tzi_name_length = len; return len; } // CalendarDateTime: DateTime [Calendar] template <typename Char> int32_t ScanCalendarDateTime(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t len = ScanDateTime(str, 0, r); if (len == 0) return 0; return len + ScanCalendar(str, len, r); } // TemporalZonedDateTimeString: // Date [TimeSpecSeparator] TimeZoneNameRequired [Calendar] template <typename Char> int32_t ScanTemporalZonedDateTimeString(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { // Date int32_t cur = s; cur += ScanDate(str, cur, r); if (cur == s) return 0; // TimeSpecSeparator cur += ScanTimeSpecSeparator(str, cur, r); // TimeZoneNameRequired int32_t len = ScanTimeZoneNameRequired(str, cur, r); if (len == 0) return 0; cur += len; // Calendar return cur + ScanCalendar(str, cur, r) - s; } SCAN_FORWARD(TemporalDateString, CalendarDateTime, ParsedISO8601Result) SCAN_FORWARD(TemporalDateTimeString, CalendarDateTime, ParsedISO8601Result) // TemporalTimeZoneString: // TemporalTimeZoneIdentifier // Date [TimeSpecSeparator] TimeZone [Calendar] template <typename Char> int32_t ScanDate_TimeSpecSeparator_TimeZone_Calendar(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { int32_t cur = s; cur += ScanDate(str, cur, r); if (cur == s) return 0; cur += ScanTimeSpecSeparator(str, cur, r); int32_t len = ScanTimeZone(str, cur, r); if (len == 0) return 0; cur += len; return cur + ScanCalendar(str, cur, r) - s; } // The lookahead is at most 8 chars. SCAN_EITHER_FORWARD(TemporalTimeZoneString, TemporalTimeZoneIdentifier, Date_TimeSpecSeparator_TimeZone_Calendar, ParsedISO8601Result) // TemporalTimeString // CalendarTime // CalendarDateTime // The lookahead is at most 7 chars. SCAN_EITHER_FORWARD(TemporalTimeString, CalendarTime, CalendarDateTime, ParsedISO8601Result) // TemporalYearMonthString: // DateSpecYearMonth // CalendarDateTime // The lookahead is at most 11 chars. SCAN_EITHER_FORWARD(TemporalYearMonthString, DateSpecYearMonth, CalendarDateTime, ParsedISO8601Result) // TemporalMonthDayString // DateSpecMonthDay // CalendarDateTime // The lookahead is at most 5 chars. SCAN_EITHER_FORWARD(TemporalMonthDayString, DateSpecMonthDay, CalendarDateTime, ParsedISO8601Result) // TemporalRelativeToString: // TemporalDateTimeString // TemporalZonedDateTimeString // TemporalZonedDateTimeString is subset of TemporalDateTimeString // See https://github.com/tc39/proposal-temporal/issues/1939 SCAN_FORWARD(TemporalRelativeToString, TemporalDateTimeString, ParsedISO8601Result) // TemporalInstantString // Date TimeZoneOffsetRequired // Date DateTimeSeparator TimeSpec TimeZoneOffsetRequired template <typename Char> int32_t ScanTemporalInstantString(base::Vector<Char> str, int32_t s, ParsedISO8601Result* r) { // Date int32_t cur = s; cur += ScanDate(str, cur, r); if (cur == s) return 0; // TimeZoneOffsetRequired int32_t len = ScanTimeZoneOffsetRequired(str, cur, r); if (len > 0) return cur + len - s; // DateTimeSeparator if (!(((cur + 1) < str.length()) && IsDateTimeSeparator(str[cur++]))) { return 0; } // TimeSpec len = ScanTimeSpec(str, cur, r); if (len == 0) return 0; cur += len; // TimeZoneOffsetRequired len = ScanTimeZoneOffsetRequired(str, cur, r); if (len == 0) return 0; return cur + len - s; } // ============================================================================== #define SATISIFY(T, R) \ template <typename Char> \ bool Satisfy##T(base::Vector<Char> str, R* r) { \ R ret; \ int32_t len = Scan##T(str, 0, &ret); \ if ((len > 0) && (len == str.length())) { \ *r = ret; \ return true; \ } \ return false; \ } #define IF_SATISFY_RETURN(T) \ { \ if (Satisfy##T(str, r)) return true; \ } #define SATISIFY_EITHER(T1, T2, T3, R) \ template <typename Char> \ bool Satisfy##T1(base::Vector<Char> str, R* r) { \ IF_SATISFY_RETURN(T2) \ IF_SATISFY_RETURN(T3) \ return false; \ } SATISIFY(TemporalDateTimeString, ParsedISO8601Result) SATISIFY(TemporalDateString, ParsedISO8601Result) SATISIFY(CalendarTime, ParsedISO8601Result) SATISIFY(DateTime, ParsedISO8601Result) SATISIFY(DateSpecYearMonth, ParsedISO8601Result) SATISIFY(DateSpecMonthDay, ParsedISO8601Result) SATISIFY(Date_TimeSpecSeparator_TimeZone_Calendar, ParsedISO8601Result) SATISIFY(CalendarDateTime, ParsedISO8601Result) SATISIFY_EITHER(TemporalTimeString, CalendarTime, CalendarDateTime, ParsedISO8601Result) SATISIFY_EITHER(TemporalYearMonthString, DateSpecYearMonth, CalendarDateTime, ParsedISO8601Result) SATISIFY_EITHER(TemporalMonthDayString, DateSpecMonthDay, CalendarDateTime, ParsedISO8601Result) SATISIFY(TimeZoneNumericUTCOffset, ParsedISO8601Result) SATISIFY(TimeZoneIANAName, ParsedISO8601Result) SATISIFY_EITHER(TemporalTimeZoneIdentifier, TimeZoneNumericUTCOffset, TimeZoneIANAName, ParsedISO8601Result) SATISIFY_EITHER(TemporalTimeZoneString, TemporalTimeZoneIdentifier, Date_TimeSpecSeparator_TimeZone_Calendar, ParsedISO8601Result) SATISIFY(TemporalInstantString, ParsedISO8601Result) SATISIFY(TemporalZonedDateTimeString, ParsedISO8601Result) SATISIFY_EITHER(TemporalRelativeToString, TemporalDateTimeString, TemporalZonedDateTimeString, ParsedISO8601Result) SATISIFY(CalendarName, ParsedISO8601Result) template <typename Char> bool SatisfyTemporalCalendarString(base::Vector<Char> str, ParsedISO8601Result* r) { IF_SATISFY_RETURN(CalendarName) IF_SATISFY_RETURN(TemporalInstantString) IF_SATISFY_RETURN(CalendarDateTime) IF_SATISFY_RETURN(CalendarTime) IF_SATISFY_RETURN(DateSpecYearMonth) IF_SATISFY_RETURN(DateSpecMonthDay) return false; } // Duration SCAN_FORWARD(TimeFractionalPart, FractionalPart, int64_t) template <typename Char> int32_t ScanFraction(base::Vector<Char> str, int32_t s, int64_t* out) { if (str.length() < (s + 2) || !IsDecimalSeparator(str[s])) return 0; int32_t len = ScanTimeFractionalPart(str, s + 1, out); return (len == 0) ? 0 : len + 1; } SCAN_FORWARD(TimeFraction, Fraction, int64_t) // Digits : Digit [Digits] template <typename Char> int32_t ScanDigits(base::Vector<Char> str, int32_t s, int64_t* out) { if (str.length() < (s + 1) || !IsDecimalDigit(str[s])) return 0; *out = ToInt(str[s]); int32_t len = 1; while (s + len + 1 <= str.length() && IsDecimalDigit(str[s + len])) { *out = 10 * (*out) + ToInt(str[s + len]); len++; } return len; } SCAN_FORWARD(DurationYears, Digits, int64_t) SCAN_FORWARD(DurationMonths, Digits, int64_t) SCAN_FORWARD(DurationWeeks, Digits, int64_t) SCAN_FORWARD(DurationDays, Digits, int64_t) // DurationWholeHours : Digits SCAN_FORWARD(DurationWholeHours, Digits, int64_t) // DurationWholeMinutes : Digits SCAN_FORWARD(DurationWholeMinutes, Digits, int64_t) // DurationWholeSeconds : Digits SCAN_FORWARD(DurationWholeSeconds, Digits, int64_t) // DurationHoursFraction : TimeFraction SCAN_FORWARD(DurationHoursFraction, TimeFraction, int64_t) // DurationMinutesFraction : TimeFraction SCAN_FORWARD(DurationMinutesFraction, TimeFraction, int64_t) // DurationSecondsFraction : TimeFraction SCAN_FORWARD(DurationSecondsFraction, TimeFraction, int64_t) #define DURATION_WHOLE_FRACTION_DESIGNATOR(Name, name, d) \ template <typename Char> \ int32_t ScanDurationWhole##Name##FractionDesignator( \ base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { \ int32_t cur = s; \ int64_t whole = ParsedISO8601Duration::kEmpty; \ cur += ScanDurationWhole##Name(str, cur, &whole); \ if (cur == s) return 0; \ int64_t fraction = ParsedISO8601Duration::kEmpty; \ int32_t len = ScanDuration##Name##Fraction(str, cur, &fraction); \ cur += len; \ if (str.length() < (cur + 1) || AsciiAlphaToLower(str[cur++]) != (d)) \ return 0; \ r->whole_##name = whole; \ r->name##_fraction = fraction; \ return cur - s; \ } DURATION_WHOLE_FRACTION_DESIGNATOR(Seconds, seconds, 's') DURATION_WHOLE_FRACTION_DESIGNATOR(Minutes, minutes, 'm') DURATION_WHOLE_FRACTION_DESIGNATOR(Hours, hours, 'h') // DurationSecondsPart : // DurationWholeSeconds DurationSecondsFractionopt SecondsDesignator SCAN_FORWARD(DurationSecondsPart, DurationWholeSecondsFractionDesignator, ParsedISO8601Duration) // DurationMinutesPart : // DurationWholeMinutes DurationMinutesFractionopt MinutesDesignator // [DurationSecondsPart] template <typename Char> int32_t ScanDurationMinutesPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s + ScanDurationWholeMinutesFractionDesignator(str, s, r); if (cur == s) return 0; return cur + ScanDurationSecondsPart(str, cur, r) - s; } // DurationHoursPart : // DurationWholeHours DurationHoursFractionopt HoursDesignator // DurationMinutesPart // // DurationWholeHours DurationHoursFractionopt HoursDesignator // [DurationSecondsPart] template <typename Char> int32_t ScanDurationHoursPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s + ScanDurationWholeHoursFractionDesignator(str, s, r); if (cur == s) return 0; int32_t len = ScanDurationMinutesPart(str, cur, r); if (len > 0) return cur + len - s; return cur + ScanDurationSecondsPart(str, cur, r) - s; } // DurationTime : // DurationTimeDesignator DurationHoursPart // DurationTimeDesignator DurationMinutesPart // DurationTimeDesignator DurationSecondsPart template <typename Char> int32_t ScanDurationTime(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; if (str.length() < (s + 1)) return 0; if (AsciiAlphaToLower(str[cur++]) != 't') return 0; if ((cur += ScanDurationHoursPart(str, cur, r)) - s > 1) return cur - s; if ((cur += ScanDurationMinutesPart(str, cur, r)) - s > 1) return cur - s; if ((cur += ScanDurationSecondsPart(str, cur, r)) - s > 1) return cur - s; return 0; } #define DURATION_AND_DESIGNATOR(Name, name, d) \ template <typename Char> \ int32_t ScanDuration##Name##Designator(base::Vector<Char> str, int32_t s, \ ParsedISO8601Duration* r) { \ int32_t cur = s; \ int64_t name; \ if ((cur += ScanDuration##Name(str, cur, &name)) == s) return 0; \ if (str.length() < (cur + 1) || AsciiAlphaToLower(str[cur++]) != (d)) { \ return 0; \ } \ r->name = name; \ return cur - s; \ } DURATION_AND_DESIGNATOR(Days, days, 'd') DURATION_AND_DESIGNATOR(Weeks, weeks, 'w') DURATION_AND_DESIGNATOR(Months, months, 'm') DURATION_AND_DESIGNATOR(Years, years, 'y') // DurationDaysPart : DurationDays DaysDesignator SCAN_FORWARD(DurationDaysPart, DurationDaysDesignator, ParsedISO8601Duration) // DurationWeeksPart : DurationWeeks WeeksDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationWeeksPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; if ((cur += ScanDurationWeeksDesignator(str, cur, r)) == s) return 0; return cur + ScanDurationDaysPart(str, cur, r) - s; } // DurationMonthsPart : // DurationMonths MonthsDesignator DurationWeeksPart // DurationMonths MonthsDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationMonthsPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len; if ((cur += ScanDurationMonthsDesignator(str, cur, r)) == s) return 0; if ((len = ScanDurationWeeksPart(str, cur, r)) > 0) return cur + len - s; return cur + ScanDurationDaysPart(str, cur, r) - s; } // DurationYearsPart : // DurationYears YearsDesignator DurationMonthsPart // DurationYears YearsDesignator DurationWeeksPart // DurationYears YearsDesignator [DurationDaysPart] template <typename Char> int32_t ScanDurationYearsPart(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; int32_t len; if ((cur += ScanDurationYearsDesignator(str, cur, r)) == s) return 0; if ((len = ScanDurationMonthsPart(str, cur, r)) > 0) return cur + len - s; if ((len = ScanDurationWeeksPart(str, cur, r)) > 0) return cur + len - s; return cur + ScanDurationDaysPart(str, cur, r) - s; } // DurationDate : // DurationYearsPart [DurationTime] // DurationMonthsPart [DurationTime] // DurationWeeksPart [DurationTime] // DurationDaysPart [DurationTime] template <typename Char> int32_t ScanDurationDate(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { int32_t cur = s; do { if ((cur += ScanDurationYearsPart(str, cur, r)) > s) break; if ((cur += ScanDurationMonthsPart(str, cur, r)) > s) break; if ((cur += ScanDurationWeeksPart(str, cur, r)) > s) break; if ((cur += ScanDurationDaysPart(str, cur, r)) > s) break; return 0; } while (false); return cur + ScanDurationTime(str, cur, r) - s; } // Duration : // Signopt DurationDesignator DurationDate // Signopt DurationDesignator DurationTime template <typename Char> int32_t ScanDuration(base::Vector<Char> str, int32_t s, ParsedISO8601Duration* r) { if (str.length() < (s + 2)) return 0; int32_t cur = s; int32_t sign = (IsSign(str[cur]) && CanonicalSign(str[cur++]) == '-') ? -1 : 1; if (AsciiAlphaToLower(str[cur++]) != 'p') return 0; int32_t len = ScanDurationDate(str, cur, r); if (len == 0) len = ScanDurationTime(str, cur, r); if (len == 0) return 0; r->sign = sign; return cur + len - s; } SCAN_FORWARD(TemporalDurationString, Duration, ParsedISO8601Duration) SATISIFY(TemporalDurationString, ParsedISO8601Duration) } // namespace #define IMPL_PARSE_METHOD(R, NAME) \ base::Optional<R> TemporalParser::Parse##NAME(Isolate* isolate, \ Handle<String> iso_string) { \ bool valid; \ R parsed; \ iso_string = String::Flatten(isolate, iso_string); \ { \ DisallowGarbageCollection no_gc; \ String::FlatContent str_content = iso_string->GetFlatContent(no_gc); \ if (str_content.IsOneByte()) { \ valid = Satisfy##NAME(str_content.ToOneByteVector(), &parsed); \ } else { \ valid = Satisfy##NAME(str_content.ToUC16Vector(), &parsed); \ } \ } \ if (valid) return parsed; \ return base::nullopt; \ } IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalDateTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalDateString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalYearMonthString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalMonthDayString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalInstantString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalZonedDateTimeString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalTimeZoneString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalRelativeToString) IMPL_PARSE_METHOD(ParsedISO8601Result, TemporalCalendarString) IMPL_PARSE_METHOD(ParsedISO8601Result, TimeZoneNumericUTCOffset) IMPL_PARSE_METHOD(ParsedISO8601Duration, TemporalDurationString) } // namespace internal } // namespace v8