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 [[nodiscard]] internal::PageAddr_t pageAddr() const { return std::get<internal::PageAddr_t>(m_value); }
180
181 /**
182 * @brief Add an element to the list held by the value (if the value type is set to list)
183 *
184 * @param value
185 */
186 void push_back(const Value& value);
187
188 /**
189 * @brief Add an element to the list held by the value (if the value type is set to list)
190 *
191 * @param value
192 */
193 void push_back(Value&& value);
194
195 std::string toString(VM& vm, bool show_as_code = false) const noexcept;
196
197 friend ARK_API bool operator==(const Value& A, const Value& B) noexcept;
198 friend ARK_API_INLINE bool operator<(const Value& A, const Value& B) noexcept;
199 friend ARK_API_INLINE bool operator!(const Value& A) noexcept;
200
201 friend class Ark::VM;
202 friend class Ark::BytecodeReader;
203 friend struct std::hash<Ark::Value>;
204
205 private:
206 ValueType m_type;
207 Value_t m_value;
208
209 [[nodiscard]] constexpr uint8_t typeNum() const noexcept { return static_cast<uint8_t>(m_type); }
210
211 [[nodiscard]] const Procedure& proc() const { return std::get<Procedure>(m_value); }
212 [[nodiscard]] const internal::Closure& closure() const { return std::get<internal::Closure>(m_value); }
213 [[nodiscard]] internal::Closure& refClosure() { return std::get<internal::Closure>(m_value); }
214 };
215
216 inline bool operator<(const Value& A, const Value& B) noexcept
217 {
218 if (A.m_type != B.m_type)
219 return (A.typeNum() - B.typeNum()) < 0;
220 return A.m_value < B.m_value;
221 }
222
223 inline bool operator!=(const Value& A, const Value& B) noexcept
224 {
225 return !(A == B);
226 }
227
228 inline bool operator!(const Value& A) noexcept
229 {
230 switch (A.valueType())
231 {
232 case ValueType::List:
233 return A.constList().empty();
234
236 return A.number() == 0.0;
237
239 return A.string().empty();
240
241 case ValueType::User:
242 [[fallthrough]];
243 case ValueType::Nil:
244 [[fallthrough]];
245 case ValueType::False:
246 return true;
247
248 case ValueType::True:
249 [[fallthrough]];
250 default:
251 return false;
252 }
253 }
254}
255
256namespace std
257{
258 [[nodiscard]] inline std::string to_string(const Ark::ValueType type) noexcept
259 {
260 const auto index = static_cast<std::underlying_type_t<Ark::ValueType>>(type);
261 return Ark::types_to_str[index];
262 }
263}
264
265template <>
266struct std::hash<std::vector<Ark::Value>>
267{
268 [[nodiscard]] std::size_t operator()(const std::vector<Ark::Value>& s) const noexcept
269 {
270 return std::hash<const Ark::Value*> {}(s.data());
271 }
272};
273
274template <>
275struct std::hash<std::shared_ptr<Ark::Value::Dict_t>>
276{
277 [[nodiscard]] std::size_t operator()(const std::shared_ptr<Ark::Value::Dict_t>& s) const noexcept
278 {
279 return std::hash<const Ark::Value::Dict_t*> {}(s.get());
280 }
281};
282
283template <>
284struct std::hash<Ark::Value>
285{
286 [[nodiscard]] std::size_t operator()(const Ark::Value& s) const noexcept
287 {
288 return std::hash<Ark::Value::Value_t> {}(s.m_value);
289 }
290};
291
292#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:47
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:213
const Procedure & proc() const
Definition Value.hpp:211
String_t & stringRef()
Definition Value.hpp:165
List_t & list()
Definition Value.hpp:169
const internal::Closure & closure() const
Definition Value.hpp:212
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:179
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
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:228
const auto Nil
ArkScript Nil value.
const auto True
ArkScript True value.
bool operator!=(const Value &A, const Value &B) noexcept
Definition Value.hpp:223
ValueType
Definition Value.hpp:32
@ Any
Used only for typechecking.
STL namespace.
std::string to_string(const Ark::ValueType type) noexcept
Definition Value.hpp:258
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:286
std::size_t operator()(const std::shared_ptr< Ark::Value::Dict_t > &s) const noexcept
Definition Value.hpp:277
std::size_t operator()(const std::vector< Ark::Value > &s) const noexcept
Definition Value.hpp:268