Explorar o código

LibJS: Implement parsing of TemporalTimeString

Linus Groh %!s(int64=3) %!d(string=hai) anos
pai
achega
453c78215c

+ 1 - 0
Userland/Libraries/LibJS/Runtime/ErrorTypes.h

@@ -224,6 +224,7 @@
     M(TemporalInvalidPlainTimeLikeObject, "Invalid plain time-like object")                                                             \
     M(TemporalInvalidPlainYearMonth, "Invalid plain year month")                                                                        \
     M(TemporalInvalidTime, "Invalid time")                                                                                              \
+    M(TemporalInvalidTimeString, "Invalid time string '{}'")                                                                            \
     M(TemporalInvalidTimeZoneName, "Invalid time zone name")                                                                            \
     M(TemporalInvalidUnitRange, "Invalid unit range, {} is larger than {}")                                                             \
     M(TemporalInvalidZonedDateTimeOffset, "Invalid offset for the provided date and time in the current time zone")                     \

+ 9 - 4
Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp

@@ -1346,16 +1346,21 @@ ThrowCompletionOr<TemporalZonedDateTime> parse_temporal_relative_to_string(Globa
 }
 
 // 13.43 ParseTemporalTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimestring
-ThrowCompletionOr<TemporalTime> parse_temporal_time_string(GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
+ThrowCompletionOr<TemporalTime> parse_temporal_time_string(GlobalObject& global_object, String const& iso_string)
 {
+    auto& vm = global_object.vm();
+
     // 1. Assert: Type(isoString) is String.
 
     // 2. If isoString does not satisfy the syntax of a TemporalTimeString (see 13.33), then
-    // a. Throw a RangeError exception.
-    // TODO
+    auto parse_result = parse_iso8601(Production::TemporalTimeString, iso_string);
+    if (!parse_result.has_value()) {
+        // a. Throw a RangeError exception.
+        return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidTimeString, iso_string);
+    }
 
     // 3. Let result be ? ParseISODateTime(isoString).
-    auto result = TRY(parse_iso_date_time(global_object, {}));
+    auto result = TRY(parse_iso_date_time(global_object, *parse_result));
 
     // 4. Return the Record { [[Hour]]: result.[[Hour]], [[Minute]]: result.[[Minute]], [[Second]]: result.[[Second]], [[Millisecond]]: result.[[Millisecond]], [[Microsecond]]: result.[[Microsecond]], [[Nanosecond]]: result.[[Nanosecond]], [[Calendar]]: result.[[Calendar]] }.
     return TemporalTime { .hour = result.hour, .minute = result.minute, .second = result.second, .millisecond = result.millisecond, .microsecond = result.microsecond, .nanosecond = result.nanosecond, .calendar = move(result.calendar) };

+ 27 - 3
Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp

@@ -422,6 +422,17 @@ bool ISO8601Parser::parse_time_spec()
     return true;
 }
 
+// https://tc39.es/proposal-temporal/#prod-Time
+bool ISO8601Parser::parse_time()
+{
+    // Time :
+    //     TimeSpec TimeZone[opt]
+    if (!parse_time_spec())
+        return false;
+    (void)parse_time_zone();
+    return true;
+}
+
 // https://tc39.es/proposal-temporal/#prod-TimeSpecSeparator
 bool ISO8601Parser::parse_time_spec_separator()
 {
@@ -475,11 +486,24 @@ bool ISO8601Parser::parse_temporal_date_time_string()
     return parse_calendar_date_time();
 }
 
+// https://tc39.es/proposal-temporal/#prod-TemporalTimeString
+bool ISO8601Parser::parse_temporal_time_string()
+{
+    // TemporalTimeString :
+    //     Time
+    //     DateTime
+    // NOTE: Reverse order here because `Time` can be a subset of `DateTime`,
+    // so we'd not attempt to parse that but may not exhaust the input string.
+    return parse_date_time()
+        || parse_time();
+}
+
 }
 
-#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS                    \
-    __JS_ENUMERATE(TemporalDateString, parse_temporal_date_string) \
-    __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string)
+#define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS                             \
+    __JS_ENUMERATE(TemporalDateString, parse_temporal_date_string)          \
+    __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string) \
+    __JS_ENUMERATE(TemporalTimeString, parse_temporal_time_string)
 
 Optional<ParseResult> parse_iso8601(Production production, StringView input)
 {

+ 3 - 0
Userland/Libraries/LibJS/Runtime/Temporal/ISO8601.h

@@ -28,6 +28,7 @@ struct ParseResult {
 enum class Production {
     TemporalDateString,
     TemporalDateTimeString,
+    TemporalTimeString,
 };
 
 Optional<ParseResult> parse_iso8601(Production, StringView);
@@ -73,11 +74,13 @@ public:
     [[nodiscard]] bool parse_calendar_name();
     [[nodiscard]] bool parse_calendar();
     [[nodiscard]] bool parse_time_spec();
+    [[nodiscard]] bool parse_time();
     [[nodiscard]] bool parse_time_spec_separator();
     [[nodiscard]] bool parse_date_time();
     [[nodiscard]] bool parse_calendar_date_time();
     [[nodiscard]] bool parse_temporal_date_string();
     [[nodiscard]] bool parse_temporal_date_time_string();
+    [[nodiscard]] bool parse_temporal_time_string();
 
 private:
     struct State {

+ 1 - 2
Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withPlainTime.js

@@ -98,8 +98,7 @@ describe("correct behavior", () => {
         );
     });
 
-    // FIXME: This currently yields an incorrect result (epochNanoseconds = 1635984000000000000)
-    test.skip("from plain time string", () => {
+    test("from plain time string", () => {
         const plainDateTime = new Temporal.PlainDateTime(2021, 11, 4, 21, 16, 56, 100, 200, 300);
         const timeZone = new Temporal.TimeZone("UTC");
         const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);