1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:37:45 +00:00

LibJS: Fix TemporalCalendarString ambiguity

This is a normative change in the Temporal spec.

See:
- 278d238
- b73aea7
This commit is contained in:
Linus Groh 2022-08-30 17:43:27 +01:00
parent 484c66125d
commit b6f101f1c0
6 changed files with 63 additions and 50 deletions

View file

@ -243,6 +243,7 @@
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
M(TemporalInvalidISODate, "Invalid ISO date") \
M(TemporalInvalidISODateTime, "Invalid ISO date time") \
M(TemporalInvalidMonthCode, "Invalid month code") \
M(TemporalInvalidMonthDayString, "Invalid month day string '{}'") \
M(TemporalInvalidMonthDayStringUTCDesignator, "Invalid month day string '{}': must not contain a UTC designator") \

View file

@ -1162,14 +1162,38 @@ Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::Signed
}
// 13.28 ParseISODateTime ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM& vm, ParseResult const& parse_result)
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM& vm, StringView iso_string)
{
// 1. Let parseResult be empty.
// 2. For each nonterminal goal of « TemporalDateTimeString, TemporalInstantString, TemporalMonthDayString, TemporalTimeString, TemporalYearMonthString, TemporalZonedDateTimeString », do
// a. If parseResult is not a Parse Node, set parseResult to ParseText(StringToCodePoints(isoString), goal).
// 3. Assert: parseResult is a Parse Node.
// NOTE: All of this is done by receiving an already parsed ISO string (ParseResult).
Optional<ParseResult> parse_result;
static constexpr auto productions = AK::Array {
Production::TemporalDateTimeString,
Production::TemporalInstantString,
Production::TemporalMonthDayString,
Production::TemporalTimeString,
Production::TemporalYearMonthString,
Production::TemporalZonedDateTimeString,
};
// 2. For each nonterminal goal of « TemporalDateTimeString, TemporalInstantString, TemporalMonthDayString, TemporalTimeString, TemporalYearMonthString, TemporalZonedDateTimeString », do
for (auto goal : productions) {
// a. If parseResult is not a Parse Node, set parseResult to ParseText(StringToCodePoints(isoString), goal).
parse_result = parse_iso8601(goal, iso_string);
if (parse_result.has_value())
break;
}
// 3. If parseResult is not a Parse Node, throw a RangeError exception.
if (!parse_result.has_value())
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidISODateTime);
return parse_iso_date_time(vm, *parse_result);
}
// 13.28 ParseISODateTime ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM& vm, ParseResult const& parse_result)
{
// 4. Let each of year, month, day, hour, minute, second, fSeconds, and calendar be the source text matched by the respective DateYear, DateMonth, DateDay, TimeHour, TimeMinute, TimeSecond, TimeFraction, and CalendarName Parse Node contained within parseResult, or an empty sequence of code points if not present.
auto year = parse_result.date_year;
auto month = parse_result.date_month;
@ -1357,24 +1381,33 @@ ThrowCompletionOr<ISODateTime> parse_temporal_zoned_date_time_string(VM& vm, Str
// 13.31 ParseTemporalCalendarString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
ThrowCompletionOr<String> parse_temporal_calendar_string(VM& vm, String const& iso_string)
{
// 1. Let parseResult be ParseText(StringToCodePoints(isoString), TemporalCalendarString).
auto parse_result = parse_iso8601(Production::TemporalCalendarString, iso_string);
// 1. Let parseResult be Completion(ParseISODateTime(isoString)).
auto parse_result_completion = parse_iso_date_time(vm, iso_string);
// 2. If parseResult is a List of errors, throw a RangeError exception.
// 2. If parseResult is a normal completion, then
if (!parse_result_completion.is_error()) {
// a. Let calendar be parseResult.[[Value]].[[Calendar]].
auto calendar = parse_result_completion.value().calendar;
// b. If calendar is undefined, return "iso8601".
if (!calendar.has_value())
return "iso8601"sv;
// c. Else, return calendar.
else
return calendar.release_value();
}
// 3. Else,
else {
// a. Set parseResult to ParseText(StringToCodePoints(isoString), CalendarName).
auto parse_result = parse_iso8601(Production::CalendarName, iso_string);
// b. If parseResult is a List of errors, throw a RangeError exception.
if (!parse_result.has_value())
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarString, iso_string);
// 3. Let id be the source text matched by the CalendarName Parse Node contained within parseResult, or an empty sequence of code points if not present.
auto id = parse_result->calendar_name;
// 4. If id is empty, then
if (!id.has_value()) {
// a. Return "iso8601".
return "iso8601"sv;
// c. Else, return isoString.
else
return iso_string;
}
// 5. Return CodePointsToString(id).
return id.value();
}
// 13.32 ParseTemporalDateString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatestring

View file

@ -161,6 +161,7 @@ Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResu
double round_number_to_increment(double, u64 increment, StringView rounding_mode);
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, StringView iso_string);
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, ParseResult const& parse_result);
ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(VM&, String const& iso_string);
ThrowCompletionOr<ISODateTime> parse_temporal_zoned_date_time_string(VM&, String const& iso_string);

View file

@ -406,17 +406,14 @@ ThrowCompletionOr<Object*> to_temporal_calendar(VM& vm, Value temporal_calendar_
// 2. Let identifier be ? ToString(temporalCalendarLike).
auto identifier = TRY(temporal_calendar_like.to_string(vm));
// 3. If IsBuiltinCalendar(identifier) is false, then
if (!is_builtin_calendar(identifier)) {
// a. Set identifier to ? ParseTemporalCalendarString(identifier).
// 3. Set identifier to ? ParseTemporalCalendarString(identifier).
identifier = TRY(parse_temporal_calendar_string(vm, identifier));
// b. If IsBuiltinCalendar(identifier) is false, throw a RangeError exception.
// 4. If IsBuiltinCalendar(identifier) is false, throw a RangeError exception.
if (!is_builtin_calendar(identifier))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, identifier);
}
// 4. Return ! CreateTemporalCalendar(identifier).
// 5. Return ! CreateTemporalCalendar(identifier).
return MUST(create_temporal_calendar(vm, identifier));
}

View file

@ -1446,24 +1446,6 @@ bool ISO8601Parser::parse_temporal_zoned_date_time_string()
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();
}
}
#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS \
@ -1475,8 +1457,8 @@ bool ISO8601Parser::parse_temporal_calendar_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(TimeZoneNumericUTCOffset, parse_time_zone_numeric_utc_offset)
__JS_ENUMERATE(TimeZoneNumericUTCOffset, parse_time_zone_numeric_utc_offset) \
__JS_ENUMERATE(CalendarName, parse_calendar_name)
Optional<ParseResult> parse_iso8601(Production production, StringView input)
{

View file

@ -52,8 +52,8 @@ enum class Production {
TemporalTimeZoneString,
TemporalYearMonthString,
TemporalZonedDateTimeString,
TemporalCalendarString,
TimeZoneNumericUTCOffset,
CalendarName,
};
Optional<ParseResult> parse_iso8601(Production, StringView);
@ -166,7 +166,6 @@ public:
[[nodiscard]] bool parse_temporal_time_zone_string();
[[nodiscard]] bool parse_temporal_year_month_string();
[[nodiscard]] bool parse_temporal_zoned_date_time_string();
[[nodiscard]] bool parse_temporal_calendar_string();
private:
struct State {