Another small tweak to tokens.

This commit is contained in:
2025-02-05 14:08:44 -05:00
parent 742fc29ab3
commit 22644c64fa

View File

@@ -407,20 +407,20 @@ enum LuaTableType {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
struct LuaToken { struct LuaToken {
public: private:
// Convert a base36 number into a 64-bit unsigned integer. If the // Convert a base36 number into a token. If the base36 number is
// base36 number is not valid, or if it exceeds 64 bits, then return // not valid, or if it exceeds 64 bits, then return maxint, ie,
// nullopt. Leading zeros are not allowed. Empty string gets parsed // the invalid token.
// as zero.
// //
static constexpr std::optional<uint64_t> parse_base36(const char *str) { static constexpr uint64_t parse(std::string_view str) {
uint64_t result = 0; uint64_t result = 0;
uint64_t maxint = uint64_t(-1); uint64_t maxint = uint64_t(-1);
// Leading zeros are not allowed. // Leading zeros are not allowed.
// Note: Token(0) is emitted as the empty string, not as '0'. if ((!str.empty()) && (str[0]=='0')) return maxint;
if ((*str == '0')) return std::nullopt;
while (*str) { for (int i = 0; i < int(str.size()); i++) {
char c = *str++; char c = str[i];
uint64_t digit = 0; uint64_t digit = 0;
if ((c >= '0') && (c <= '9')) { if ((c >= '0') && (c <= '9')) {
digit = uint64_t(c - '0'); digit = uint64_t(c - '0');
@@ -429,14 +429,13 @@ public:
} else if ((c >= 'A') && (c <= 'Z')) { } else if ((c >= 'A') && (c <= 'Z')) {
digit = uint64_t(c - 'A' + 10); digit = uint64_t(c - 'A' + 10);
} else { } else {
// Invalid digits return nullopt. return maxint;
return std::nullopt;
} }
// Multiply existing number by 36, then add the digit. // Multiply existing number by 36, then add the digit.
// We have two checks to prevent integer overflow. // We have two checks to prevent integer overflow.
if (result > (maxint / 36)) return std::nullopt; if (result > (maxint / 36)) return maxint;
result *= 36; result *= 36;
if (digit > (maxint - result)) return std::nullopt; if (digit > (maxint - result)) return maxint;
result += digit; result += digit;
} }
return result; return result;
@@ -450,20 +449,22 @@ public:
template<class T> template<class T>
LuaToken(T arg) = delete; 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. // If the string is not a valid token, then this initializes the
// It cannot be used for runtime creation of tokens. It is // token to the invalid token.
// limited to 10-character tokens.
//
// Note: using 'throw' in a consteval gives largely the
// effect of a static_assert.
// //
consteval LuaToken(const char *str) : value(0) { LuaToken(std::string_view s) : value(parse(s)) {}
auto opt = parse_base36(str);
if (!opt.has_value()) throw "not a valid token"; // Construct a token from a compile-time constant string.
if (opt.value_or(0) > 3656158440062975) throw "token may have at most 10 characters"; //
value = opt.value_or(0); // 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. // Construct a token from an int64.
@@ -493,6 +494,10 @@ public:
// //
void *voidvalue() const { return (void*)value; } 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. // Convert the token to a string.
// //
// The conversion to string consists of expressing the value // The conversion to string consists of expressing the value