1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module grimoire.stdlib.map; 7 8 import std.typecons : Tuple, tuple; 9 import std.conv : to; 10 import grimoire.assembly, grimoire.compiler, grimoire.runtime; 11 import grimoire.stdlib.util; 12 13 /// Hashmap 14 private final class Map(T) { 15 /// Payload 16 T[GrString] data; 17 18 /// Ctor 19 this(GrString[] keys, T[] values) { 20 for (size_t i; i < keys.length; ++i) { 21 data[keys[i]] = values[i]; 22 } 23 } 24 25 this(Map!T map) { 26 data = map.data.dup; 27 } 28 } 29 30 private { 31 alias IntMap = Map!(GrInt); 32 alias FloatMap = Map!(GrFloat); 33 alias StringMap = Map!(GrString); 34 alias ObjectMap = Map!(GrPtr); 35 } 36 37 /// Iterator 38 private final class MapIter(T) { 39 Tuple!(GrString, T)[] pairs; 40 size_t index; 41 } 42 43 package(grimoire.stdlib) void grLoadStdLibMap(GrLibrary library) { 44 library.addForeign("Map", ["T"]); 45 library.addForeign("MapIter", ["T"]); 46 47 static foreach (t; ["Int", "Float", "String", "Object"]) { 48 mixin("GrType any" ~ t ~ "Map = grAny(\"M\", (type, data) { 49 if (type.baseType != GrBaseType.foreign) 50 return false; 51 auto subType = grUnmangleComposite(type.mangledType); 52 if(subType.name != \"Map\") 53 return false; 54 if(subType.signature.length != 1) 55 return false; 56 data.set(\"T\", subType.signature[0]); 57 data.set(\"A\", grArray(subType.signature[0])); 58 return grIsKindOf" ~ t ~ "(subType.signature[0].baseType); 59 }); 60 61 GrType any" ~ t 62 ~ "Array = grAny(\"A\", (type, data) { 63 if (type.baseType != GrBaseType.array_) 64 return false; 65 const GrType subType = grUnmangle(type.mangledType); 66 data.set(\"M\", grGetForeignType(\"Map\", [subType])); 67 return grIsKindOf" ~ t ~ "(subType.baseType); 68 }); 69 70 library.addPrimitive(&_make_!\"" ~ t 71 ~ "\", \"Map\", [grStringArray, any" ~ t ~ "Array], [grAny(\"M\")]); 72 73 library.addPrimitive(&_copy_!\"" ~ t 74 ~ "\", \"copy\", [any" ~ t ~ "Map], [grAny(\"M\")]); 75 76 library.addPrimitive(&_size_!\"" ~ t ~ "\", \"size\", [any" 77 ~ t ~ "Map], [grInt]); 78 79 library.addPrimitive(&_empty_!\"" ~ t ~ "\", \"empty?\", [ 80 any" ~ t ~ "Map 81 ], [grBool]); 82 83 library.addPrimitive(&_clear_!\"" ~ t ~ "\", \"clear\", [ 84 any" ~ t ~ "Map 85 ], [grAny(\"M\")]); 86 87 library.addPrimitive(&_set_!\"" ~ t 88 ~ "\", \"set\", [any" ~ t ~ "Map, grString, grAny(\"T\")]); 89 90 library.addPrimitive(&_get_!\"" ~ t 91 ~ "\", \"get\", [any" ~ t ~ "Map, grString], [grAny(\"T\")]); 92 93 library.addPrimitive(&_has_!\"" 94 ~ t ~ "\", \"has?\", [any" ~ t ~ "Map, grString], [grBool]); 95 96 library.addPrimitive(&_remove_!\"" ~ t 97 ~ "\", \"remove\", [any" ~ t ~ "Map, grString]); 98 99 library.addPrimitive(&_byKeys_!\"" ~ t ~ "\", \"byKeys\", [any" ~ t 100 ~ "Map], [grStringArray]); 101 102 library.addPrimitive(&_byValues_!\"" ~ t ~ "\", \"byValues\", [any" ~ t ~ "Map], [any" 103 ~ t ~ "Array]); 104 105 library.addPrimitive(&_each_!\"" ~ t ~ "\", \"each\", [ 106 grAny(\"A\", (type, data) { 107 if (type.baseType != GrBaseType.foreign) 108 return false; 109 auto subType = grUnmangleComposite(type.mangledType); 110 if(subType.name != \"Map\") 111 return false; 112 if(subType.signature.length != 1) 113 return false; 114 data.set(\"R\", grGetForeignType(\"MapIter\", subType.signature)); 115 return grIsKindOf" ~ t ~ "(subType.signature[0].baseType); 116 }) 117 ], [grAny(\"R\")]); 118 119 library.addPrimitive(&_next_!\"" 120 ~ t ~ "\", \"next\", [ 121 grAny(\"R\", (type, data) { 122 if (type.baseType != GrBaseType.foreign) 123 return false; 124 auto result = grUnmangleComposite(type.mangledType); 125 if(result.signature.length != 1 || result.name != \"MapIter\") 126 return false; 127 data.set(\"T\", grGetClassType(\"Pair\", [grString, result.signature[0]])); 128 return grIsKindOf" ~ t ~ "(result.signature[0].baseType); 129 }) 130 ], [grBool, grAny(\"T\")]); 131 "); 132 } 133 134 GrType boolMap = grGetForeignType("Map", [grBool]); 135 library.addPrimitive(&_print_!("bool", false), "print", [boolMap]); 136 library.addPrimitive(&_print_!("bool", true), "printl", [boolMap]); 137 138 GrType intMap = grGetForeignType("Map", [grInt]); 139 library.addPrimitive(&_print_!("int", false), "print", [intMap]); 140 library.addPrimitive(&_print_!("int", true), "printl", [intMap]); 141 142 GrType floatMap = grGetForeignType("Map", [grFloat]); 143 library.addPrimitive(&_print_!("float", false), "print", [floatMap]); 144 library.addPrimitive(&_print_!("float", true), "printl", [floatMap]); 145 146 GrType stringMap = grGetForeignType("Map", [grString]); 147 library.addPrimitive(&_print_!("string", false), "print", [stringMap]); 148 library.addPrimitive(&_print_!("string", true), "printl", [stringMap]); 149 } 150 151 private void _make_(string t)(GrCall call) { 152 mixin(t ~ "Map map = new " ~ t ~ "Map(call.getStringArray(0).data, call.get" 153 ~ t ~ "Array(1).data);"); 154 call.setForeign(map); 155 } 156 157 private void _copy_(string t)(GrCall call) { 158 mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);"); 159 if (!map) { 160 call.raise("NullError"); 161 return; 162 } 163 mixin("call.setForeign!" ~ t ~ "Map(new " ~ t ~ "Map(map));"); 164 } 165 166 private void _size_(string t)(GrCall call) { 167 mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);"); 168 if (!map) { 169 call.raise("NullError"); 170 return; 171 } 172 call.setInt(cast(GrInt) map.data.length); 173 } 174 175 private void _empty_(string t)(GrCall call) { 176 mixin("const " ~ t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);"); 177 if (!map) { 178 call.raise("NullError"); 179 return; 180 } 181 call.setBool(map.data.length == 0); 182 } 183 184 private void _clear_(string t)(GrCall call) { 185 mixin(t ~ "Map map = call.getForeign!" ~ t ~ "Map(0);"); 186 if (!map) { 187 call.raise("NullError"); 188 return; 189 } 190 map.data.clear(); 191 mixin("call.setForeign!" ~ t ~ "Map(map);"); 192 } 193 194 private void _set_(string t)(GrCall call) { 195 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 196 if (!map) { 197 call.raise("NullError"); 198 return; 199 } 200 static if (t == "Object") { 201 map.data[call.getString(1)] = call.getPtr(2); 202 } 203 else { 204 mixin("map.data[call.getString(1)] = call.get" ~ t ~ "(2);"); 205 } 206 } 207 208 private void _get_(string t)(GrCall call) { 209 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 210 if (!map) { 211 call.raise("NullError"); 212 return; 213 } 214 static if (t == "Object") { 215 call.setPtr(map.data[call.getString(1)]); 216 } 217 else { 218 mixin("call.set" ~ t ~ "(map.data[call.getString(1)]);"); 219 } 220 } 221 222 private void _has_(string t)(GrCall call) { 223 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 224 if (!map) { 225 call.raise("NullError"); 226 return; 227 } 228 call.setBool((call.getString(1) in map.data) !is null); 229 } 230 231 private void _remove_(string t)(GrCall call) { 232 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 233 if (!map) { 234 call.raise("NullError"); 235 return; 236 } 237 map.data.remove(call.getString(1)); 238 } 239 240 private void _byKeys_(string t)(GrCall call) { 241 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 242 if (!map) { 243 call.raise("NullError"); 244 return; 245 } 246 GrStringArray ary = new GrStringArray; 247 ary.data = map.data.keys; 248 call.setStringArray(ary); 249 } 250 251 private void _byValues_(string t)(GrCall call) { 252 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 253 if (!map) { 254 call.raise("NullError"); 255 return; 256 } 257 mixin("Gr" ~ t ~ "Array ary = new Gr" ~ t ~ "Array;"); 258 ary.data = map.data.values; 259 mixin("call.set" ~ t ~ "Array(ary);"); 260 } 261 262 private void _printb_(string t)(GrCall call) { 263 Map map = call.getForeign!(IntMap)(0); 264 if (!map) { 265 call.raise("NullError"); 266 return; 267 } 268 GrString result = "{"; 269 bool isFirst = true; 270 foreach (key, value; map.data) { 271 if (isFirst) { 272 isFirst = false; 273 } 274 else { 275 result ~= ", "; 276 } 277 result ~= "\"" ~ key ~ "\"=>" ~ to!string(cast(GrBool) value); 278 } 279 result ~= "}"; 280 _stdOut(result); 281 } 282 283 private void _printlb_(string t)(GrCall call) { 284 Map map = call.getForeign!(IntMap)(0); 285 if (!map) { 286 call.raise("NullError"); 287 return; 288 } 289 GrString result = "{"; 290 bool isFirst = true; 291 foreach (key, value; map.data) { 292 if (isFirst) { 293 isFirst = false; 294 } 295 else { 296 result ~= ", "; 297 } 298 result ~= "\"" ~ key ~ "\"=>" ~ to!string(cast(GrBool) value); 299 } 300 result ~= "}\n"; 301 _stdOut(result); 302 } 303 304 private void _each_(string t)(GrCall call) { 305 mixin(t ~ "Map map = call.getForeign!(" ~ t ~ "Map)(0);"); 306 if (!map) { 307 call.raise("NullError"); 308 return; 309 } 310 static if (t == "Int") { 311 MapIter!(GrInt) iter = new MapIter!(GrInt); 312 } 313 else static if (t == "Float") { 314 MapIter!(GrFloat) iter = new MapIter!(GrFloat); 315 } 316 else static if (t == "String") { 317 MapIter!(GrString) iter = new MapIter!(GrString); 318 } 319 else static if (t == "Object") { 320 MapIter!(GrPtr) iter = new MapIter!(GrPtr); 321 } 322 foreach (pair; map.data.byKeyValue()) { 323 iter.pairs ~= tuple(pair.key, pair.value); 324 } 325 call.setForeign(iter); 326 } 327 328 private void _next_(string t)(GrCall call) { 329 static if (t == "Int") { 330 MapIter!(GrInt) iter = call.getForeign!(MapIter!(GrInt))(0); 331 } 332 else static if (t == "Float") { 333 MapIter!(GrFloat) iter = call.getForeign!(MapIter!(GrFloat))(0); 334 } 335 else static if (t == "String") { 336 MapIter!(GrString) iter = call.getForeign!(MapIter!(GrString))(0); 337 } 338 else static if (t == "Object") { 339 MapIter!(GrPtr) iter = call.getForeign!(MapIter!(GrPtr))(0); 340 } 341 if (!iter) { 342 call.raise("NullError"); 343 return; 344 } 345 if (iter.index >= iter.pairs.length) { 346 call.setBool(false); 347 call.setPtr(null); 348 return; 349 } 350 call.setBool(true); 351 static if (t == "Int") { 352 GrObject obj = new GrObject(["first", "second"]); 353 obj.setString("first", iter.pairs[iter.index][0]); 354 obj.setInt("second", iter.pairs[iter.index][1]); 355 call.setObject(obj); 356 } 357 else static if (t == "Float") { 358 GrObject obj = new GrObject(["first", "second"]); 359 obj.setString("first", iter.pairs[iter.index][0]); 360 obj.setFloat("second", iter.pairs[iter.index][1]); 361 call.setObject(obj); 362 } 363 else static if (t == "String") { 364 GrObject obj = new GrObject(["first", "second"]); 365 obj.setString("first", iter.pairs[iter.index][0]); 366 obj.setString("second", iter.pairs[iter.index][1]); 367 call.setObject(obj); 368 } 369 else static if (t == "Object") { 370 GrObject obj = new GrObject(["first", "second"]); 371 obj.setString("first", iter.pairs[iter.index][0]); 372 obj.setPtr("second", iter.pairs[iter.index][1]); 373 call.setObject(obj); 374 } 375 iter.index++; 376 } 377 378 private void _print_(string t, bool newLine)(GrCall call) { 379 static if (t == "bool" || t == "int") { 380 IntMap map = call.getForeign!(IntMap)(0); 381 } 382 else static if (t == "float") { 383 FloatMap map = call.getForeign!(FloatMap)(0); 384 } 385 else static if (t == "string") { 386 StringMap map = call.getForeign!(StringMap)(0); 387 } 388 if (!map) { 389 call.raise("NullError"); 390 return; 391 } 392 GrString result = "{"; 393 bool isFirst = true; 394 foreach (key, value; map.data) { 395 if (isFirst) { 396 isFirst = false; 397 } 398 else { 399 result ~= ", "; 400 } 401 result ~= "\"" ~ key ~ "\"=>"; 402 static if (t == "string") { 403 result ~= "\"" ~ to!string(value) ~ "\""; 404 } 405 else static if (t == "bool") { 406 result ~= to!string(cast(bool) value); 407 } 408 else { 409 result ~= to!string(value); 410 } 411 } 412 result ~= newLine ? "}\n" : "}"; 413 _stdOut(result); 414 }