-
Notifications
You must be signed in to change notification settings - Fork 1
/
Table.cpp
283 lines (250 loc) · 8.49 KB
/
Table.cpp
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include "Table.h"
#include "Interpreter.h"
//Should be identical to at_ref except that it returns Value() instead of doing a talloc() call.
Value Table::at(Interpreter& interp, Value index) // Intentional copy of 2nd param
{
switch (index.t_vType)
{
default:
interp.RuntimeError(nullptr, ErrorCode::BadMemberAccess, "Bad type used to index into Table!");
return Value();
case(Value::vType::String): // Just use our hashtable, innit?
{
if (t_hash.count(index))
{
return t_hash.at(index);
}
return Value();
}
case(Value::vType::Integer):
break;
case(Value::vType::Double): //Dude. You can't use a raw-ass double to index. That's fucking stupid. You're stupid.
//I'm rounding this while glaring at you sternly.
index = math::round({ index });
break;
}
//Should be able to safely assert by this point that array_index has been set to something.
Value::JoaoInt array_index = index.t_value.as_int;
if (array_index < 0 || array_index >= static_cast<ptrdiff_t>(t_array.size())) // This fluke of integer casting means that arrays cannot be more than 8 exabytes big.
{
if (t_hash.count(index))
{
return t_hash.at(index);
}
return Value();
}
return t_array.at(array_index); // Friendly reminder that std::vector::at() does bounds-checking, while std::vector::operator[] does not.
}
Value& Table::at_ref(Interpreter& interp, Value index) // Intentional copy of 2nd param
{
switch (index.t_vType)
{
default:
interp.RuntimeError(nullptr, ErrorCode::BadMemberAccess, "Bad type used to index into Table!");
return Value::dev_null;
case(Value::vType::String): // Just use our properties, innit?
{
if (t_hash.count(index))
{
return t_hash.at(index);
}
#ifdef JOAO_SAFE
++interp.value_init_count;
if (interp.value_init_count > MAX_VARIABLES)
{
throw error::max_variables(std::string("Program reached the limit of ") + std::to_string(MAX_VARIABLES) + std::string("instantiated variables!"));
}
#endif
return talloc(index, Value());
}
case(Value::vType::Integer):
break;
case(Value::vType::Double): //Dude. You can't use a raw-ass double to index. That's fucking stupid. You're stupid.
//I'm rounding this while glaring at you sternly.
index = math::round({ index }).t_value.as_int;
break;
}
Value::JoaoInt array_index = index.t_value.as_int;
if (array_index < 0 || array_index >= static_cast<ptrdiff_t>(t_array.size()))
{
if (t_hash.count(index))
{
return t_hash.at(index);
}
#ifdef JOAO_SAFE
++interp.value_init_count;
if (interp.value_init_count > MAX_VARIABLES)
{
throw error::max_variables(std::string("Program reached the limit of ") + std::to_string(MAX_VARIABLES) + std::string("instantiated variables!"));
}
#endif
return talloc(index, Value());
}
return t_array.at(array_index); // Friendly reminder that std::vector::at() does bounds-checking, while std::vector::operator[] does not.
}
bool Table::at_set_raw(Value index, const Value& newval)
{
switch (index.t_vType)
{
default:
return true;
case(Value::vType::String):
case(Value::vType::Integer):
case(Value::vType::Double):
talloc(index, newval);
return false;
}
}
void Table::at_set(Interpreter& interp, Value index, Value& newval)
{
switch (index.t_vType)
{
default:
interp.RuntimeError(nullptr, ErrorCode::BadMemberAccess, "Bad type used to index into Table!");
return;
case(Value::vType::String):
case(Value::vType::Integer):
case(Value::vType::Double):
talloc(index, newval);
return;
}
}
Value& Table::talloc(Value index, const Value& newval)
{
switch (index.t_vType)
{
case(Value::vType::String):
{
// Unfortunately we can't just use our properties since, since these elements are removable,
// this would allow Objects which inherit from /table to rescind their properties and go AWOL from the OOP structure,
// which, while pleasantly chaotic, would be a horrible paradigm to allow, and honestly would probably end up being kinda buggy.
t_hash[index] = newval;
return t_hash.at(index);
}
case(Value::vType::Integer):
break;
case(Value::vType::Double): // It looks like you were trying to index by doubles. Did you mean to use Integers?
index = math::round({ index });
break;
default: UNLIKELY // This is a bug, but, whatever.
index = Value(0);
}
Value::JoaoInt array_index = index.t_value.as_int;
if (array_index < 0)
{
t_hash[index] = newval;
return t_hash.at(index);
}
//Would this fit in the next sequential spot?
if (array_index == static_cast<ptrdiff_t>(t_array.size()))
{ // Perfect! A pleasant and surprisingly common case.
t_array.push_back(newval);
return t_array.at(array_index);
}
//Is this index already in the array to begin with?
if (array_index < static_cast<ptrdiff_t>(t_array.size()))
{
return t_array.at(array_index) = newval;
}
//..Is this index already in the HASHTABLE to begin with?
if (t_hash.count(index))
{
return t_hash.at(index) = newval;
}
//If we won't cause a rehash by adding this new element to the hashtable...
//FIXME: This sucks, and is likely going to be hilariously inaccurate with this novel hashtable implementation.
//if (static_cast<float>((t_hash.size() + 1)) / t_hash.bucket_count() < t_hash.max_load_factor())
if (static_cast<float>(t_hash.size()) / t_hash.capacity() > 0.8)
{
// Just stick it in there, then, tbh
t_hash[index] = newval;
return t_hash.at(index);
}
//If we *would*, do a check to make sure that we can't just move some stuff over
for (Value::JoaoInt i = t_array.size(); i < array_index; ++i)
{
if (!t_hash.count(Value(i)))
goto JUST_HASH_IT;
}
//Oh god, we can actually do this.
for (Value::JoaoInt i = t_array.size(); i < array_index; ++i)
{
Value ival = Value(i);
t_array.push_back(std::move(t_hash.at(ival)));
t_hash.erase(ival);
}
t_array.push_back(newval);
return t_array.at(array_index);
JUST_HASH_IT:
t_hash[index] = newval;
return t_hash.at(index);
}
void Table::tfree(const Value& index)
{
//NOTE: This function should *always* follow the pattern of data access that talloc() does, since it needs to reverse-engineer what it did from the index.
Value::JoaoInt array_index;
switch (index.t_vType)
{
case(Value::vType::String):
{
// Unfortunately we can't just use our properties since, since these elements are removable,
// this would allow Objects which inherit from /table to rescind their properties and go AWOL from the OOP structure,
// which, while pleasantly chaotic, would be a horrible paradigm to allow, and honestly would probably end up being kinda buggy.
t_hash.erase(index);
return;
}
case(Value::vType::Integer):
array_index = index.t_value.as_int;
break;
case(Value::vType::Double): // It looks like you were trying to index by doubles. Did you mean to use Integers?
array_index = math::round({ index }).t_value.as_int;
break;
default: UNLIKELY // This is a bug, but, whatever.
array_index = 0;
}
Value array_val = Value(array_index);
if (array_index < 0)
{
t_hash.erase(array_val);
return;
}
//Is this index already in the array to begin with?
const Value::JoaoInt arrsize = static_cast<Value::JoaoInt>(t_array.size());
if (array_index < arrsize)
{
//Oh, christ.
//So right now, tfree() is only called by /table/remove()
//So we can avoid having to do some hashtable weirdness in this case,
//and just do an "oops it just erases it and shifts the elements afterwards! lol" for now
//Before we do this, imagine that there's an index [9] present in the hashtable, and the array only goes from [0] to [8].
//Inserting na�vely in that case would clobber the 9th element, which is a bad.
//talloc() tries to prevent this, but it's still possible, so lets do some checks first.
//FIXME: This nonsense shouldn't really be in tfree(); it'd be better as something that insert() and remove() have sovereignly,
//so this function can be used more generically for freeing table data.
for (Value next_key = Value(t_array.size()); t_hash.count(next_key); next_key = Value(t_array.size())) // If this is the case
{ // take that element and actually put it on the fucking array, I guess
t_array.push_back(std::move(t_hash.at(next_key)));
t_hash.erase(next_key);
}
/*
if (array_index + 1 == arrsize)
{
t_array.pop_back();
return;
}
*/
t_array.erase(t_array.begin()+array_index); // This is kind of a FIXME? Maybe? I guess?
return;
}
//..Is this index already in the HASHTABLE to begin with?
if (t_hash.count(array_val))
{
t_hash.erase(array_val);
return;
}
//This index didn't exist. Weird.
#ifdef _DEBUG
std::cout << "DEBUG: Attempted to tfree() an index that didn't exist!\n";
#endif
return;
}