ArkScript
A small, lisp-inspired, functional scripting language
Value.hpp
Go to the documentation of this file.
1/**
2 * @file Value.hpp
3 * @author Lexy 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-2026
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 Garbage = 14, ///< Used to signal a value was used and can/should be collected and removed from the stack
50 Any = 15 ///< Used only for typechecking
51 };
52
53 constexpr std::array types_to_str = {
54 "List",
55 "Number",
56 "String",
57 "Function",
58 "CProc",
59 "Closure",
60 "UserType",
61 "Dict",
62 "Nil",
63 "Bool",
64 "Bool",
65 "Undefined",
66 "Reference",
67 "InstPtr",
68 "Garbage",
69 "Any"
70 };
71}
72
73template <>
74struct std::hash<Ark::ValueType>
75{
76 [[nodiscard]] std::size_t operator()(const Ark::ValueType& s) const noexcept
77 {
78 return std::hash<std::underlying_type_t<Ark::ValueType>> {}(static_cast<std::underlying_type_t<Ark::ValueType>>(s));
79 }
80};
81
82namespace Ark
83{
84 class VM;
85 class BytecodeReader;
86
87 namespace internal
88 {
89 class Dict;
90 }
91
93 {
94 public:
95 using Number_t = double;
96 using String_t = std::string;
97 using List_t = std::vector<Value>;
98 using Ref_t = Value*;
100
101 using Value_t = std::variant<
102 Number_t, // 8 bytes
103 String_t, // 32 bytes
104 internal::PageAddr_t, // 2 bytes
105 Procedure, // 32 bytes
106 internal::Closure, // 24 bytes
107 UserType, // 24 bytes
108 List_t, // 24 bytes
109 std::shared_ptr<Dict_t>, // 32 bytes
110 Ref_t // 8 bytes
111 >; // +8 bytes overhead
112 // total 40 bytes
113
114 /**
115 * @brief Construct a new Value object
116 *
117 */
118 Value() noexcept;
119
120 /**
121 * @brief Construct a new Value object
122 *
123 * @param type the value type which is going to be held
124 */
125 explicit Value(ValueType type) noexcept;
126
127 /**
128 * @brief Construct a new Value object
129 * @details Use at your own risks. Asking for a value type N and putting a non-matching value
130 * will result in errors at runtime.
131 *
132 * @tparam T
133 * @param type value type wanted
134 * @param value value needed
135 */
136 template <typename T>
137 Value(const ValueType type, T&& value) noexcept :
138 m_type(type), m_value(value)
139 {}
140
141 explicit Value(int value) noexcept;
142 explicit Value(double value) noexcept;
143 explicit Value(const String_t& value) noexcept;
144 explicit Value(const char* value) noexcept;
145 explicit Value(internal::PageAddr_t value) noexcept;
146 explicit Value(Procedure&& value) noexcept;
147 explicit Value(List_t&& value) noexcept;
148 explicit Value(internal::Closure&& value) noexcept;
149 explicit Value(UserType&& value) noexcept;
150 explicit Value(Dict_t&& value) noexcept;
151 explicit Value(Ref_t ref) noexcept;
152
153 [[nodiscard]] ValueType valueType() const noexcept { return m_type; }
154 [[nodiscard]] bool isFunction() const noexcept
155 {
156 return m_type == ValueType::PageAddr || m_type == ValueType::Closure || m_type == ValueType::CProc ||
157 (m_type == ValueType::Reference && reference()->isFunction());
158 }
159 [[nodiscard]] bool isIndexable() const noexcept
160 {
161 return m_type == ValueType::List || m_type == ValueType::String;
162 }
163
164 [[nodiscard]] Number_t number() const { return std::get<Number_t>(m_value); }
165
166 [[nodiscard]] const String_t& string() const { return std::get<String_t>(m_value); }
167 [[nodiscard]] String_t& stringRef() { return std::get<String_t>(m_value); }
168
169
170 [[nodiscard]] const List_t& constList() const { return std::get<List_t>(m_value); }
171 [[nodiscard]] List_t& list() { return std::get<List_t>(m_value); }
172
173 [[nodiscard]] const UserType& usertype() const { return std::get<UserType>(m_value); }
174 [[nodiscard]] UserType& usertypeRef() { return std::get<UserType>(m_value); }
175
176 [[nodiscard]] const Dict_t& dict() const { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
177 [[nodiscard]] Dict_t& dictRef() { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
178
179 [[nodiscard]] Ref_t reference() const { return std::get<Ref_t>(m_value); }
180
181 [[nodiscard]] internal::PageAddr_t pageAddr() const { return std::get<internal::PageAddr_t>(m_value); }
182
183 /**
184 * @brief Add an element to the list held by the value (if the value type is set to list)
185 *
186 * @param value
187 */
188 void push_back(const Value& value);
189
190 /**
191 * @brief Add an element to the list held by the value (if the value type is set to list)
192 *
193 * @param value
194 */
195 void push_back(Value&& value);
196
197 std::string toString(VM& vm, bool show_as_code = false) const noexcept;
198
199 friend ARK_API bool operator==(const Value& A, const Value& B) noexcept;
200 friend ARK_API_INLINE bool operator<(const Value& A, const Value& B) noexcept;
201 friend ARK_API bool operator!(const Value& A) noexcept;
202
203 friend class Ark::VM;
204 friend class Ark::BytecodeReader;
205 friend struct std::hash<Ark::Value>;
206
207 private:
208 ValueType m_type;
209 Value_t m_value;
210
211 [[nodiscard]] constexpr uint8_t typeNum() const noexcept { return static_cast<uint8_t>(m_type); }
212
213 [[nodiscard]] const Procedure& proc() const { return std::get<Procedure>(m_value); }
214 [[nodiscard]] const internal::Closure& closure() const { return std::get<internal::Closure>(m_value); }
215 [[nodiscard]] internal::Closure& refClosure() { return std::get<internal::Closure>(m_value); }
216 };
217
218 inline bool operator<(const Value& A, const Value& B) noexcept
219 {
220 if (A.m_type != B.m_type)
221 return false;
222 return A.m_value < B.m_value;
223 }
224
225 inline bool operator!=(const Value& A, const Value& B) noexcept
226 {
227 return !(A == B);
228 }
229}
230
231namespace std
232{
233 [[nodiscard]] inline std::string to_string(const Ark::ValueType type) noexcept
234 {
235 const auto index = static_cast<std::underlying_type_t<Ark::ValueType>>(type);
236 return Ark::types_to_str[index];
237 }
238}
239
240template <>
241struct std::hash<std::vector<Ark::Value>>
242{
243 [[nodiscard]] std::size_t operator()(const std::vector<Ark::Value>& s) const noexcept
244 {
245 return std::hash<const Ark::Value*> {}(s.data());
246 }
247};
248
249template <>
250struct std::hash<std::shared_ptr<Ark::Value::Dict_t>>
251{
252 [[nodiscard]] std::size_t operator()(const std::shared_ptr<Ark::Value::Dict_t>& s) const noexcept
253 {
254 return std::hash<const Ark::Value::Dict_t*> {}(s.get());
255 }
256};
257
258template <>
259struct std::hash<Ark::Value>
260{
261 [[nodiscard]] std::size_t operator()(const Ark::Value& s) const noexcept
262 {
263 return std::hash<Ark::Value::Value_t> {}(s.m_value);
264 }
265};
266
267#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:48
const Dict_t & dict() const
Definition Value.hpp:176
const String_t & string() const
Definition Value.hpp:166
std::string String_t
Definition Value.hpp:96
double Number_t
Definition Value.hpp:95
const List_t & constList() const
Definition Value.hpp:170
internal::Closure & refClosure()
Definition Value.hpp:215
const Procedure & proc() const
Definition Value.hpp:213
String_t & stringRef()
Definition Value.hpp:167
List_t & list()
Definition Value.hpp:171
const internal::Closure & closure() const
Definition Value.hpp:214
Ref_t reference() const
Definition Value.hpp:179
bool isFunction() const noexcept
Definition Value.hpp:154
ValueType valueType() const noexcept
Definition Value.hpp:153
Number_t number() const
Definition Value.hpp:164
const UserType & usertype() const
Definition Value.hpp:173
bool isIndexable() const noexcept
Definition Value.hpp:159
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:101
internal::PageAddr_t pageAddr() const
Definition Value.hpp:181
std::vector< Value > List_t
Definition Value.hpp:97
UserType & usertypeRef()
Definition Value.hpp:174
Dict_t & dictRef()
Definition Value.hpp:177
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:53
const auto False
ArkScript False value.
const auto Nil
ArkScript Nil value.
const auto True
ArkScript True value.
bool operator!=(const Value &A, const Value &B) noexcept
Definition Value.hpp:225
ValueType
Definition Value.hpp:32
@ Garbage
Used to signal a value was used and can/should be collected and removed from the stack.
@ Any
Used only for typechecking.
STL namespace.
std::string to_string(const Ark::ValueType type) noexcept
Definition Value.hpp:233
std::size_t operator()(const Ark::ValueType &s) const noexcept
Definition Value.hpp:76
std::size_t operator()(const Ark::Value &s) const noexcept
Definition Value.hpp:261
std::size_t operator()(const std::shared_ptr< Ark::Value::Dict_t > &s) const noexcept
Definition Value.hpp:252
std::size_t operator()(const std::vector< Ark::Value > &s) const noexcept
Definition Value.hpp:243