So I've been trying to implement a Token class in C++. At first I wanted to use a simple enum class to store the Kind of the Token.
But then I came across a problem. Some Tokens (such as keywords, identifiers, numeric literals, etc.) would need to store an external value along with their Kind, while others wouldn't. I tried to get around the problem by creating a struct for each Token Kind and then using an std::variant for the Kind type. Here's the code I ended up with:
#include <cstdint>
#include <string>
#include <variant>
class Token
{
public:
struct Num
{
std::int64_t val;
};
struct Float
{
double val;
};
struct String
{
std::string val;
};
struct Keyword
{
std::string val;
};
struct Identity
{
std::string val;
};
struct LParen
{
};
struct RParen
{
};
struct LBrace
{
};
struct RBrace
{
};
struct LBracket
{
};
struct RBracket
{
};
struct Semicolon
{
};
struct Colon
{
};
struct Dot
{
};
struct Operator
{
enum class Name : std::uint_fast8_t
{
Add = 0,
Sub,
Mult,
Div,
Pow
};
Name val;
};
struct EndOfFile
{
};
struct Invalid
{
};
using Kind =
std::variant<Num, Float, String, Keyword, Identity, LParen, RParen, LBrace, RBrace,
LBracket, RBracket, Semicolon, Colon, Dot, Operator, EndOfFile, Invalid>;
struct Span
{
std::uint_fast64_t row;
std::uint_fast64_t col;
};
Token() = delete;
Token(Kind const& kind, Span span) : m_kind(kind), m_span(span) {}
auto kind() const -> Kind { return m_kind; }
auto span() const -> Span { return m_span; }
auto isEOF() const -> bool { return std::holds_alternative<EndOfFile>(m_kind); }
auto isValid() const -> bool { return !std::holds_alternative<Invalid>(m_kind); }
private:
Kind m_kind;
Span m_span;
};
Is this a valid way to do what I originally intended to do? Or is it way too complicated and there's a simpler way?