-
Notifications
You must be signed in to change notification settings - Fork 1
/
Object.h
244 lines (209 loc) · 6.82 KB
/
Object.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#pragma once
#include "Forward.h"
#include "AST.h"
#include "Error.h"
class PolyTable
{
struct HollowKnight
{
protected:
HollowKnight(void* p, const std::type_info& i)
:ptr(p)
,info(i)
{
}
public:
void* ptr = nullptr;
const std::type_info& info;
virtual ~HollowKnight() = default;
};
template <typename Type>
struct Vessel : public HollowKnight
{
Vessel(const Type& val)
:HollowKnight(new Type(val), typeid(val))
{
static_assert(sizeof(*this) == sizeof(HollowKnight)); // Necessary for this to be stored in a std::vector properly.
static_assert(!std::is_same<void,Type>()); // Why did you do this
static_assert(!std::is_reference<Type>()); // why
}
Vessel()
:HollowKnight(new Type(), typeid(Type))
{
}
virtual ~Vessel() // I am become death, the destructor of worlds
{
delete static_cast<Type*>(ptr);
}
Vessel(const Vessel&) = delete; // Cannot be copied
Vessel(Vessel&& other) // Can be moved, though, no biggie
:HollowKnight(other.ptr,other.info)
{
other.ptr = nullptr;
}
};
public:
HashTable<ImmutableString,HollowKnight> data;
template <typename Type>
Type& at(const ImmutableString& key)
{
HollowKnight& hk = data.at(key);
if(hk.info != typeid(Type))
{
throw error::malicious("Data at key is of the incorrect type!"); // Haha, yes
}
return *static_cast<Type*>(hk.ptr);
}
template <typename Type>
Type* lazy_at(const ImmutableString& key)
{
HollowKnight* hk = data.lazy_at(key);
if(hk == nullptr)
return nullptr;
if(hk->info != typeid(Type))
{
throw error::malicious("Data at key is of the incorrect type!"); // Haha, yes
}
return static_cast<Type*>(hk->ptr);
}
template <typename Type>
void insert(const ImmutableString& key, Type& value)
{
data.insert(key,Vessel<Type>(value));
}
template <typename Type>
void insert(const ImmutableString& key)
{
if constexpr(std::is_default_constructible<Type>())
{
data.insert(key,Vessel<Type>());
}
else
{
return;
}
}
};
/*
This is a sort of metatype which provides overriding, perhaps natively-implemented behavior for certain operations and methods for ObjectTypes which have a pointer to it.
*/
class Metatable
{
public:
Hashtable<ImmutableString,NativeMethod*> metamethods; // by "void" I mean "NativeMethod
~Metatable()
{
for (auto it = metamethods.begin(); it != metamethods.end(); it++)
{
delete it.value();
}
}
void append_method(const ImmutableString& str, NativeMethod* newmethod)
{
metamethods.insert(str, newmethod);
}
friend class Object;
friend class ObjectType;
};
class Object
{
protected:
Hashtable<ImmutableString, Value> properties; // A general container for all Value-type properties of this object. Functions are stored in the Program's ObjectTree.
Hashtable<ImmutableString, Value>* base_properties; //A pointer to our ObjectType's property hashtable, to look up default values when needed
Hashtable<ImmutableString, Function*>* base_funcs; // Pointer to object's base functions.
Metatable* mt = nullptr;
PolyTable mt_privates; //These properties should be accessed by nobody but the metatable's metamethods themselves.
public:
ImmutableString object_type; // A string denoting the directory position of this object.
PolyTable& get_privates() { return mt_privates; }
Value* has_property(Interpreter&, const ImmutableString&);
Value get_property(Interpreter&, const ImmutableString&);
Value get_property_raw(const ImmutableString&);
void set_property(Interpreter&, const ImmutableString&, Value);
void set_property_raw(const ImmutableString&, Value);
Value call_method(Interpreter&, const ImmutableString&, std::vector<Value>& args);
Function* has_method(Interpreter&, const ImmutableString&);
Object(const ImmutableString& objty, Hashtable<ImmutableString, Value>* puh, Hashtable<ImmutableString, Function*>* fuh, Metatable* m = nullptr)
:base_properties(puh)
,base_funcs(fuh)
,mt(m)
,object_type(objty)
{
}
virtual ~Object() = default; // FIXME: make this not necessary to ensure tables are deleted.
std::string dump()
{
std::string st = object_type.to_string() + "/{";
for (auto it : properties) {
// Do stuff
st += "(" + it.first.to_string() + "," + it.second.to_string() + ") ";
}
return st + "}";
}
// you ever work on a project for so long that you start to change your mind about its naming conventions
std::string to_json();
bool virtual is_table() const { return false; }
friend class ObjectType;
};
class ObjectType // Stores the default methods and properties of this type of Object.
//Note that this logic assumes that inheritence has already been "figured out," meaning it makes no attempt to parse some grand Object tree to figure out what to do;
//it either has the property, or it doesn't, or it has the function, or it does not.
{
ImmutableString object_type;
Hashtable<ImmutableString, Function*> typefuncs;
Hashtable<ImmutableString, Value> typeproperties;
Metatable* mt = nullptr;
bool owns_metatable = false; // Marks whether we own the metatable pointer we have
public:
std::string get_name() const { return object_type.to_string(); };
bool is_table_type = false;
ObjectType(std::string n)
:object_type(n)
{
}
ObjectType(const ImmutableString& n, Metatable* m, bool o = false)
:object_type(n)
,mt(m)
,owns_metatable(o)
{
}
ObjectType(const ImmutableString& n, Hashtable<ImmutableString, Value> typep)
:object_type(n)
,typeproperties(typep)
{
}
~ObjectType()
{
if (owns_metatable)
delete mt;
}
//Note that this does not create a Value with Objectptr type; this is moreso an interface for the Interpreter during Object construction than anything else
//It is presumed that whoever is calling this function will take ownership of this novel heap pointer and ensure that it be properly GC'd in the future.
Object* makeObject(Interpreter&, std::vector<Value>&&);
Value get_typeproperty(Interpreter&, const ImmutableString&, ASTNode*);
Value* has_typeproperty([[maybe_unused]] Interpreter& interp, const ImmutableString& str, [[maybe_unused]] ASTNode* getter)
{
if (!typeproperties.count(str))
{
return nullptr;
}
return &(typeproperties.at(str));
}
Function* has_typemethod(Interpreter&, const ImmutableString&, ASTNode*);
//Passed Parser-by-reference as the Interpreter should never be calling this; ObjectTypes are static at runtime (for now, anyways!)
void set_typeproperty(Parser&,const ImmutableString&, const Value&);
void set_typeproperty_raw(const ImmutableString&, Value);
void set_typemethod(Parser&, std::string, Function*);
void set_typemethod_raw(const ImmutableString&, Function*);
void append_native_method(ImmutableString str, NativeMethod* newmethod)
{
#ifdef _DEBUG
if(!mt) UNLIKELY
{
throw std::runtime_error("Metatable not yet provided for this ObjectType which has NativeMethods!");
}
#endif
mt->append_method(str,newmethod);
}
friend class ObjectTree;
};