Jelajahi Sumber

(#153) Improve error messages in chop_timestamp()

rexim 4 tahun lalu
induk
melakukan
5e5da7d067
1 mengubah file dengan 48 tambahan dan 22 penghapusan
  1. 48 22
      src/vodus_message.cpp

+ 48 - 22
src/vodus_message.cpp

@@ -169,40 +169,66 @@ struct Message_Buffer
     }
 };
 
-Timestamp chop_timestamp(String_View *input)
+Timestamp chop_timestamp(const char *input_filepath,
+                         size_t line_number,
+                         String_View *input)
 {
-    input->chop_by_delim('[');
-    auto raw_timestamp = input->chop_by_delim(']').trim();
+    const auto origin_input = *input;
 
-    // TODO(#153): message parsing should give more precise position of the syntax errors
-    auto hours = unwrap_or_panic(
-        raw_timestamp.chop_by_delim(':').trim().as_integer<uint64_t>(),
-        "Incorrect timestamp. Hours must be a number.");
+    auto syntax_panic = [&] (auto ...args) {
+        panic(input_filepath, ":", line_number, ":", input->data - origin_input.data + 1, ": ",
+              args...);
+    };
 
-    auto minutes = unwrap_or_panic(
-        raw_timestamp.chop_by_delim(':').trim().as_integer<uint64_t>(),
-        "Incorrect timestamp. Minutes must be a number.");
+    auto unwrap_or_syntax_panic = [&] (auto maybe, auto ...args) {
+        if (!maybe.has_value) {
+            syntax_panic(args...);
+        }
 
-    auto seconds = unwrap_or_panic(
-        raw_timestamp.chop_by_delim('.').trim().as_integer<uint64_t>(),
-        "Incorrect timestamp. Seconds must be a number");
+        return maybe.unwrap;
+    };
 
-    auto mseconds = 0;
+    auto is_digit = [](char x) -> bool {
+        return isdigit(x);
+    };
 
-    if (raw_timestamp.count > 0) {
+    auto expect_char = [&](char x) {
+        if (input->count == 0 || *input->data != x) {
+            syntax_panic("Expected `", x, "`");
+        }
+        input->chop(1);
+    };
+
+    auto expect_number = [&]() -> uint64_t {
+        return unwrap_or_syntax_panic(
+            input->chop_while(is_digit).as_integer<uint64_t>(),
+            "Expected number");
+    };
+
+    *input = input->trim_begin();
+
+    expect_char('[');
+    auto hours = expect_number();
+    expect_char(':');
+    auto minutes = expect_number();
+    expect_char(':');
+    auto seconds = expect_number();
+
+    Timestamp mseconds = 0;
+    if (input->count > 0 && *input->data == '.') {
+        input->chop(1);
+        auto mseconds_str = input->chop_while(is_digit);
         for (size_t i = 0; i < 3; ++i) {
-            if (i < raw_timestamp.count) {
-                if (isdigit(raw_timestamp.data[i])) {
-                    mseconds = mseconds * 10 + raw_timestamp.data[i] - '0';
-                } else {
-                    panic("Incorrect timestamp. Milliseconds must be a number");
-                }
+            if (i < mseconds_str.count) {
+                mseconds = mseconds * 10 + mseconds_str.data[i] - '0';
             } else {
                 mseconds *= 10;
             }
         }
     }
 
+    expect_char(']');
+
     return (hours * 60 * 60 + minutes * 60 + seconds) * 1000 + mseconds;
 }
 
@@ -237,7 +263,7 @@ size_t parse_messages_from_string_view(String_View input,
     {
         String_View message = input.chop_by_delim('\n');
 
-        (*messages)[messages_size].timestamp = chop_timestamp(&message);
+        (*messages)[messages_size].timestamp = chop_timestamp(input_filepath, line_number, &message);
         (*messages)[messages_size].nickname  = chop_nickname(&message);
         (*messages)[messages_size].message   = chop_message(&message);
         messages_size++;