From 22644c64fa27df30fa166fdbe1aad4061269974a Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 5 Feb 2025 14:08:44 -0500 Subject: [PATCH] Another small tweak to tokens. --- luprex/cpp/core/luastack.hpp | 57 ++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 83715634..a0a7aad2 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -407,20 +407,20 @@ enum LuaTableType { //////////////////////////////////////////////////////////////////// struct LuaToken { -public: - // Convert a base36 number into a 64-bit unsigned integer. If the - // base36 number is not valid, or if it exceeds 64 bits, then return - // nullopt. Leading zeros are not allowed. Empty string gets parsed - // as zero. +private: + // Convert a base36 number into a token. If the base36 number is + // not valid, or if it exceeds 64 bits, then return maxint, ie, + // the invalid token. // - static constexpr std::optional parse_base36(const char *str) { + static constexpr uint64_t parse(std::string_view str) { uint64_t result = 0; uint64_t maxint = uint64_t(-1); + // Leading zeros are not allowed. - // Note: Token(0) is emitted as the empty string, not as '0'. - if ((*str == '0')) return std::nullopt; - while (*str) { - char c = *str++; + if ((!str.empty()) && (str[0]=='0')) return maxint; + + for (int i = 0; i < int(str.size()); i++) { + char c = str[i]; uint64_t digit = 0; if ((c >= '0') && (c <= '9')) { digit = uint64_t(c - '0'); @@ -429,14 +429,13 @@ public: } else if ((c >= 'A') && (c <= 'Z')) { digit = uint64_t(c - 'A' + 10); } else { - // Invalid digits return nullopt. - return std::nullopt; + return maxint; } // Multiply existing number by 36, then add the digit. // We have two checks to prevent integer overflow. - if (result > (maxint / 36)) return std::nullopt; + if (result > (maxint / 36)) return maxint; result *= 36; - if (digit > (maxint - result)) return std::nullopt; + if (digit > (maxint - result)) return maxint; result += digit; } return result; @@ -450,20 +449,22 @@ public: template LuaToken(T arg) = delete; - // Construct a token from a short string. + // Construct a token from a string. // - // This is meant to be used for constants in the code. - // It cannot be used for runtime creation of tokens. It is - // limited to 10-character tokens. - // - // Note: using 'throw' in a consteval gives largely the - // effect of a static_assert. + // If the string is not a valid token, then this initializes the + // token to the invalid token. // - consteval LuaToken(const char *str) : value(0) { - auto opt = parse_base36(str); - if (!opt.has_value()) throw "not a valid token"; - if (opt.value_or(0) > 3656158440062975) throw "token may have at most 10 characters"; - value = opt.value_or(0); + LuaToken(std::string_view s) : value(parse(s)) {} + + // Construct a token from a compile-time constant string. + // + // It appears that the code below throws an exception if the + // string is invalid. But in reality, since this function is + // consteval (evaluated at compile time), the error is + // generated during the compilation. + // + consteval LuaToken(const char *s) : value(parse(s)) { + if (is_invalid()) throw "Invalid token"; } // Construct a token from an int64. @@ -493,6 +494,10 @@ public: // void *voidvalue() const { return (void*)value; } + // Return true if it's the invalid token. + // + constexpr bool is_invalid() { return value == uint64_t(-1); } + // Convert the token to a string. // // The conversion to string consists of expressing the value