1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17#pragma once
18
19#include "C4Id.h"
20#include "C4AulScriptStrict.h"
21
22#include <concepts>
23#include <cstdint>
24#include <optional>
25#include <string>
26#include <type_traits>
27
28// class declarations
29class C4Value;
30class C4Object;
31class C4String;
32class C4ValueArray;
33class C4ValueHash;
34class C4ValueContainer;
35
36// C4Value type
37enum C4V_Type
38{
39 C4V_Any = 0, // unknown / no type
40 C4V_Int = 1, // Integer
41 C4V_Bool = 2, // Boolean
42 C4V_C4ID = 3, // C4ID
43 C4V_C4Object = 4, // Pointer on Object
44
45 C4V_String = 5, // String
46
47 C4V_Array = 6, // pointer on array of values
48 C4V_Map = 7, // pointer on map of values
49
50 C4V_pC4Value = 8, // reference on a value (variable)
51
52 C4V_C4ObjectEnum = 9, // enumerated object
53
54};
55
56constexpr auto C4V_Last = static_cast<std::underlying_type_t<C4V_Type>>(C4V_pC4Value);
57
58const char *GetC4VName(const C4V_Type Type);
59char GetC4VID(const C4V_Type Type);
60C4V_Type GetC4VFromID(char C4VID);
61
62using C4ValueInt = int32_t;
63
64union C4V_Data
65{
66 C4ValueInt Int;
67 C4ID ID;
68 C4Object *Obj;
69 C4String *Str;
70 C4Value *Ref;
71 C4ValueContainer *Container;
72 C4ValueArray *Array;
73 C4ValueHash *Map;
74 std::intptr_t Raw;
75 // cheat a little - assume that all members have the same length
76 explicit operator bool() const noexcept { return Ref; }
77 bool operator==(C4V_Data b) const noexcept { return Ref == b.Ref; }
78 C4V_Data &operator=(C4Value *p) { Ref = p; return *this; }
79};
80// converter function, used in converter table
81struct C4VCnvFn
82{
83 bool(*Function)(C4Value *, C4V_Type, bool); // function to be called; returns whether possible
84 bool Warn;
85};
86
87template <typename T> struct C4ValueConv;
88
89template<typename T> concept C4ValueInteger = !std::same_as<T, C4ID>;
90
91class C4Value
92{
93public:
94 C4Value() : Type(C4V_Any), NextRef(nullptr), FirstRef(nullptr) { Data.Raw = 0; }
95
96 C4Value(const C4Value &nValue, C4ValueHash *owningMap = nullptr) : Data(nValue.Data), Type(nValue.Type), NextRef(nullptr), FirstRef(nullptr), OwningMap(owningMap)
97 {
98 AddDataRef();
99 }
100
101 C4Value(C4V_Data nData, C4V_Type nType) : Data(nData), Type(nData || nType == C4V_Int || nType == C4V_Bool ? nType : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
102 {
103 AddDataRef();
104 }
105
106 template<typename T> requires (!std::same_as<T, C4ID>)
107 explicit C4Value(T nData, C4V_Type nType) : Type(nData || nType == C4V_Int || nType == C4V_Bool ? nType : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
108 {
109 Data.Raw = 0;
110 Data.Int = nData; AddDataRef();
111 }
112
113 explicit C4Value(C4ID id) : Type(id ? C4V_C4ID : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
114 {
115 Data.Raw = 0;
116 Data.ID = id;
117 }
118
119 explicit C4Value(C4Object *pObj) : Type(pObj ? C4V_C4Object : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
120 {
121 Data.Obj = pObj; AddDataRef();
122 }
123
124 explicit C4Value(C4String *pStr) : Type(pStr ? C4V_String : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
125 {
126 Data.Str = pStr; AddDataRef();
127 }
128
129 explicit C4Value(C4ValueArray *pArray) : Type(pArray ? C4V_Array : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
130 {
131 Data.Array = pArray; AddDataRef();
132 }
133
134 explicit C4Value(C4ValueHash *pMap) : Type(pMap ? C4V_Map : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
135 {
136 Data.Map = pMap; AddDataRef();
137 }
138
139 explicit C4Value(C4Value *pVal) : Type(pVal ? C4V_pC4Value : C4V_Any), NextRef(nullptr), FirstRef(nullptr)
140 {
141 Data.Ref = pVal; AddDataRef();
142 }
143
144 static C4Value *OfMap(C4ValueHash *map)
145 {
146 auto ret = new C4Value;
147 ret->OwningMap = map;
148 return ret;
149 }
150
151 C4Value &operator=(const C4Value &nValue);
152
153 ~C4Value();
154
155 // explicit conversion from int, bool and id
156 std::optional<StdStrBuf> toString() const;
157
158 // Checked getters
159 C4ValueInt getInt() { return ConvertTo(vtToType: C4V_Int) ? Data.Int : 0; }
160 C4ValueInt getIntOrID() { Deref(); if (Type == C4V_Int || Type == C4V_Bool) return Data.Int; else if (Type == C4V_C4ID) return static_cast<C4ValueInt>(Data.ID); else return 0; }
161 bool getBool() { return ConvertTo(vtToType: C4V_Bool) ? !!Data.Int : false; }
162 C4ID getC4ID() { return ConvertTo(vtToType: C4V_C4ID) ? Data.ID : C4ID_None; }
163 C4Object *getObj() { return ConvertTo(vtToType: C4V_C4Object) ? Data.Obj : nullptr; }
164 C4String *getStr() { return ConvertTo(vtToType: C4V_String) ? Data.Str : nullptr; }
165 C4ValueArray *getArray() { return ConvertTo(vtToType: C4V_Array) ? Data.Array : nullptr; }
166 C4ValueHash *getMap() { return ConvertTo(vtToType: C4V_Map) ? Data.Map : nullptr; }
167 C4Value *getRef() { return ConvertTo(vtToType: C4V_pC4Value) ? Data.Ref : nullptr; }
168
169 // Unchecked getters
170 C4ValueInt _getInt() const { return Data.Int; }
171 bool _getBool() const { return !!Data.Int; }
172 C4ID _getC4ID() const { return Data.ID; }
173 C4Object *_getObj() const { return Data.Obj; }
174 C4String *_getStr() const { return Data.Str; }
175 C4ValueArray *_getArray() const { return Data.Array; }
176 C4ValueHash *_getMap() const { return Data.Map; }
177 C4Value *_getRef() const { return Data.Ref; }
178 std::intptr_t _getRaw() const { return Data.Raw; }
179
180 // Template versions
181 template <typename T> inline T Get() { return C4ValueConv<T>::FromC4V(*this); }
182
183 // only false for C4Script values nil, 0, false and NONE
184 // this is the same as bool conversion in C4Script
185 explicit operator bool() const { return static_cast<bool>(GetRefVal().GetData()); }
186
187 void Set(const C4Value &nValue) { if (this != &nValue) Set(nData: nValue.Data, nType: nValue.Type); }
188
189 void SetInt(C4ValueInt i) { C4V_Data d; d.Raw = 0; d.Int = i; Set(nData: d, nType: C4V_Int); }
190
191 void SetBool(bool b) { C4V_Data d; d.Raw = 0; d.Int = b; Set(nData: d, nType: C4V_Bool); }
192
193 void SetC4ID(C4ID id) { C4V_Data d; d.Raw = 0; d.ID = id; Set(nData: d, nType: C4V_C4ID); }
194
195 void SetObject(C4Object *Obj) { C4V_Data d; d.Obj = Obj; Set(nData: d, nType: C4V_C4Object); }
196
197 void SetString(C4String *Str) { C4V_Data d; d.Str = Str; Set(nData: d, nType: C4V_String); }
198
199 void SetArray(C4ValueArray *Array) { C4V_Data d; d.Array = Array; Set(nData: d, nType: C4V_Array); }
200
201 void SetMap(C4ValueHash *Map) { C4V_Data d; d.Map = Map; Set(nData: d, nType: C4V_Map); }
202
203 void SetRef(C4Value *nValue) { C4V_Data d; d.Ref = nValue; Set(nData: d, nType: C4V_pC4Value); }
204
205 void Set0();
206
207 bool Equals(const C4Value &other, C4AulScriptStrict strict) const;
208
209 bool operator==(const C4Value &Value2) const;
210
211 // Change and set Type to int in case it was any before (avoids GuessType())
212 C4Value &operator+=(C4ValueInt by) { GetData().Int += by; GetRefVal().Type = C4V_Int; return *this; }
213 C4Value &operator++() { GetData().Int++; GetRefVal().Type = C4V_Int; return *this; }
214 C4Value operator++(int) { C4Value alt = GetRefVal(); GetData().Int++; GetRefVal().Type = C4V_Int; return alt; }
215 C4Value &operator--() { GetData().Int--; GetRefVal().Type = C4V_Int; return *this; }
216 C4Value &operator-=(C4ValueInt by) { GetData().Int -= by; GetRefVal().Type = C4V_Int; return *this; }
217 C4Value operator--(int) { C4Value alt = GetRefVal(); GetData().Int--; GetRefVal().Type = C4V_Int; return alt; }
218
219 void Move(C4Value *nValue);
220
221 C4Value GetRef() { return C4Value(this); }
222 void Deref() { Set(GetRefVal()); }
223 bool IsRef() { return Type == C4V_pC4Value; }
224
225 // get data of referenced value
226 C4V_Data GetData() const { return GetRefVal().Data; }
227 C4V_Data &GetData() { return GetRefVal().Data; }
228
229 // get type of referenced value
230 C4V_Type GetType() const { return GetRefVal().Type; }
231
232 // return referenced value
233 const C4Value &GetRefVal() const;
234 C4Value &GetRefVal();
235
236 // Get the Value at the index. May Throw C4AulExecError
237 void GetContainerElement(C4Value *index, C4Value &to, struct C4AulContext *pctx = nullptr, bool noref = false);
238 // Set the length of the array. Throws C4AulExecError if not an array
239 void SetArrayLength(int32_t size, C4AulContext *cthr);
240
241 const char *GetTypeName() const { return GetC4VName(Type: GetType()); }
242 const char *GetTypeInfo();
243
244 void DenumeratePointer();
245
246 std::string GetDataString() const;
247
248 inline bool ConvertTo(C4V_Type vtToType, bool fStrict = true) // convert to dest type
249 {
250 C4VCnvFn Fn = C4ScriptCnvMap[Type][vtToType];
251 if (Fn.Function)
252 return (*Fn.Function)(this, vtToType, fStrict);
253 return true;
254 }
255
256 void HintType(C4V_Type type);
257
258 // Compilation
259 void CompileFunc(StdCompiler *pComp);
260
261protected:
262 // data
263 C4V_Data Data;
264
265 // reference-list
266 union
267 {
268 C4Value *NextRef;
269 C4ValueContainer *BaseContainer;
270 };
271 C4Value *FirstRef;
272
273 C4ValueHash *OwningMap = nullptr;
274
275 // data type
276 C4V_Type Type : 8;
277 bool HasBaseContainer = false;
278
279 C4Value *GetNextRef() { if (HasBaseContainer) return nullptr; else return NextRef; }
280 C4ValueContainer *GetBaseContainer() { if (HasBaseContainer) return BaseContainer; else return nullptr; }
281
282 void Set(C4V_Data nData, C4V_Type nType);
283
284 void AddRef(C4Value *pRef);
285 void DelRef(const C4Value *pRef, C4Value *pNextRef, C4ValueContainer *pBaseContainer);
286
287 void AddDataRef();
288 void DelDataRef(C4V_Data Data, C4V_Type Type, C4Value *pNextRef, C4ValueContainer *pBaseContainer);
289
290 void CheckRemoveFromMap();
291
292 // guess type from data (if type == c4v_any)
293 C4V_Type GuessType();
294
295 static C4VCnvFn C4ScriptCnvMap[C4V_Last + 1][C4V_Last + 1];
296 static bool FnCnvInt2Id(C4Value *Val, C4V_Type toType, bool fStrict);
297 static bool FnCnvGuess(C4Value *Val, C4V_Type toType, bool fStrict);
298
299 friend class C4Object;
300 friend class C4AulDefFunc;
301};
302
303// converter
304inline C4Value C4VInt(C4ValueInt iVal) { return C4Value{iVal, C4V_Int}; }
305inline C4Value C4VBool(bool fVal) { return C4Value{fVal, C4V_Bool}; }
306inline C4Value C4VID(C4ID idVal) { return C4Value{idVal}; }
307inline C4Value C4VObj(C4Object *pObj) { return C4Value(pObj); }
308inline C4Value C4VString(C4String *pStr) { return C4Value(pStr); }
309inline C4Value C4VArray(C4ValueArray *pArray) { return C4Value(pArray); }
310inline C4Value C4VMap(C4ValueHash *pMap) { return C4Value(pMap); }
311inline C4Value C4VRef(C4Value *pVal) { return pVal->GetRef(); }
312
313C4Value C4VString(StdStrBuf &&strString);
314C4Value C4VString(const char *strString);
315
316// converter templates
317template <> struct C4ValueConv<C4ValueInt>
318{
319 inline static C4V_Type Type() { return C4V_Int; }
320 inline static C4ValueInt FromC4V(C4Value &v) { return v.getInt(); }
321 inline static C4ValueInt _FromC4V(const C4Value &v) { return v._getInt(); }
322 inline static C4Value ToC4V(int32_t v) { return C4VInt(iVal: v); }
323};
324
325template <> struct C4ValueConv<bool>
326{
327 inline static C4V_Type Type() { return C4V_Bool; }
328 inline static bool FromC4V(C4Value &v) { return v.getBool(); }
329 inline static bool _FromC4V(const C4Value &v) { return v._getBool(); }
330 inline static C4Value ToC4V(bool v) { return C4VBool(fVal: v); }
331};
332
333template <> struct C4ValueConv<C4ID>
334{
335 inline static C4V_Type Type() { return C4V_C4ID; }
336 inline static C4ID FromC4V(C4Value &v) { return v.getC4ID(); }
337 inline static C4ID _FromC4V(const C4Value &v) { return v._getC4ID(); }
338 inline static C4Value ToC4V(C4ID v) { return C4VID(idVal: v); }
339};
340
341template <> struct C4ValueConv<C4Object *>
342{
343 inline static C4V_Type Type() { return C4V_C4Object; }
344 inline static C4Object *FromC4V(C4Value &v) { return v.getObj(); }
345 inline static C4Object *_FromC4V(const C4Value &v) { return v._getObj(); }
346 inline static C4Value ToC4V(C4Object *v) { return C4VObj(pObj: v); }
347};
348
349template <> struct C4ValueConv<C4String *>
350{
351 inline static C4V_Type Type() { return C4V_String; }
352 inline static C4String *FromC4V(C4Value &v) { return v.getStr(); }
353 inline static C4String *_FromC4V(const C4Value &v) { return v._getStr(); }
354 inline static C4Value ToC4V(C4String *v) { return C4VString(pStr: v); }
355};
356
357template <> struct C4ValueConv<C4ValueArray *>
358{
359 inline static C4V_Type Type() { return C4V_Array; }
360 inline static C4ValueArray *FromC4V(C4Value &v) { return v.getArray(); }
361 inline static C4ValueArray *_FromC4V(const C4Value &v) { return v._getArray(); }
362 inline static C4Value ToC4V(C4ValueArray *v) { return C4VArray(pArray: v); }
363};
364
365template <> struct C4ValueConv<C4ValueHash *>
366{
367 inline static C4V_Type Type() { return C4V_Map; }
368 inline static C4ValueHash *FromC4V(C4Value &v) { return v.getMap(); }
369 inline static C4ValueHash *_FromC4V(const C4Value &v) { return v._getMap(); }
370 inline static C4Value ToC4V(C4ValueHash *v) { return C4VMap(pMap: v); }
371};
372
373template <> struct C4ValueConv<C4Value *>
374{
375 inline static C4V_Type Type() { return C4V_pC4Value; }
376 inline static C4Value *FromC4V(C4Value &v) { return v.getRef(); }
377 inline static C4Value *_FromC4V(const C4Value &v) { return v._getRef(); }
378 inline static C4Value ToC4V(C4Value *v) { return C4VRef(pVal: v); }
379};
380
381namespace std
382{
383 template<>
384 struct hash<C4Value>
385 {
386 std::size_t operator()(C4Value value) const;
387 };
388}
389
390extern const C4Value C4VNull, C4VFalse, C4VTrue;
391