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 }