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(int64_t value) noexcept;
143 explicit Value(double value) noexcept;
144 explicit Value(const String_t& value) noexcept;
145 explicit Value(const char* value) noexcept;
146 explicit Value(internal::PageAddr_t value) noexcept;
147 explicit Value(Procedure&& value) noexcept;
148 explicit Value(List_t&& value) noexcept;
149 explicit Value(internal::Closure&& value) noexcept;
150 explicit Value(UserType&& value) noexcept;
151 explicit Value(Dict_t&& value) noexcept;
152 explicit Value(Ref_t ref) noexcept;
153
154 [[nodiscard]] ValueType valueType() const noexcept { return m_type; }
155 [[nodiscard]] bool isFunction() const noexcept
156 {
157 return m_type == ValueType::PageAddr || m_type == ValueType::Closure || m_type == ValueType::CProc ||
158 (m_type == ValueType::Reference && reference()->isFunction());
159 }
160 [[nodiscard]] bool isIndexable() const noexcept
161 {
162 return m_type == ValueType::List || m_type == ValueType::String;
163 }
164
165 [[nodiscard]] Number_t number() const { return std::get<Number_t>(m_value); }
166
167 [[nodiscard]] const String_t& string() const { return std::get<String_t>(m_value); }
168 [[nodiscard]] String_t& stringRef() { return std::get<String_t>(m_value); }
169
170
171 [[nodiscard]] const List_t& constList() const { return std::get<List_t>(m_value); }
172 [[nodiscard]] List_t& list() { return std::get<List_t>(m_value); }
173
174 [[nodiscard]] const UserType& usertype() const { return std::get<UserType>(m_value); }
175 [[nodiscard]] UserType& usertypeRef() { return std::get<UserType>(m_value); }
176
177 [[nodiscard]] const Dict_t& dict() const { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
178 [[nodiscard]] Dict_t& dictRef() { return *std::get<std::shared_ptr<Dict_t>>(m_value); }
179
180 [[nodiscard]] Ref_t reference() const { return std::get<Ref_t>(m_value); }
181
182 [[nodiscard]] internal::PageAddr_t pageAddr() const { return std::get<internal::PageAddr_t>(m_value); }
183
184 /**
185 * @brief Add an element to the list held by the value (if the value type is set to list)
186 *
187 * @param value
188 */
189 void push_back(const Value& value);
190
191 /**
192 * @brief Add an element to the list held by the value (if the value type is set to list)
193 *
194 * @param value
195 */
196 void push_back(Value&& value);
197
198 std::string toString(VM& vm, bool show_as_code = false) const noexcept;
199
200 friend ARK_API bool operator==(const Value& A, const Value& B) noexcept;
201 friend ARK_API_INLINE bool operator<(const Value& A, const Value& B) noexcept;
202 friend ARK_API bool operator!(const Value& A) noexcept;
203
204 friend class Ark::VM;
205 friend class Ark::internal::Closure;
206 friend class Ark::BytecodeReader;
207 friend struct std::hash<Ark::Value>;
208
209 private:
210 ValueType m_type;
211 Value_t m_value;
212
213 [[nodiscard]] constexpr uint8_t typeNum() const noexcept { return static_cast<uint8_t>(m_type); }
214
215 [[nodiscard]] const Procedure& proc() const { return std::get<Procedure>(m_value); }
216 [[nodiscard]] const internal::Closure& closure() const { return std::get<internal::Closure>(m_value); }
217 [[nodiscard]] internal::Closure& refClosure() { return std::get<internal::Closure>(m_value); }
218 };
219
220 inline bool operator<(const Value& A, const Value& B) noexcept
221 {
222 if (A.m_type != B.m_type)
223 return false;
224 return A.m_value < B.m_value;
225 }
226
227 inline bool operator!=(const Value& A, const Value& B) noexcept
228 {
229 return !(A == B);
230 }
231}
232
233namespace std
234{
235 [[nodiscard]] inline std::string to_string(const Ark::ValueType type) noexcept
236 {
237 const auto index = static_cast<std::underlying_type_t<Ark::ValueType>>(type);
238 return Ark::types_to_str[index];
239 }
240}
241
242template <>
243struct std::hash<Ark::Value::List_t>
244{
245 [[nodiscard]] std::size_t operator()(const Ark::Value::List_t& s) const noexcept
246 {
247 return std::hash<const Ark::Value*> {}(s.data());
248 }
249};
250
251template <>
252struct std::hash<std::shared_ptr<Ark::Value::Dict_t>>
253{
254 [[nodiscard]] std::size_t operator()(const std::shared_ptr<Ark::Value::Dict_t>& s) const noexcept
255 {
256 return std::hash<const Ark::Value::Dict_t*> {}(s.get());
257 }
258};
259
260template <>
261struct std::hash<Ark::Value>
262{
263 [[nodiscard]] std::size_t operator()(const Ark::Value& s) const noexcept
264 {
265 return std::hash<Ark::Value::Value_t> {}(s.m_value);
266 }
267};
268
269#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:60
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:40
const Dict_t & dict() const
Definition Value.hpp:177
const String_t & string() const
Definition Value.hpp:167
std::string String_t
Definition Value.hpp:96
double Number_t
Definition Value.hpp:95
const List_t & constList() const
Definition Value.hpp:171
internal::Closure & refClosure()
Definition Value.hpp:217
const Procedure & proc() const
Definition Value.hpp:215
String_t & stringRef()
Definition Value.hpp:168
List_t & list()
Definition Value.hpp:172
const internal::Closure & closure() const
Definition Value.hpp:216
Ref_t reference() const
Definition Value.hpp:180
bool isFunction() const noexcept
Definition Value.hpp:155
ValueType valueType() const noexcept
Definition Value.hpp:154
Number_t number() const
Definition Value.hpp:165
const UserType & usertype() const
Definition Value.hpp:174
bool isIndexable() const noexcept
Definition Value.hpp:160
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:182
std::vector< Value > List_t
Definition Value.hpp:97
UserType & usertypeRef()
Definition Value.hpp:175
Dict_t & dictRef()
Definition Value.hpp:178
Closure management.
Definition Closure.hpp:36
bool operator<(const Namespace &, const Namespace &)
Definition Namespace.hpp:27
uint16_t PageAddr_t
Definition Closure.hpp:27
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:227
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:235
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:263
std::size_t operator()(const Ark::Value::List_t &s) const noexcept
Definition Value.hpp:245
std::size_t operator()(const std::shared_ptr< Ark::Value::Dict_t > &s) const noexcept
Definition Value.hpp:254