1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module grimoire.stdlib.array; 7 8 import std.range; 9 import grimoire.assembly, grimoire.compiler, grimoire.runtime; 10 11 package(grimoire.stdlib) void grLoadStdLibArray(GrLibrary library) { 12 library.addForeign("ArrayIter", ["T"]); 13 14 static foreach (t; ["Int", "Float", "String", "Object"]) { 15 mixin("GrType any" ~ t ~ "Array = grAny(\"A\", (type, data) { 16 if (type.baseType != GrBaseType.array_) 17 return false; 18 const GrType subType = grUnmangle(type.mangledType); 19 data.set(\"T\", subType); 20 return grIsKindOf" ~ t ~ "(subType.baseType); 21 }); 22 library.addPrimitive(&_copy_!\"" ~ t ~ "\", \"copy\", [any" 23 ~ t ~ "Array], [grAny(\"A\")]); 24 library.addPrimitive(&_size_!\"" ~ t ~ "\", \"size\", [any" ~ t ~ "Array], [grInt]); 25 library.addPrimitive(&_resize_!\"" ~ t ~ "\", \"resize\", [ 26 any" 27 ~ t ~ "Array, grInt 28 ], [grAny(\"A\")]); 29 library.addPrimitive(&_empty_!\"" ~ t ~ "\", \"empty?\", [ 30 any" ~ t ~ "Array 31 ], [grBool]); 32 library.addPrimitive(&_fill_!\"" ~ t ~ "\", \"fill\", [ 33 any" ~ t 34 ~ "Array, grAny(\"T\") 35 ], [grAny(\"A\")]); 36 library.addPrimitive(&_clear_!\"" ~ t ~ "\", \"clear\", [ 37 any" ~ t 38 ~ "Array 39 ], [grAny(\"A\")]); 40 library.addPrimitive(&_unshift_!\"" ~ t ~ "\", \"unshift\", [ 41 any" ~ t ~ "Array, grAny(\"T\") 42 ], [grAny(\"A\")]); 43 library.addPrimitive(&_push_!\"" ~ t 44 ~ "\", \"push\", [ 45 any" ~ t ~ "Array, grAny(\"T\") 46 ], [grAny(\"A\")]); 47 library.addPrimitive(&_shift_!\"" ~ t ~ "\", \"shift\", [ 48 any" ~ t ~ "Array 49 ], [grAny(\"T\")]); 50 library.addPrimitive(&_pop_!\"" ~ t 51 ~ "\", \"pop\", [ 52 any" ~ t ~ "Array 53 ], [grAny(\"T\")]); 54 library.addPrimitive(&_shift1_!\"" ~ t ~ "\", \"shift\", [ 55 any" ~ t ~ "Array, grInt 56 ], [grAny(\"A\")]); 57 library.addPrimitive(&_pop1_!\"" ~ t ~ "\", \"pop\", [ 58 any" ~ t 59 ~ "Array, grInt 60 ], [grAny(\"A\")]); 61 library.addPrimitive(&_first_!\"" ~ t ~ "\", \"first\", [ 62 any" ~ t ~ "Array 63 ], [grAny(\"T\")]); 64 library.addPrimitive(&_last_!\"" ~ t ~ "\", \"last\", [ 65 any" 66 ~ t ~ "Array 67 ], [grAny(\"T\")]); 68 library.addPrimitive(&_remove_!\"" ~ t ~ "\", \"remove\", [ 69 any" ~ t ~ "Array, grInt 70 ], [grAny(\"A\")]); 71 library.addPrimitive(&_remove2_!\"" ~ t ~ "\", \"remove\", [ 72 any" ~ t ~ "Array, grInt, grInt 73 ], [grAny(\"A\")]); 74 library.addPrimitive(&_slice_!\"" 75 ~ t ~ "\", \"slice!\", [ 76 any" ~ t ~ "Array, grInt, grInt 77 ], [grAny(\"A\")]); 78 library.addPrimitive(&_slice_copy_!\"" ~ t ~ "\", \"slice\", [ 79 any" ~ t ~ "Array, grInt, grInt 80 ], [grAny(\"A\")]); 81 library.addPrimitive(&_reverse_!\"" ~ t 82 ~ "\", \"reverse\", [ 83 any" ~ t ~ "Array 84 ], [grAny(\"A\")]); 85 library.addPrimitive(&_insert_!\"" ~ t ~ "\", \"insert\", [ 86 any" ~ t ~ "Array, grInt, grAny(\"T\") 87 ], [grAny(\"A\")]); 88 library.addPrimitive(&_each_!\"" ~ t 89 ~ "\", \"each\", [ 90 grAny(\"A\", (type, data) { 91 if (type.baseType != GrBaseType.array_) 92 return false; 93 const GrType subType = grUnmangle(type.mangledType); 94 data.set(\"R\", grGetForeignType(\"ArrayIter\", [subType])); 95 return grIsKindOf" ~ t ~ "(subType.baseType); 96 }) 97 ], [grAny(\"R\")]); 98 library.addPrimitive(&_next_!\"" 99 ~ t ~ "\", \"next\", [ 100 grAny(\"R\", (type, data) { 101 if (type.baseType != GrBaseType.foreign) 102 return false; 103 auto result = grUnmangleComposite(type.mangledType); 104 if(result.signature.length != 1 || result.name != \"ArrayIter\") 105 return false; 106 data.set(\"T\", result.signature[0]); 107 return grIsKindOf" ~ t ~ "(result.signature[0].baseType); 108 }) 109 ], [grBool, grAny(\"T\")]); 110 "); 111 112 static if (t != "Object") { 113 mixin(" 114 library.addPrimitive(&_sort_!\"" ~ t ~ "\", \"sort\", [ 115 any" ~ t ~ "Array 116 ], [grAny(\"A\")]); 117 library.addPrimitive(&_findFirst_!\"" ~ t ~ "\", \"findFirst\", [ 118 any" ~ t ~ "Array, grAny(\"T\") 119 ], [grInt]); 120 library.addPrimitive(&_findLast_!\"" ~ t 121 ~ "\", \"findLast\", [ 122 any" ~ t ~ "Array, grAny(\"T\") 123 ], [grInt]); 124 library.addPrimitive(&_findLast_!\"" ~ t ~ "\", \"findLast\", [ 125 any" ~ t 126 ~ "Array, grAny(\"T\") 127 ], [grInt]); 128 library.addPrimitive(&_has_!\"" ~ t 129 ~ "\", \"has?\", [ 130 any" ~ t ~ "Array, grAny(\"T\") 131 ], [grBool]); 132 "); 133 } 134 } 135 } 136 137 private void _copy_(string t)(GrCall call) { 138 mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array; 139 copy.data = call.get" 140 ~ t ~ "Array(0).data.dup; 141 call.set" ~ t ~ "Array(copy);"); 142 } 143 144 private void _size_(string t)(GrCall call) { 145 mixin("call.setInt(cast(GrInt) call.get" ~ t ~ "Array(0).data.length);"); 146 } 147 148 private void _resize_(string t)(GrCall call) { 149 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 150 const GrInt size = call.getInt(1); 151 if (size < 0) { 152 call.raise("ArgumentError"); 153 return; 154 } 155 static if (t == "Float") { 156 if (size > array.data.length) { 157 GrInt index = cast(GrInt) array.data.length; 158 array.data.length = size; 159 for (; index < array.data.length; ++index) 160 array.data[index] = 0f; 161 } 162 else { 163 array.data.length = size; 164 } 165 } 166 else { 167 array.data.length = size; 168 } 169 mixin("call.set" ~ t ~ "Array(array);"); 170 } 171 172 private void _empty_(string t)(GrCall call) { 173 mixin("const Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 174 call.setBool(array.data.empty); 175 } 176 177 private void _fill_(string t)(GrCall call) { 178 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 179 static if (t == "Object") { 180 GrPtr value = call.getPtr(1); 181 } 182 else { 183 mixin("auto value = call.get" ~ t ~ "(1);"); 184 } 185 for (size_t index; index < array.data.length; ++index) 186 array.data[index] = value; 187 mixin("call.set" ~ t ~ "Array(array);"); 188 } 189 190 private void _clear_(string t)(GrCall call) { 191 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 192 array.data.length = 0; 193 mixin("call.set" ~ t ~ "Array(array);"); 194 } 195 196 private void _unshift_(string t)(GrCall call) { 197 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 198 static if (t == "Object") { 199 array.data = call.getPtr(1) ~ array.data; 200 } 201 else { 202 mixin("array.data = call.get" ~ t ~ "(1) ~ array.data;"); 203 } 204 mixin("call.set" ~ t ~ "Array(array);"); 205 } 206 207 private void _push_(string t)(GrCall call) { 208 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 209 static if (t == "Object") { 210 array.data ~= call.getPtr(1); 211 } 212 else { 213 mixin("array.data ~= call.get" ~ t ~ "(1);"); 214 } 215 mixin("call.set" ~ t ~ "Array(array);"); 216 } 217 218 private void _shift_(string t)(GrCall call) { 219 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 220 if (!array.data.length) { 221 call.raise("IndexError"); 222 return; 223 } 224 static if (t == "Object") { 225 call.setPtr(array.data[0]); 226 } 227 else { 228 mixin("call.set" ~ t ~ "(array.data[0]);"); 229 } 230 array.data = array.data[1 .. $]; 231 } 232 233 private void _pop_(string t)(GrCall call) { 234 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 235 if (!array.data.length) { 236 call.raise("IndexError"); 237 return; 238 } 239 static if (t == "Object") { 240 call.setPtr(array.data[$ - 1]); 241 } 242 else { 243 mixin("call.set" ~ t ~ "(array.data[$ - 1]);"); 244 } 245 array.data.length--; 246 } 247 248 private void _shift1_(string t)(GrCall call) { 249 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 250 GrInt size = call.getInt(1); 251 if (size < 0) { 252 call.raise("IndexError"); 253 return; 254 } 255 if (array.data.length < size) { 256 size = cast(GrInt) array.data.length; 257 } 258 mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;"); 259 copy.data = array.data[0 .. size]; 260 array.data = array.data[size .. $]; 261 mixin("call.set" ~ t ~ "Array(copy);"); 262 } 263 264 private void _pop1_(string t)(GrCall call) { 265 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 266 GrInt size = call.getInt(1); 267 if (size < 0) { 268 call.raise("IndexError"); 269 return; 270 } 271 if (array.data.length < size) { 272 size = cast(GrInt) array.data.length; 273 } 274 mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;"); 275 copy.data = array.data[$ - size .. $]; 276 array.data.length -= size; 277 mixin("call.set" ~ t ~ "Array(copy);"); 278 } 279 280 private void _first_(string t)(GrCall call) { 281 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 282 if (!array.data.length) { 283 call.raise("IndexError"); 284 return; 285 } 286 static if (t == "Object") { 287 mixin("call.setPtr(array.data[0]);"); 288 } 289 else { 290 mixin("call.set" ~ t ~ "(array.data[0]);"); 291 } 292 } 293 294 private void _last_(string t)(GrCall call) { 295 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 296 if (!array.data.length) { 297 call.raise("IndexError"); 298 return; 299 } 300 static if (t == "Object") { 301 mixin("call.setPtr(array.data[$ - 1]);"); 302 } 303 else { 304 mixin("call.set" ~ t ~ "(array.data[$ - 1]);"); 305 } 306 } 307 308 private void _remove_(string t)(GrCall call) { 309 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 310 GrInt index = call.getInt(1); 311 if (index < 0) 312 index = (cast(GrInt) array.data.length) + index; 313 if (!array.data.length || index >= array.data.length || index < 0) { 314 mixin("call.set" ~ t ~ "Array(array);"); 315 return; 316 } 317 if (index + 1 == array.data.length) { 318 array.data.length--; 319 mixin("call.set" ~ t ~ "Array(array);"); 320 return; 321 } 322 if (index == 0) { 323 array.data = array.data[1 .. $]; 324 mixin("call.set" ~ t ~ "Array(array);"); 325 return; 326 } 327 array.data = array.data[0 .. index] ~ array.data[index + 1 .. $]; 328 mixin("call.set" ~ t ~ "Array(array);"); 329 } 330 331 private void _remove2_(string t)(GrCall call) { 332 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 333 GrInt index1 = call.getInt(1); 334 GrInt index2 = call.getInt(2); 335 if (index1 < 0) 336 index1 = (cast(GrInt) array.data.length) + index1; 337 if (index2 < 0) 338 index2 = (cast(GrInt) array.data.length) + index2; 339 340 if (index2 < index1) { 341 const GrInt temp = index1; 342 index1 = index2; 343 index2 = temp; 344 } 345 346 if (!array.data.length || index1 >= array.data.length || index2 < 0) { 347 mixin("call.set" ~ t ~ "Array(array);"); 348 return; 349 } 350 351 if (index1 < 0) 352 index1 = 0; 353 if (index2 >= array.data.length) 354 index2 = (cast(GrInt) array.data.length) - 1; 355 356 if (index1 == 0 && (index2 + 1) == array.data.length) { 357 array.data.length = 0; 358 mixin("call.set" ~ t ~ "Array(array);"); 359 return; 360 } 361 if (index1 == 0) { 362 array.data = array.data[(index2 + 1) .. $]; 363 mixin("call.set" ~ t ~ "Array(array);"); 364 return; 365 } 366 if ((index2 + 1) == array.data.length) { 367 array.data = array.data[0 .. index1]; 368 mixin("call.set" ~ t ~ "Array(array);"); 369 return; 370 } 371 array.data = array.data[0 .. index1] ~ array.data[(index2 + 1) .. $]; 372 mixin("call.set" ~ t ~ "Array(array);"); 373 } 374 375 private void _slice_(string t)(GrCall call) { 376 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 377 GrInt index1 = call.getInt(1); 378 GrInt index2 = call.getInt(2); 379 if (index1 < 0) 380 index1 = (cast(GrInt) array.data.length) + index1; 381 if (index2 < 0) 382 index2 = (cast(GrInt) array.data.length) + index2; 383 384 if (index2 < index1) { 385 const GrInt temp = index1; 386 index1 = index2; 387 index2 = temp; 388 } 389 390 if (!array.data.length || index1 >= array.data.length || index2 < 0) { 391 array.data.length = 0; 392 mixin("call.set" ~ t ~ "Array(array);"); 393 return; 394 } 395 396 if (index1 < 0) 397 index1 = 0; 398 if (index2 >= array.data.length) 399 index2 = (cast(GrInt) array.data.length - 1); 400 401 if (index1 == 0 && (index2 + 1) == array.data.length) { 402 mixin("call.set" ~ t ~ "Array(array);"); 403 return; 404 } 405 array.data = array.data[index1 .. index2 + 1]; 406 mixin("call.set" ~ t ~ "Array(array);"); 407 } 408 409 private void _slice_copy_(string t)(GrCall call) { 410 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 411 mixin("Gr" ~ t ~ "Array copy = new Gr" ~ t ~ "Array;"); 412 GrInt index1 = call.getInt(1); 413 GrInt index2 = call.getInt(2); 414 if (index1 < 0) 415 index1 = (cast(GrInt) array.data.length) + index1; 416 if (index2 < 0) 417 index2 = (cast(GrInt) array.data.length) + index2; 418 419 if (index2 < index1) { 420 const GrInt temp = index1; 421 index1 = index2; 422 index2 = temp; 423 } 424 425 if (!array.data.length || index1 >= array.data.length || index2 < 0) { 426 mixin("call.set" ~ t ~ "Array(copy);"); 427 return; 428 } 429 430 if (index1 < 0) 431 index1 = 0; 432 if (index2 >= array.data.length) 433 index2 = (cast(GrInt) array.data.length - 1); 434 435 if (index1 == 0 && (index2 + 1) == array.data.length) { 436 copy.data = array.data; 437 } 438 else { 439 copy.data = array.data[index1 .. index2 + 1]; 440 } 441 mixin("call.set" ~ t ~ "Array(copy);"); 442 } 443 444 private void _reverse_(string t)(GrCall call) { 445 import std.algorithm.mutation : reverse; 446 447 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 448 array.data = array.data.reverse; 449 mixin("call.set" ~ t ~ "Array(array);"); 450 } 451 452 private void _insert_(string t)(GrCall call) { 453 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 454 GrInt index = call.getInt(1); 455 static if (t == "Object") { 456 GrPtr value = call.getPtr(2); 457 } 458 else { 459 mixin("auto value = call.get" ~ t ~ "(2);"); 460 } 461 if (index < 0) 462 index = (cast(GrInt) array.data.length) + index; 463 if (!array.data.length || index >= array.data.length || index < 0) { 464 call.raise("IndexError"); 465 return; 466 } 467 if (index + 1 == array.data.length) { 468 array.data = array.data[0 .. index] ~ value ~ array.data[$ - 1]; 469 mixin("call.set" ~ t ~ "Array(array);"); 470 return; 471 } 472 if (index == 0) { 473 array.data = value ~ array.data; 474 mixin("call.set" ~ t ~ "Array(array);"); 475 return; 476 } 477 array.data = array.data[0 .. index] ~ value ~ array.data[index .. $]; 478 mixin("call.set" ~ t ~ "Array(array);"); 479 } 480 481 private void _sort_(string t)(GrCall call) { 482 import std.algorithm.sorting : sort; 483 484 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 485 array.data.sort(); 486 mixin("call.set" ~ t ~ "Array(array);"); 487 } 488 489 private void _findFirst_(string t)(GrCall call) { 490 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 491 mixin("auto value = call.get" ~ t ~ "(1);"); 492 for (GrInt index; index < array.data.length; ++index) { 493 if (array.data[index] == value) { 494 call.setInt(index); 495 return; 496 } 497 } 498 call.setInt(-1); 499 } 500 501 private void _findLast_(string t)(GrCall call) { 502 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 503 mixin("auto value = call.get" ~ t ~ "(1);"); 504 for (GrInt index = (cast(GrInt) array.data.length) - 1; index > 0; --index) { 505 if (array.data[index] == value) { 506 call.setInt(index); 507 return; 508 } 509 } 510 call.setInt(-1); 511 } 512 513 private void _has_(string t)(GrCall call) { 514 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 515 mixin("auto value = call.get" ~ t ~ "(1);"); 516 for (GrInt index; index < array.data.length; ++index) { 517 if (array.data[index] == value) { 518 call.setBool(true); 519 return; 520 } 521 } 522 call.setBool(false); 523 } 524 525 private final class ArrayIter(T) { 526 T[] array; 527 size_t index; 528 } 529 530 private void _each_(string t)(GrCall call) { 531 mixin("Gr" ~ t ~ "Array array = call.get" ~ t ~ "Array(0);"); 532 static if (t == "Int") { 533 ArrayIter!(GrInt) iter = new ArrayIter!(GrInt); 534 } 535 else static if (t == "Float") { 536 ArrayIter!(GrFloat) iter = new ArrayIter!(GrFloat); 537 } 538 else static if (t == "String") { 539 ArrayIter!(GrString) iter = new ArrayIter!(GrString); 540 } 541 else static if (t == "Object") { 542 ArrayIter!(GrPtr) iter = new ArrayIter!(GrPtr); 543 } 544 iter.array = array.data.dup; 545 call.setForeign(iter); 546 } 547 548 private void _next_(string t)(GrCall call) { 549 static if (t == "Int") { 550 ArrayIter!(GrInt) iter = call.getForeign!(ArrayIter!(GrInt))(0); 551 } 552 else static if (t == "Float") { 553 ArrayIter!(GrFloat) iter = call.getForeign!(ArrayIter!(GrFloat))(0); 554 } 555 else static if (t == "String") { 556 ArrayIter!(GrString) iter = call.getForeign!(ArrayIter!(GrString))(0); 557 } 558 else static if (t == "Object") { 559 ArrayIter!(GrPtr) iter = call.getForeign!(ArrayIter!(GrPtr))(0); 560 } 561 if (!iter) { 562 call.raise("NullError"); 563 return; 564 } 565 if (iter.index >= iter.array.length) { 566 call.setBool(false); 567 static if (t == "Int") { 568 call.setInt(0); 569 } 570 else static if (t == "Float") { 571 call.setFloat(0f); 572 } 573 else static if (t == "String") { 574 call.setString(""); 575 } 576 else static if (t == "Object") { 577 call.setPtr(null); 578 } 579 return; 580 } 581 call.setBool(true); 582 static if (t == "Int") { 583 call.setInt(iter.array[iter.index]); 584 } 585 else static if (t == "Float") { 586 call.setFloat(iter.array[iter.index]); 587 } 588 else static if (t == "String") { 589 call.setString(iter.array[iter.index]); 590 } 591 else static if (t == "Object") { 592 call.setPtr(iter.array[iter.index]); 593 } 594 iter.index++; 595 }