[tint][utils] Abstract absl dependency

Move the use of absl::from_chars() in the WGSL parser into utils, behind
a new abstraction.

If we decide one day to drop the absl dependency, we can reimplement
this function. It also consolodates tint source_set dependencies on absl
to the common utils module.

No new tests as this is a thin wrapper around an existing
implementation, and the single use is already heavily tested for parsing.

Change-Id: I1ce5d68857e81299d1c97322b0ec28f0a83a31b7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/134581
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 7f2dd00..3305b8d 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -251,6 +251,8 @@
     "utils/hashset.h",
     "utils/map.h",
     "utils/math.h",
+    "utils/parse_num.cc",
+    "utils/parse_num.h",
     "utils/predicates.h",
     "utils/scoped_assignment.h",
     "utils/slice.h",
@@ -273,6 +275,8 @@
   } else {
     sources += [ "diagnostic/printer_other.cc" ]
   }
+
+  deps = [ ":abseil" ]
 }
 
 libtint_source_set("libtint_clone_context_hdrs") {
@@ -1064,7 +1068,6 @@
   ]
 
   deps = [
-    ":abseil",
     ":libtint_ast_src",
     ":libtint_builtins_src",
     ":libtint_program_src",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 2e097f4..d8fe22a 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -536,6 +536,8 @@
   utils/hashset.h
   utils/map.h
   utils/math.h
+  utils/parse_num.cc
+  utils/parse_num.h
   utils/predicates.h
   utils/scoped_assignment.h
   utils/slice.h
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 1e1dad4..4ed4786 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -25,9 +25,9 @@
 #include <type_traits>
 #include <utility>
 
-#include "absl/strings/charconv.h"
 #include "src/tint/debug.h"
 #include "src/tint/number.h"
+#include "src/tint/utils/parse_num.h"
 #include "src/tint/utils/unicode.h"
 
 namespace tint::reader::wgsl {
@@ -414,12 +414,13 @@
         end_ptr = &at(length() - 1) + 1;
     }
 
-    double value = 0;
-    auto ret = absl::from_chars(&at(start), end_ptr, value);
-    bool overflow = ret.ec != std::errc();
+    auto ret = utils::ParseDouble(std::string_view(&at(start), end - start));
+    double value = ret ? ret.Get() : 0.0;
+    bool overflow = !ret && ret.Failure() == utils::ParseNumberError::kResultOutOfRange;
 
-    // Value didn't fit in a double, check for underflow as that is 0.0 in WGSL and not an error.
-    if (ret.ec == std::errc::result_out_of_range) {
+    // If the value didn't fit in a double, check for underflow as that is 0.0 in WGSL and not an
+    // error.
+    if (overflow) {
         // The exponent is negative, so treat as underflow
         if (negative_exponent) {
             overflow = false;
@@ -446,7 +447,6 @@
         }
     }
 
-    TINT_ASSERT(Reader, end_ptr == ret.ptr);
     advance(end - start);
 
     if (has_f_suffix) {
diff --git a/src/tint/utils/parse_num.cc b/src/tint/utils/parse_num.cc
new file mode 100644
index 0000000..fa04344
--- /dev/null
+++ b/src/tint/utils/parse_num.cc
@@ -0,0 +1,98 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/utils/parse_num.h"
+
+#include <charconv>
+
+#include "absl/strings/charconv.h"
+
+namespace tint::utils {
+
+namespace {
+
+template <typename T>
+Result<T, ParseNumberError> Parse(std::string_view number) {
+    T val = 0;
+    if constexpr (std::is_floating_point_v<T>) {
+        auto result = absl::from_chars(number.data(), number.data() + number.size(), val);
+        if (result.ec == std::errc::result_out_of_range) {
+            return ParseNumberError::kResultOutOfRange;
+        }
+        if (result.ec != std::errc() || result.ptr != number.data() + number.size()) {
+            return ParseNumberError::kUnparsable;
+        }
+    } else {
+        auto result = std::from_chars(number.data(), number.data() + number.size(), val);
+        if (result.ec == std::errc::result_out_of_range) {
+            return ParseNumberError::kResultOutOfRange;
+        }
+        if (result.ec != std::errc() || result.ptr != number.data() + number.size()) {
+            return ParseNumberError::kUnparsable;
+        }
+    }
+    return val;
+}
+
+}  // namespace
+
+Result<float, ParseNumberError> ParseFloat(std::string_view str) {
+    return Parse<float>(str);
+}
+
+Result<double, ParseNumberError> ParseDouble(std::string_view str) {
+    return Parse<double>(str);
+}
+
+Result<int, ParseNumberError> ParseInt(std::string_view str) {
+    return Parse<int>(str);
+}
+
+Result<unsigned int, ParseNumberError> ParseUint(std::string_view str) {
+    return Parse<unsigned int>(str);
+}
+
+Result<int64_t, ParseNumberError> ParseInt64(std::string_view str) {
+    return Parse<int64_t>(str);
+}
+
+Result<uint64_t, ParseNumberError> ParseUint64(std::string_view str) {
+    return Parse<uint64_t>(str);
+}
+
+Result<int32_t, ParseNumberError> ParseInt32(std::string_view str) {
+    return Parse<int32_t>(str);
+}
+
+Result<uint32_t, ParseNumberError> ParseUint32(std::string_view str) {
+    return Parse<uint32_t>(str);
+}
+
+Result<int16_t, ParseNumberError> ParseInt16(std::string_view str) {
+    return Parse<int16_t>(str);
+}
+
+Result<uint16_t, ParseNumberError> ParseUint16(std::string_view str) {
+    return Parse<uint16_t>(str);
+}
+
+Result<int8_t, ParseNumberError> ParseInt8(std::string_view str) {
+    return Parse<int8_t>(str);
+}
+
+Result<uint8_t, ParseNumberError> ParseUint8(std::string_view str) {
+    return Parse<uint8_t>(str);
+}
+
+}  // namespace tint::utils
diff --git a/src/tint/utils/parse_num.h b/src/tint/utils/parse_num.h
new file mode 100644
index 0000000..6d4fcb4
--- /dev/null
+++ b/src/tint/utils/parse_num.h
@@ -0,0 +1,126 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_UTILS_PARSE_NUM_H_
+#define SRC_TINT_UTILS_PARSE_NUM_H_
+
+#include <optional>
+#include <string>
+
+#include "src/tint/utils/result.h"
+
+namespace tint::utils {
+
+/// Error returned by the number parsing functions
+enum class ParseNumberError {
+    /// The number was unparsable
+    kUnparsable,
+    /// The parsed number is not representable by the target datatype
+    kResultOutOfRange,
+};
+
+/// @param str the string
+/// @returns the string @p str parsed as a float
+Result<float, ParseNumberError> ParseFloat(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a double
+Result<double, ParseNumberError> ParseDouble(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a int
+Result<int, ParseNumberError> ParseInt(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a unsigned int
+Result<unsigned int, ParseNumberError> ParseUint(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a int64_t
+Result<int64_t, ParseNumberError> ParseInt64(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a uint64_t
+Result<uint64_t, ParseNumberError> ParseUint64(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a int32_t
+Result<int32_t, ParseNumberError> ParseInt32(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a uint32_t
+Result<uint32_t, ParseNumberError> ParseUint32(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a int16_t
+Result<int16_t, ParseNumberError> ParseInt16(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a uint16_t
+Result<uint16_t, ParseNumberError> ParseUint16(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a int8_t
+Result<int8_t, ParseNumberError> ParseInt8(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a uint8_t
+Result<uint8_t, ParseNumberError> ParseUint8(std::string_view str);
+
+/// @param str the string
+/// @returns the string @p str parsed as a the number @p T
+template <typename T>
+inline Result<T, ParseNumberError> ParseNumber(std::string_view str) {
+    if constexpr (std::is_same_v<T, float>) {
+        return ParseFloat(str);
+    }
+    if constexpr (std::is_same_v<T, double>) {
+        return ParseDouble(str);
+    }
+    if constexpr (std::is_same_v<T, int>) {
+        return ParseInt(str);
+    }
+    if constexpr (std::is_same_v<T, unsigned int>) {
+        return ParseUint(str);
+    }
+    if constexpr (std::is_same_v<T, int64_t>) {
+        return ParseInt64(str);
+    }
+    if constexpr (std::is_same_v<T, uint64_t>) {
+        return ParseUint64(str);
+    }
+    if constexpr (std::is_same_v<T, int32_t>) {
+        return ParseInt32(str);
+    }
+    if constexpr (std::is_same_v<T, uint32_t>) {
+        return ParseUint32(str);
+    }
+    if constexpr (std::is_same_v<T, int16_t>) {
+        return ParseInt16(str);
+    }
+    if constexpr (std::is_same_v<T, uint16_t>) {
+        return ParseUint16(str);
+    }
+    if constexpr (std::is_same_v<T, int8_t>) {
+        return ParseInt8(str);
+    }
+    if constexpr (std::is_same_v<T, uint8_t>) {
+        return ParseUint8(str);
+    }
+    return ParseNumberError::kUnparsable;
+}
+
+}  // namespace tint::utils
+
+#endif  // SRC_TINT_UTILS_PARSE_NUM_H_