| 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 |
| 29 | class C4Value; |
| 30 | class C4Object; |
| 31 | class C4String; |
| 32 | class C4ValueArray; |
| 33 | class C4ValueHash; |
| 34 | class C4ValueContainer; |
| 35 | |
| 36 | // C4Value type |
| 37 | enum 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 | |
| 56 | constexpr auto C4V_Last = static_cast<std::underlying_type_t<C4V_Type>>(C4V_pC4Value); |
| 57 | |
| 58 | const char *GetC4VName(const C4V_Type Type); |
| 59 | char GetC4VID(const C4V_Type Type); |
| 60 | C4V_Type GetC4VFromID(char C4VID); |
| 61 | |
| 62 | using C4ValueInt = int32_t; |
| 63 | |
| 64 | union 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 |
| 81 | struct C4VCnvFn |
| 82 | { |
| 83 | bool(*Function)(C4Value *, C4V_Type, bool); // function to be called; returns whether possible |
| 84 | bool Warn; |
| 85 | }; |
| 86 | |
| 87 | template <typename T> struct C4ValueConv; |
| 88 | |
| 89 | template<typename T> concept C4ValueInteger = !std::same_as<T, C4ID>; |
| 90 | |
| 91 | class C4Value |
| 92 | { |
| 93 | public: |
| 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 | |
| 261 | protected: |
| 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 |
| 304 | inline C4Value C4VInt(C4ValueInt iVal) { return C4Value{iVal, C4V_Int}; } |
| 305 | inline C4Value C4VBool(bool fVal) { return C4Value{fVal, C4V_Bool}; } |
| 306 | inline C4Value C4VID(C4ID idVal) { return C4Value{idVal}; } |
| 307 | inline C4Value C4VObj(C4Object *pObj) { return C4Value(pObj); } |
| 308 | inline C4Value C4VString(C4String *pStr) { return C4Value(pStr); } |
| 309 | inline C4Value C4VArray(C4ValueArray *pArray) { return C4Value(pArray); } |
| 310 | inline C4Value C4VMap(C4ValueHash *pMap) { return C4Value(pMap); } |
| 311 | inline C4Value C4VRef(C4Value *pVal) { return pVal->GetRef(); } |
| 312 | |
| 313 | C4Value C4VString(StdStrBuf &&strString); |
| 314 | C4Value C4VString(const char *strString); |
| 315 | |
| 316 | // converter templates |
| 317 | template <> 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 | |
| 325 | template <> 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 | |
| 333 | template <> 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 | |
| 341 | template <> 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 | |
| 349 | template <> 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 | |
| 357 | template <> 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 | |
| 365 | template <> 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 | |
| 373 | template <> 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 | |
| 381 | namespace std |
| 382 | { |
| 383 | template<> |
| 384 | struct hash<C4Value> |
| 385 | { |
| 386 | std::size_t operator()(C4Value value) const; |
| 387 | }; |
| 388 | } |
| 389 | |
| 390 | extern const C4Value C4VNull, C4VFalse, C4VTrue; |
| 391 | |