1 module zua.vm.std.table;
2 import zua.vm.engine;
3 import zua.vm.reflection;
4 import std.algorithm.mutation;
5 import std.typecons;
6 import std.variant;
7 import std.conv;
8 
9 private string ltable_concat(TableValue table, Nullable!string nsep, Nullable!long ni, Nullable!long nj) {
10 	string sep = nsep.isNull ? "" : nsep.get;
11 	long i = ni.isNull ? 1 : ni.get;
12 	long j = nj.isNull ? cast(long) table.length.num : nj.get;
13 
14 	string res;
15 	foreach (at; i .. j + 1) {
16 		if (at > i) res ~= sep;
17 		Value v = table.get(Value(at));
18 		if (v.type != ValueType.String && v.type != ValueType.Number) {
19 			throw new Exception("invalid value (" ~ v.typeStr ~ ") at index " ~ at.to!string ~ " in table for 'concat'");
20 		}
21 		res ~= v.toString;
22 	}
23 	return res;
24 }
25 
26 private void ltable_insert(TableValue table, Algebraic!(double, Value) mpos, Nullable!Value value) {
27 	if (value.isNull) {
28 		if (auto n = mpos.peek!double) {
29 			table.rawset(Value(table.length.num + 1), Value(*n));
30 		}
31 		else {
32 			table.rawset(Value(table.length.num + 1), mpos.get!Value);
33 		}
34 	}
35 	else {
36 		if (mpos.peek!double == null) {
37 			throw new Exception("bad argument #2 to 'insert' (number expected, got " ~ mpos.get!Value.typeStr ~ ")");
38 		}
39 		double pos = cast(double)cast(long)mpos.get!double;
40 		if (table.rawhas(Value(pos))) {
41 			Value prev = *table.rawget(Value(pos));
42 			double i = pos + 1;
43 			for (; table.rawhas(Value(i)); ++i) {
44 				Value nprev = *table.rawget(Value(i));
45 				table.rawset(Value(i), prev);
46 				prev = nprev;
47 			}
48 			table.rawset(Value(i), prev);
49 		}
50 		table.rawset(Value(pos), value.get);
51 	}
52 }
53 
54 private double ltable_maxn(TableValue table) {
55 	double max = 0;
56 
57 	for (size_t i = table.array.length; i > 0; --i) {
58 		if (table.rawhas(Value(i))) {
59 			max = i;
60 			break;
61 		}
62 	}
63 
64 	foreach (Tuple!(Value, Value) pair; table.hash.iterator) {
65 		if (pair[0].type == ValueType.Number) {
66 			const double n = pair[0].num;
67 			if (n > max) max = n;
68 		}
69 	}
70 
71 	return max;
72 }
73 
74 private Value[] ltable_remove(TableValue table, Nullable!long mpos) {
75 	if (mpos.isNull) {
76 		Value len = table.length;
77 		if (len.num == 0) return [];
78 		Value res = *table.rawget(len);
79 		table.rawset(len, Value());
80 		return [res];
81 	}
82 	else {
83 		double pos = cast(double)mpos.get;
84 		if (!table.rawhas(Value(pos))) return [];
85 		Value res = *table.rawget(Value(pos));
86 		double i = pos;
87 		for (; table.rawhas(Value(i + 1)); ++i) {
88 			table.rawset(Value(i), *table.rawget(Value(i + 1)));
89 		}
90 		table.rawset(Value(i), Value());
91 		return [res];
92 	}
93 }
94 
95 private bool lt(Nullable!FunctionValue compare, Value a, Value b) {
96 	if (compare.isNull) {
97 		return a.lessThan(b);
98 	}
99 	else {
100 		Value[] res = compare.get.ccall([a, b]);
101 		if (res.length == 0) return false;
102 		else return res[0].toBool;
103 	}
104 }
105 
106 private pragma(inline) bool le(Nullable!FunctionValue compare, Value a, Value b) {
107 	return !lt(compare, b, a);
108 	// (a <= b) is !(b < a)
109 }
110 
111 private size_t partition(Nullable!FunctionValue cmp, Value[] arr) {
112 	Value a = arr[0];
113 	Value b = arr[arr.length / 2];
114 	Value c = arr[$ - 1];
115 
116 	Value pivot;
117 
118 	if ((le(cmp, b, a) && lt(cmp, a, c)) || (le(cmp, c, a) && lt(cmp, a, b))) {
119 		pivot = a;
120 		swap(arr[0], arr[$ - 1]);
121 	}
122 	else if ((le(cmp, a, b) && lt(cmp, b, c)) || (le(cmp, c, b) && lt(cmp, b, a))) {
123 		pivot = b;
124 		swap(arr[arr.length / 2], arr[$ - 1]);
125 	}
126 	else pivot = c;
127 
128 	size_t i = -1;
129 	foreach (j; 0 .. arr.length - 1) {
130 		if (lt(cmp, arr[j], pivot)) {
131 			i++;
132 			swap(arr[i], arr[j]);
133 		}
134 	}
135 	swap(arr[i + 1], arr[$ - 1]);
136 	return i + 1;
137 }
138 
139 private void sort(Nullable!FunctionValue compare, Value[] arr) {
140 	size_t index = partition(compare, arr);
141 	if (index > 1) sort(compare, arr[0 .. index]);
142 	if (index < arr.length - 1) sort(compare, arr[index + 1 .. $]);
143 }
144 
145 private void ltable_sort(TableValue table, Nullable!FunctionValue compare) {
146 	size_t length = cast(size_t) table.length.num;
147 	if (length == 0) return;
148 	Value[] arr;
149 	arr.length = length;
150 	foreach (i; 0 .. length) {
151 		Value* v = table.rawget(Value(i + 1));
152 		if (v != null) arr[i] = *v;
153 	}
154 	sort(compare, arr);
155 	foreach (i; 0 .. length) {
156 		table.rawset(Value(i + 1), arr[i]);
157 	}
158 }
159 
160 private void ltable_foreachi(TableValue table, FunctionValue func) {
161 	for (double i = 1;; ++i) {
162 		if (!table.rawhas(Value(i))) break;
163 		func.ccall([Value(i), *table.rawget(Value(i))]);
164 	}
165 }
166 
167 private void ltable_foreach(TableValue table, FunctionValue func) {
168 	foreach (i; 0 .. table.array.length) {
169 		Value v = table.array[i];
170 		if (!v.isNil) func.ccall([Value(i + 1), v]);
171 	}
172 	foreach (Tuple!(Value, Value) pair; table.hash.iterator) {
173 		if (!pair[1].isNil) func.ccall([pair[0], pair[1]]);
174 	}
175 }
176 
177 private Value ltable_getn(TableValue table) {
178 	return table.length;
179 }
180 
181 private void ltable_setn(TableValue) {
182 	throw new Exception("'setn' is obsolete");
183 }
184 
185 /** Get table library */
186 Value tablelib() {
187 	TableValue res = new TableValue;
188 	res.set(Value("concat"), exposeFunction!(ltable_concat, "concat"));
189 	res.set(Value("insert"), exposeFunction!(ltable_insert, "insert"));
190 	res.set(Value("maxn"), exposeFunction!(ltable_maxn, "maxn"));
191 	res.set(Value("remove"), exposeFunction!(ltable_remove, "remove"));
192 	res.set(Value("sort"), exposeFunction!(ltable_sort, "sort"));
193 
194 	res.set(Value("foreach"), exposeFunction!(ltable_foreach, "foreach"));
195 	res.set(Value("foreachi"), exposeFunction!(ltable_foreachi, "foreachi"));
196 	res.set(Value("getn"), exposeFunction!(ltable_getn, "getn"));
197 	res.set(Value("setn"), exposeFunction!(ltable_setn, "setn"));
198 	return Value(res);
199 }