#pragma once #include #include #include #include #include #include #include namespace sliger { enum class ValueType { Null, Int, Bool, String, Ptr, }; inline auto value_type_to_string(ValueType type) -> std::string { switch (type) { case ValueType::Null: return "Null"; case ValueType::Int: return "Int"; case ValueType::Bool: return "Bool"; case ValueType::String: return "String"; case ValueType::Ptr: return "Ptr"; } std::unreachable(); } class Values; struct Null { }; struct Int { int32_t value; }; struct Bool { bool value; }; struct String { std::string value; }; struct Ptr { uint32_t value; }; // clang-format off template struct ValueTypeToType { }; template <> struct ValueTypeToType { using Type = Null; }; template <> struct ValueTypeToType { using Type = Int; }; template <> struct ValueTypeToType { using Type = Bool; }; template <> struct ValueTypeToType { using Type = String; }; template <> struct ValueTypeToType { using Type = Ptr; }; // clang-format on class Value { public: Value(Null&& value) : m_type(ValueType::Null) , value(value) { } Value(Int&& value) : m_type(ValueType::Int) , value(value) { } Value(Bool&& value) : m_type(ValueType::Bool) , value(value) { } Value(String&& value) : m_type(ValueType::String) , value(value) { } Value(Ptr&& value) : m_type(ValueType::Ptr) , value(value) { } inline auto type() const -> ValueType { return m_type; }; template inline auto as() -> ValueTypeToType::Type& { try { return std::get::Type>(value); } catch (const std::bad_variant_access& ex) { std::cerr << std::format("error: tried to unwrap {} as {}\n", value_type_to_string(this->m_type), value_type_to_string(VT)); std::exit(1); } } template inline auto as() const -> const ValueTypeToType::Type& { try { return std::get::Type>(value); } catch (const std::bad_variant_access& ex) { std::cerr << std::format("error: tried to unwrap {} as {}\n", value_type_to_string(this->m_type), value_type_to_string(VT)); std::exit(1); } } // clang-format off inline auto as_null() -> Null& { return as(); } inline auto as_int() -> Int& { return as(); } inline auto as_bool() -> Bool& { return as(); } inline auto as_string() -> String& { return as(); } inline auto as_ptr() -> Ptr& { return as(); } inline auto as_null() const -> const Null& { return as(); } inline auto as_int() const -> const Int& { return as(); } inline auto as_bool() const -> const Bool& { return as(); } inline auto as_string() const -> const String& { return as(); } inline auto as_ptr() const -> const Ptr& { return as(); } // clang-format on inline auto to_string() const -> std::string { switch (this->m_type) { case ValueType::Null: return "null"; case ValueType::Int: return std::to_string(as_int().value); case ValueType::Bool: return as_bool().value ? "true" : "false"; case ValueType::String: return std::format("\"{}\"", as_string().value); case ValueType::Ptr: return std::to_string(as_ptr().value); } std::unreachable(); } inline auto to_repr_string() const -> std::string { return std::format( "{}({})", value_type_to_string(this->m_type), to_string()); } private: ValueType m_type; std::variant value; }; }