ArkScript
A small, lisp-inspired, functional scripting language
Value.hpp
Go to the documentation of this file.
1/**
2 * @file Value.hpp
3 * @author Lex Plateau (lexplt.dev@gmail.com)
4 * @brief Default value type handled by the virtual machine
5 * @date 2024-04-20
6 *
7 * @copyright Copyright (c) 2020-2025
8 *
9 */
10
11#ifndef ARK_VM_VALUE_HPP
12#define ARK_VM_VALUE_HPP
13
14#include <vector>
15#include <variant>
16#include <string>
17#include <cinttypes>
18#include <array>
19#include <memory>
20#include <type_traits>
21
26
27namespace Ark
28{
29 // Order is important because we are doing some optimizations to check ranges
30 // of types based on their integer values.
31 enum class ValueType : unsigned
32 {
33 List = 0,
34 Number = 1,
35 String = 2,
36 PageAddr = 3,
37 CProc = 4,
38 Closure = 5,
39 User = 6,
40 Dict = 7,
41
42 Nil = 8,
43 True = 9,
44 False = 10,
45 Undefined = 11,
46 Reference = 12,
47 InstPtr = 13,
48
49 Any = 14 ///< Used only for typechecking
50 };
51
52 constexpr std::array types_to_str = {
53 "List",
54 "Number",
55 "String",
56 "Function",
57 "CProc",
58 "Closure",
59 "UserType",
60 "Dict",
61 "Nil",
62 "Bool",
63 "Bool",
64 "Undefined",
65 "Reference",
66 "InstPtr",
67 "Any"
68 };
69}
70
71template <>
72struct std::hash<Ark::ValueType>
73{
74 [[nodiscard]] std::size_t operator()(const Ark::ValueType& s) const noexcept
75 {
76 return std::hash<std::underlying_type_t<Ark::ValueType>> {}(static_cast<std::underlying_type_t<Ark::ValueType>>(s));
77 }
78};
79
80namespace Ark
81{
82 class VM;
83 class BytecodeReader;
84
85 namespace internal
86 {
87 class Dict;
88 }
89
91 {
92 public:
93 using Number_t = double;
94 using String_t = std::string;
95 using List_t = std::vector<Value>;
96 using Ref_t = Value*;
98
99 using Value_t = std::variant<
100 Number_t, // 8 bytes
101 String_t, // 32 bytes
102 internal::PageAddr_t, // 2 bytes
103 Procedure, // 32 bytes
104 internal::Closure, // 24 bytes
105 UserType, // 24 bytes
106 List_t, // 24 bytes
107 std::shared_ptr<Dict_t>, // 32 bytes
108 Ref_t // 8 bytes
109 >; // +8 bytes overhead
110 // total 40 bytes
111
112 /**
113 * @brief Construct a new Value object
114 *
115 */
116 Value() noexcept;
117
118 /**
119 * @brief Construct a new Value object
120 *
121 * @param type the value type which is going to be held
122 */
123 explicit Value(ValueType type) noexcept;
124
125 /**
126 * @brief Construct a new Value object
127 * @details Use at your own risks. Asking for a value type N and putting a non-matching value
128 * will result in errors at runtime.
129 *
130 * @tparam T
131 * @param type value type wanted
132 * @param value value needed
133 */
134 template <typename T>
135 Value(const ValueType type, T&& value) noexcept :
136 m_type(type), m_value(value)
137 {}
138
139 explicit Value(int value) noexcept;
140 explicit Value(double value) noexcept;
141 explicit Value(const String_t& value) noexcept;
142 explicit Value(const char* value) noexcept;
143 explicit Value(internal::PageAddr_t value) noexcept;
144 explicit Value(Procedure&& value) noexcept;
145 explicit Value(List_t&& value) noexcept;
146 explicit Value(internal::Closure&& value) noexcept;
147 explicit Value(UserType&& value) noexcept;
148 explicit Value(Dict_t&& value) noexcept;
149 explicit Value(Ref_t ref) noexcept;
150
151 [[nodiscard]] ValueType valueType() const noexcept { return m_type; }
152 [[nodiscard]] bool isFunction() const noexcept
153 {
154 return m_type == ValueType::PageAddr || m_type == ValueType::Closure || m_type == ValueType::CProc ||
155 (m_type == ValueType::Reference && reference()->isFunction());
156 }
157 [[nodiscard]] bool isIndexable() const noexcept
158 {
159 return m_type == ValueType::List || m_type == ValueType::String;
160 }
161
162 [[nodiscard]] Number_t number() const { return std::get<Number_t>(m_value); }
163
164 [[nodiscard]] const String_t& string() const { return std::get<String_t>(m_value); }
165 [[nodiscard]] String_t& stringRef() { return std::get<String_t>(m_value); }
166
167
168 [[nodiscard]] const List_t& constList() const { return std::get<List_t>(m_value); }
169 [[nodiscard]] List_t& list() { return std::get<List_t>(m_value); }
170
171 [[nodiscard]] const UserType& usertype() const { return std::get<UserType>(m_value); }
172 [[nodiscard]] UserType& usertypeRef() { return std::get<UserType>(m_value); }
173
174 [[nodiscard]] const Dict_t& dict() const { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
175 [[nodiscard]] Dict_t& dictRef() { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
176
177 [[nodiscard]] Ref_t reference() const { return std::get<Ref_t>(m_value); }
178
179 /**
180 * @brief Add an element to the list held by the value (if the value type is set to list)
181 *
182 * @param value
183 */
184 void push_back(const Value& value);
185
186 /**
187 * @brief Add an element to the list held by the value (if the value type is set to list)
188 *
189 * @param value
190 */
191 void push_back(Value&& value);
192
193 std::string toString(VM& vm) const noexcept;
194
195 friend ARK_API bool operator==(const Value& A, const Value& B) noexcept;
196 friend ARK_API_INLINE bool operator<(const Value& A, const Value& B) noexcept;
197 friend ARK_API_INLINE bool operator!(const Value& A) noexcept;
198
199 friend class Ark::VM;
201 friend struct std::hash<Ark::Value>;
202
203 private:
204 ValueType m_type;
205 Value_t m_value;
206
207 [[nodiscard]] constexpr uint8_t typeNum() const noexcept { return static_cast<uint8_t>(m_type); }
208
209 [[nodiscard]] internal::PageAddr_t pageAddr() const { return std::get<internal::PageAddr_t>(m_value); }
210 [[nodiscard]] const Procedure& proc() const { return std::get<Procedure>(m_value); }
211 [[nodiscard]] const internal::Closure& closure() const { return std::get<internal::Closure>(m_value); }
212 [[nodiscard]] internal::Closure& refClosure() { return std::get<internal::Closure>(m_value); }
213 };
214
215 inline bool operator<(const Value& A, const Value& B) noexcept
216 {
217 if (A.m_type != B.m_type)
218 return (A.typeNum() - B.typeNum()) < 0;
219 return A.m_value < B.m_value;
220 }
221
222 inline bool operator!=(const Value& A, const Value& B) noexcept
223 {
224 return !(A == B);
225 }
226
227 inline bool operator!(const Value& A) noexcept
228 {
229 switch (A.valueType())
230 {
231 case ValueType::List:
232 return A.constList().empty();
233
235 return A.number() == 0.0;
236
238 return A.string().empty();
239
240 case ValueType::User:
241 [[fallthrough]];
242 case ValueType::Nil:
243 [[fallthrough]];
244 case ValueType::False:
245 return true;
246
247 case ValueType::True:
248 [[fallthrough]];
249 default:
250 return false;
251 }
252 }
253}
254
255namespace std
256{
257 [[nodiscard]] inline std::string to_string(const Ark::ValueType type) noexcept
258 {
259 const auto index = static_cast<std::underlying_type_t<Ark::ValueType>>(type);
260 return Ark::types_to_str[index];
261 }
262}
263
264template <>
265struct std::hash<std::vector<Ark::Value>>
266{
267 [[nodiscard]] std::size_t operator()(const std::vector<Ark::Value>& s) const noexcept
268 {
269 return std::hash<const Ark::Value*> {}(s.data());
270 }
271};
272
273template <>
274struct std::hash<std::shared_ptr<Ark::Value::Dict_t>>
275{
276 [[nodiscard]] std::size_t operator()(const std::shared_ptr<Ark::Value::Dict_t>& s) const noexcept
277 {
278 return std::hash<const Ark::Value::Dict_t*> {}(s.get());
279 }
280};
281
282template <>
283struct std::hash<Ark::Value>
284{
285 [[nodiscard]] std::size_t operator()(const Ark::Value& s) const noexcept
286 {
287 return std::hash<Ark::Value::Value_t> {}(s.m_value);
288 }
289};
290
291#endif
Subtype of the value type, handling closures.
#define ARK_API
Definition Module.hpp:22
ArkScript configuration macros.
#define ARK_API_INLINE
Definition Platform.hpp:50
Wrapper object for user-defined functions.
Subtype of the value, capable of handling any C++ type.
This class is just a helper to.
Storage class to hold custom functions.
Definition Procedure.hpp:26
A class to be use C++ objects in ArkScript.
Definition UserType.hpp:48
The ArkScript virtual machine, executing ArkScript bytecode.
Definition VM.hpp:46
const Dict_t & dict() const
Definition Value.hpp:174
const String_t & string() const
Definition Value.hpp:164
std::string String_t
Definition Value.hpp:94
double Number_t
Definition Value.hpp:93
const List_t & constList() const
Definition Value.hpp:168
internal::Closure & refClosure()
Definition Value.hpp:212
const Procedure & proc() const
Definition Value.hpp:210
String_t & stringRef()
Definition Value.hpp:165
List_t & list()
Definition Value.hpp:169
const internal::Closure & closure() const
Definition Value.hpp:211
Ref_t reference() const
Definition Value.hpp:177
bool isFunction() const noexcept
Definition Value.hpp:152
ValueType valueType() const noexcept
Definition Value.hpp:151
Number_t number() const
Definition Value.hpp:162
const UserType & usertype() const
Definition Value.hpp:171
bool isIndexable() const noexcept
Definition Value.hpp:157
std::variant< Number_t, String_t, internal::PageAddr_t, Procedure, internal::Closure, UserType, List_t, std::shared_ptr< Dict_t >, Ref_t > Value_t
Definition Value.hpp:99
internal::PageAddr_t pageAddr() const
Definition Value.hpp:209
std::vector< Value > List_t
Definition Value.hpp:95
UserType & usertypeRef()
Definition Value.hpp:172
Dict_t & dictRef()
Definition Value.hpp:175
Closure management.
Definition Closure.hpp:35
bool operator<(const Namespace &, const Namespace &)
Definition Namespace.hpp:27
bool operator==(const Namespace &A, const Namespace &B)
Definition Namespace.hpp:21
uint16_t PageAddr_t
Definition Closure.hpp:26
constexpr std::array types_to_str
Definition Value.hpp:52
const auto False
ArkScript False value.
bool operator!(const Value &A) noexcept
Definition Value.hpp:227
const auto Nil
ArkScript Nil value.
const auto True
ArkScript True value.
bool operator!=(const Value &A, const Value &B) noexcept
Definition Value.hpp:222
ValueType
Definition Value.hpp:32
@ Any
Used only for typechecking.
STL namespace.
std::string to_string(const Ark::ValueType type) noexcept
Definition Value.hpp:257
std::size_t operator()(const Ark::ValueType &s) const noexcept
Definition Value.hpp:74
std::size_t operator()(const Ark::Value &s) const noexcept
Definition Value.hpp:285
std::size_t operator()(const std::shared_ptr< Ark::Value::Dict_t > &s) const noexcept
Definition Value.hpp:276
std::size_t operator()(const std::vector< Ark::Value > &s) const noexcept
Definition Value.hpp:267