1 module wren.core; 2 import wren.math; 3 import wren.primitive; 4 import wren.value; 5 import wren.vm; 6 7 // Throwing exception support 8 import dplug.core : mallocNew; 9 10 // The core module source that is interpreted whenever core is initialized. 11 private static const(char)[] coreModuleSource = import("wren_core.wren"); 12 13 /++ Boolean primitives +/ 14 @WrenPrimitive("Bool", "!") 15 bool bool_not(WrenVM* vm, Value* args) @nogc 16 { 17 return RETURN_BOOL(args, !AS_BOOL(args[0])); 18 } 19 20 @WrenPrimitive("Bool", "toString") 21 bool bool_toString(WrenVM* vm, Value* args) @nogc 22 { 23 if (AS_BOOL(args[0])) 24 { 25 return RETURN_VAL(args, CONST_STRING(vm, "true")); 26 } 27 else 28 { 29 return RETURN_VAL(args, CONST_STRING(vm, "false")); 30 } 31 } 32 33 /++ Class primitives +/ 34 @WrenPrimitive("Class", "name") 35 bool class_name(WrenVM* vm, Value* args) @nogc 36 { 37 return RETURN_OBJ(args, AS_CLASS(args[0]).name); 38 } 39 40 @WrenPrimitive("Class", "supertype") 41 bool class_supertype(WrenVM* vm, Value* args) @nogc 42 { 43 ObjClass* classObj = AS_CLASS(args[0]); 44 45 // Object has no superclass. 46 if (classObj.superclass == null) return RETURN_NULL(args); 47 48 return RETURN_OBJ(args, classObj.superclass); 49 } 50 51 @WrenPrimitive("Class", "toString") 52 bool class_toString(WrenVM* vm, Value* args) @nogc 53 { 54 return RETURN_OBJ(args, AS_CLASS(args[0]).name); 55 } 56 57 @WrenPrimitive("Class", "attributes") 58 bool class_attributes(WrenVM* vm, Value* args) @nogc 59 { 60 return RETURN_VAL(args, AS_CLASS(args[0]).attributes); 61 } 62 63 /++ Fiber primitives +/ 64 @WrenPrimitive("Fiber", "new(_)", MethodType.METHOD_PRIMITIVE, true) 65 bool fiber_new(WrenVM* vm, Value* args) @nogc 66 { 67 if (!validateFn(vm, args[1], "Argument")) return false; 68 69 ObjClosure* closure = AS_CLOSURE(args[1]); 70 if (closure.fn.arity > 1) 71 { 72 return RETURN_ERROR(vm, "Function cannot take more than one parameter."); 73 } 74 75 return RETURN_OBJ(args, wrenNewFiber(vm, closure)); 76 } 77 78 @WrenPrimitive("Fiber", "abort(_)", MethodType.METHOD_PRIMITIVE, true) 79 bool fiber_abort(WrenVM* vm, Value* args) @nogc 80 { 81 vm.fiber.error = args[1]; 82 83 // If the error is explicitly null, it's not really an abort. 84 return IS_NULL(args[1]); 85 } 86 87 @WrenPrimitive("Fiber", "current", MethodType.METHOD_PRIMITIVE, true) 88 bool fiber_current(WrenVM* vm, Value* args) @nogc 89 { 90 return RETURN_OBJ(args, vm.fiber); 91 } 92 93 @WrenPrimitive("Fiber", "suspend()", MethodType.METHOD_PRIMITIVE, true) 94 bool fiber_suspend(WrenVM* vm, Value* args) @nogc 95 { 96 return RETURN_VAL(args, AS_FIBER(args[0]).error); 97 } 98 99 @WrenPrimitive("Fiber", "yield()", MethodType.METHOD_PRIMITIVE, true) 100 bool fiber_yield(WrenVM* vm, Value* args) @nogc 101 { 102 ObjFiber* current = vm.fiber; 103 vm.fiber = current.caller; 104 105 // Unhook this fiber from the one that called it. 106 current.caller = null; 107 current.state = FiberState.FIBER_OTHER; 108 109 if (vm.fiber != null) 110 { 111 // Make the caller's run method return null. 112 vm.fiber.stackTop[-1] = NULL_VAL; 113 } 114 115 return false; 116 } 117 118 @WrenPrimitive("Fiber", "yield(_)", MethodType.METHOD_PRIMITIVE, true) 119 bool fiber_yield1(WrenVM* vm, Value* args) @nogc 120 { 121 ObjFiber* current = vm.fiber; 122 vm.fiber = current.caller; 123 124 // Unhook this fiber from the one that called it. 125 current.caller = null; 126 current.state = FiberState.FIBER_OTHER; 127 128 if (vm.fiber != null) 129 { 130 // Make the caller's run method return the argument passed to yield. 131 vm.fiber.stackTop[-1] = args[1]; 132 133 // When the yielding fiber resumes, we'll store the result of the yield 134 // call in its stack. Since Fiber.yield(value) has two arguments (the Fiber 135 // class and the value) and we only need one slot for the result, discard 136 // the other slot now. 137 current.stackTop--; 138 } 139 140 return false; 141 } 142 143 // Transfer execution to [fiber] coming from the current fiber whose stack has 144 // [args]. 145 // 146 // [isCall] is true if [fiber] is being called and not transferred. 147 // 148 // [hasValue] is true if a value in [args] is being passed to the new fiber. 149 // Otherwise, `null` is implicitly being passed. 150 static bool runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, bool isCall, 151 bool hasValue, const(char)* verb) @nogc 152 { 153 if (wrenHasError(fiber)) 154 { 155 return RETURN_ERROR(vm, "Cannot $ an aborted fiber.", verb); 156 } 157 158 if (isCall) 159 { 160 // You can't call a called fiber, but you can transfer directly to it, 161 // which is why this check is gated on `isCall`. This way, after resuming a 162 // suspended fiber, it will run and then return to the fiber that called it 163 // and so on. 164 if (fiber.caller != null) return RETURN_ERROR(vm, "Fiber has already been called."); 165 166 if (fiber.state == FiberState.FIBER_ROOT) return RETURN_ERROR(vm, "Cannot call root fiber."); 167 168 // Remember who ran it. 169 fiber.caller = vm.fiber; 170 } 171 172 if (fiber.numFrames == 0) 173 { 174 return RETURN_ERROR(vm, "Cannot $ a finished fiber.", verb); 175 } 176 177 // When the calling fiber resumes, we'll store the result of the call in its 178 // stack. If the call has two arguments (the fiber and the value), we only 179 // need one slot for the result, so discard the other slot now. 180 if (hasValue) vm.fiber.stackTop--; 181 182 if (fiber.numFrames == 1 && 183 fiber.frames[0].ip == fiber.frames[0].closure.fn.code.data) 184 { 185 // The fiber is being started for the first time. If its function takes a 186 // parameter, bind an argument to it. 187 if (fiber.frames[0].closure.fn.arity == 1) 188 { 189 fiber.stackTop[0] = hasValue ? args[1] : NULL_VAL; 190 fiber.stackTop++; 191 } 192 } 193 else 194 { 195 // The fiber is being resumed, make yield() or transfer() return the result. 196 fiber.stackTop[-1] = hasValue ? args[1] : NULL_VAL; 197 } 198 199 vm.fiber = fiber; 200 return false; 201 } 202 203 @WrenPrimitive("Fiber", "call()") 204 bool fiber_call(WrenVM* vm, Value* args) @nogc 205 { 206 return runFiber(vm, AS_FIBER(args[0]), args, true, false, "call"); 207 } 208 209 @WrenPrimitive("Fiber", "call(_)") 210 bool fiber_call1(WrenVM* vm, Value* args) @nogc 211 { 212 return runFiber(vm, AS_FIBER(args[0]), args, true, true, "call"); 213 } 214 215 @WrenPrimitive("Fiber", "error") 216 bool fiber_error(WrenVM* vm, Value* args) @nogc 217 { 218 return RETURN_VAL(args, AS_FIBER(args[0]).error); 219 } 220 221 @WrenPrimitive("Fiber", "isDone") 222 bool fiber_isDone(WrenVM* vm, Value* args) @nogc 223 { 224 ObjFiber* runFiber = AS_FIBER(args[0]); 225 return RETURN_BOOL(args, runFiber.numFrames == 0 || wrenHasError(runFiber)); 226 } 227 228 @WrenPrimitive("Fiber", "transfer()") 229 bool fiber_transfer(WrenVM* vm, Value* args) @nogc 230 { 231 return runFiber(vm, AS_FIBER(args[0]), args, false, false, "transfer to"); 232 } 233 234 @WrenPrimitive("Fiber", "transfer(_)") 235 bool fiber_transfer1(WrenVM* vm, Value* args) @nogc 236 { 237 return runFiber(vm, AS_FIBER(args[0]), args, false, true, "transfer to"); 238 } 239 240 @WrenPrimitive("Fiber", "transferError(_)") 241 bool fiber_transferError(WrenVM* vm, Value* args) @nogc 242 { 243 runFiber(vm, AS_FIBER(args[0]), args, false, true, "transfer to"); 244 vm.fiber.error = args[1]; 245 return false; 246 } 247 248 @WrenPrimitive("Fiber", "try()") 249 bool fiber_try(WrenVM* vm, Value* args) @nogc 250 { 251 runFiber(vm, AS_FIBER(args[0]), args, true, false, "try"); 252 253 // If we're switching to a valid fiber to try, remember that we're trying it. 254 if (!wrenHasError(vm.fiber)) vm.fiber.state = FiberState.FIBER_TRY; 255 return false; 256 } 257 258 @WrenPrimitive("Fiber", "try(_)") 259 bool fiber_try1(WrenVM* vm, Value* args) @nogc 260 { 261 runFiber(vm, AS_FIBER(args[0]), args, true, true, "try"); 262 263 // If we're switching to a valid fiber to try, remember that we're trying it. 264 if (!wrenHasError(vm.fiber)) vm.fiber.state = FiberState.FIBER_TRY; 265 return false; 266 } 267 268 /++ Fn primitives +/ 269 270 @WrenPrimitive("Fn", "new(_)", MethodType.METHOD_PRIMITIVE, true) 271 bool fn_new(WrenVM* vm, Value* args) @nogc 272 { 273 if (!validateFn(vm, args[1], "Argument")) return false; 274 275 // The block argument is already a function, so just return it. 276 return RETURN_VAL(args, args[1]); 277 } 278 279 @WrenPrimitive("Fn", "arity") 280 bool fn_arity(WrenVM* vm, Value* args) @nogc 281 { 282 return RETURN_NUM(args, AS_CLOSURE(args[0]).fn.arity); 283 } 284 285 static void call_fn(WrenVM* vm, Value* args, int numArgs) @nogc 286 { 287 // +1 to include the function itself. 288 wrenCallFunction(vm, vm.fiber, AS_CLOSURE(args[0]), numArgs + 1); 289 } 290 291 @WrenPrimitive("Fn", "call()", MethodType.METHOD_FUNCTION_CALL) 292 bool fn_call0(WrenVM* vm, Value* args) @nogc 293 { 294 call_fn(vm, args, 0); 295 return false; 296 } 297 298 // This mixin is a mess, but we need to generate the primitives 299 // to allow a user to call a function with up to 16 arguments. 300 // This is the cleanest that I could make it, but it's still definitely a mess. 301 mixin(() { 302 import std.format : format; 303 import std.range : repeat, join; 304 305 string ret = ""; 306 307 // Build up our argument array here 308 foreach(i; 1 .. 17) { 309 string args = "_" ~ ",_".repeat(i - 1).join; 310 // God have mercy on my soul for this format string 311 ret ~= format!q{ 312 @WrenPrimitive("Fn", "call(%1$s)", MethodType.METHOD_FUNCTION_CALL) 313 bool fn_call%2$d(WrenVM* vm, Value* args) @nogc 314 { 315 call_fn(vm, args, %2$d); 316 return false; 317 } 318 }(args, i); 319 } 320 321 return ret; 322 }()); 323 324 @WrenPrimitive("Fn", "toString") 325 bool fn_toString(WrenVM* vm, Value* args) @nogc 326 { 327 return RETURN_VAL(args, CONST_STRING(vm, "<fn>")); 328 } 329 330 /++ List primitives +/ 331 @WrenPrimitive("List", "filled(_,_)", MethodType.METHOD_PRIMITIVE, true) 332 bool list_filled(WrenVM* vm, Value* args) @nogc 333 { 334 if (!validateInt(vm, args[1], "Size")) return false; 335 if (AS_NUM(args[1]) < 0) return RETURN_ERROR(vm, "Size cannot be negative."); 336 337 uint size = cast(uint)AS_NUM(args[1]); 338 ObjList* list = wrenNewList(vm, size); 339 340 for (uint i = 0; i < size; i++) 341 { 342 list.elements.data[i] = args[2]; 343 } 344 345 return RETURN_OBJ(args, list); 346 } 347 348 @WrenPrimitive("List", "new()", MethodType.METHOD_PRIMITIVE, true) 349 bool list_new(WrenVM* vm, Value* args) @nogc 350 { 351 return RETURN_OBJ(args, wrenNewList(vm, 0)); 352 } 353 354 @WrenPrimitive("List", "[_]") 355 bool list_subscript(WrenVM* vm, Value* args) @nogc 356 { 357 ObjList* list = AS_LIST(args[0]); 358 359 if (IS_NUM(args[1])) 360 { 361 uint index = validateIndex(vm, args[1], list.elements.count, 362 "Subscript"); 363 if (index == uint.max) return false; 364 365 return RETURN_VAL(args, list.elements.data[index]); 366 } 367 368 if (!IS_RANGE(args[1])) 369 { 370 return RETURN_ERROR(vm, "Subscript must be a number or a range."); 371 } 372 373 int step; 374 uint count = list.elements.count; 375 uint start = calculateRange(vm, AS_RANGE(args[1]), &count, &step); 376 if (start == uint.max) return false; 377 378 ObjList* result = wrenNewList(vm, count); 379 for (uint i = 0; i < count; i++) 380 { 381 result.elements.data[i] = list.elements.data[start + i * step]; 382 } 383 384 return RETURN_OBJ(args, result); 385 } 386 387 @WrenPrimitive("List", "[_]=(_)") 388 bool list_subscriptSetter(WrenVM* vm, Value* args) @nogc 389 { 390 ObjList* list = AS_LIST(args[0]); 391 uint index = validateIndex(vm, args[1], list.elements.count, 392 "Subscript"); 393 if (index == uint.max) return false; 394 395 list.elements.data[index] = args[2]; 396 return RETURN_VAL(args, args[2]); 397 } 398 399 @WrenPrimitive("List", "add(_)") 400 bool list_add(WrenVM* vm, Value* args) @nogc 401 { 402 wrenValueBufferWrite(vm, &AS_LIST(args[0]).elements, args[1]); 403 return RETURN_VAL(args, args[1]); 404 } 405 406 // Adds an element to the list and then returns the list itself. This is called 407 // by the compiler when compiling list literals instead of using add() to 408 // minimize stack churn. 409 @WrenPrimitive("List", "addCore_(_)") 410 bool list_addCore(WrenVM* vm, Value* args) @nogc 411 { 412 wrenValueBufferWrite(vm, &AS_LIST(args[0]).elements, args[1]); 413 414 // Return the list. 415 return RETURN_VAL(args, args[0]); 416 } 417 418 @WrenPrimitive("List", "clear()") 419 bool list_clear(WrenVM* vm, Value* args) @nogc 420 { 421 wrenValueBufferClear(vm, &AS_LIST(args[0]).elements); 422 return RETURN_NULL(args); 423 } 424 425 @WrenPrimitive("List", "count") 426 bool list_count(WrenVM* vm, Value* args) @nogc 427 { 428 return RETURN_NUM(args, AS_LIST(args[0]).elements.count); 429 } 430 431 @WrenPrimitive("List", "insert(_,_)") 432 bool list_insert(WrenVM* vm, Value* args) @nogc 433 { 434 ObjList* list = AS_LIST(args[0]); 435 436 // count + 1 here so you can "insert" at the very end. 437 uint index = validateIndex(vm, args[1], list.elements.count + 1, "Index"); 438 if (index == uint.max) return false; 439 440 wrenListInsert(vm, list, args[2], index); 441 return RETURN_VAL(args, args[2]); 442 } 443 444 @WrenPrimitive("List", "iterate(_)") 445 bool list_iterate(WrenVM* vm, Value* args) @nogc 446 { 447 ObjList* list = AS_LIST(args[0]); 448 449 // If we're starting the iteration, return the first index. 450 if (IS_NULL(args[1])) 451 { 452 if (list.elements.count == 0) return RETURN_FALSE(args); 453 return RETURN_NUM(args, 0); 454 } 455 456 if (!validateInt(vm, args[1], "Iterator")) return false; 457 458 // Stop if we're out of bounds. 459 double index = AS_NUM(args[1]); 460 if (index < 0 || index >= list.elements.count - 1) return RETURN_FALSE(args); 461 462 // Otherwise, move to the next index. 463 return RETURN_NUM(args, index + 1); 464 } 465 466 @WrenPrimitive("List", "iteratorValue(_)") 467 bool list_iteratorValue(WrenVM* vm, Value* args) @nogc 468 { 469 ObjList* list = AS_LIST(args[0]); 470 uint index = validateIndex(vm, args[1], list.elements.count, "Iterator"); 471 if (index == uint.max) return false; 472 473 return RETURN_VAL(args, list.elements.data[index]); 474 } 475 476 @WrenPrimitive("List", "removeAt(_)") 477 bool list_removeAt(WrenVM* vm, Value* args) @nogc 478 { 479 ObjList* list = AS_LIST(args[0]); 480 uint index = validateIndex(vm, args[1], list.elements.count, "Index"); 481 if (index == uint.max) return false; 482 483 return RETURN_VAL(args, wrenListRemoveAt(vm, list, index)); 484 } 485 486 @WrenPrimitive("List", "remove(_)") 487 bool list_removeValue(WrenVM* vm, Value* args) @nogc 488 { 489 ObjList* list = AS_LIST(args[0]); 490 int index = wrenListIndexOf(vm, list, args[1]); 491 if (index == -1) return RETURN_NULL(args); 492 return RETURN_VAL(args, wrenListRemoveAt(vm, list, index)); 493 } 494 495 @WrenPrimitive("List", "indexOf(_)") 496 bool list_indexOf(WrenVM* vm, Value* args) @nogc 497 { 498 ObjList* list = AS_LIST(args[0]); 499 return RETURN_NUM(args, wrenListIndexOf(vm, list, args[1])); 500 } 501 502 @WrenPrimitive("List", "swap(_,_)") 503 bool list_swap(WrenVM* vm, Value* args) @nogc 504 { 505 ObjList* list = AS_LIST(args[0]); 506 uint indexA = validateIndex(vm, args[1], list.elements.count, "Index 0"); 507 if (indexA == uint.max) return false; 508 uint indexB = validateIndex(vm, args[2], list.elements.count, "Index 1"); 509 if (indexB == uint.max) return false; 510 511 Value a = list.elements.data[indexA]; 512 list.elements.data[indexA] = list.elements.data[indexB]; 513 list.elements.data[indexB] = a; 514 515 return RETURN_NULL(args); 516 } 517 /++ Null primitives +/ 518 @WrenPrimitive("Null", "!") 519 bool null_not(WrenVM* vm, Value* args) @nogc 520 { 521 return RETURN_VAL(args, TRUE_VAL); 522 } 523 524 @WrenPrimitive("Null", "toString") 525 bool null_toString(WrenVM* vm, Value* args) @nogc 526 { 527 return RETURN_VAL(args, CONST_STRING(vm, "null")); 528 } 529 530 /++ Num primitives +/ 531 532 // Porting over macros is always a joy. 533 private string DEF_NUM_CONSTANT(string name, string val) 534 { 535 import std.format : format; 536 537 return format!q{ 538 @WrenPrimitive("Num", "%1$s", MethodType.METHOD_PRIMITIVE, true) 539 bool num_%1$s(WrenVM* vm, Value* args) @nogc 540 { 541 return RETURN_NUM(args, %2$s); 542 } 543 }(name, val); 544 } 545 546 mixin(DEF_NUM_CONSTANT("infinity", "double.infinity")); 547 mixin(DEF_NUM_CONSTANT("nan", "WREN_DOUBLE_NAN")); 548 mixin(DEF_NUM_CONSTANT("pi", "3.14159265358979323846264338327950288")); 549 mixin(DEF_NUM_CONSTANT("tau", "6.28318530717958647692528676655900577")); 550 551 mixin(DEF_NUM_CONSTANT("largest", "double.max")); 552 mixin(DEF_NUM_CONSTANT("smallest", "double.min_normal")); 553 554 mixin(DEF_NUM_CONSTANT("maxSafeInteger", "9007199254740991.0")); 555 mixin(DEF_NUM_CONSTANT("minSafeInteger", "-9007199254740991.0")); 556 557 private string DEF_NUM_INFIX(string name, string op, string type) 558 { 559 import std.format : format; 560 561 return format!q{ 562 @WrenPrimitive("Num", "%2$s(_)") 563 bool num_%1$s(WrenVM* vm, Value* args) @nogc 564 { 565 if (!validateNum(vm, args[1], "Right operand")) return false; 566 return RETURN_%3$s(args, AS_NUM(args[0]) %2$s AS_NUM(args[1])); 567 } 568 }(name, op, type); 569 } 570 571 mixin(DEF_NUM_INFIX("minus", "-", "NUM")); 572 mixin(DEF_NUM_INFIX("plus", "+", "NUM")); 573 mixin(DEF_NUM_INFIX("multiply", "*", "NUM")); 574 mixin(DEF_NUM_INFIX("divide", "/", "NUM")); 575 mixin(DEF_NUM_INFIX("lt", "<", "BOOL")); 576 mixin(DEF_NUM_INFIX("gt", ">", "BOOL")); 577 mixin(DEF_NUM_INFIX("lte", "<=", "BOOL")); 578 mixin(DEF_NUM_INFIX("gte", ">=", "BOOL")); 579 580 private string DEF_NUM_BITWISE(string name, string op) 581 { 582 import std.format : format; 583 584 return format!q{ 585 @WrenPrimitive("Num", "%2$s(_)") 586 bool num_bitwise%1$s(WrenVM* vm, Value* args) @nogc 587 { 588 if (!validateNum(vm, args[1], "Right operand")) return false; 589 uint left = cast(uint)AS_NUM(args[0]); 590 uint right = cast(uint)AS_NUM(args[1]); 591 return RETURN_NUM(args, left %2$s right); 592 } 593 }(name, op); 594 } 595 596 mixin(DEF_NUM_BITWISE("And", "&")); 597 mixin(DEF_NUM_BITWISE("Or", "|")); 598 mixin(DEF_NUM_BITWISE("Xor", "^")); 599 mixin(DEF_NUM_BITWISE("LeftShift", "<<")); 600 mixin(DEF_NUM_BITWISE("RightShift", ">>")); 601 602 private string DEF_NUM_FN(string name, string fn) 603 { 604 import std.format : format; 605 606 return format!q{ 607 @WrenPrimitive("Num", "%1$s") 608 bool num_%1$s(WrenVM* vm, Value* args) @nogc 609 { 610 import core.stdc.math : %2$s; 611 return RETURN_NUM(args, %2$s(AS_NUM(args[0]))); 612 } 613 }(name, fn); 614 } 615 616 mixin(DEF_NUM_FN("abs", "fabs")); 617 mixin(DEF_NUM_FN("acos", "acos")); 618 mixin(DEF_NUM_FN("asin", "asin")); 619 mixin(DEF_NUM_FN("atan", "atan")); 620 mixin(DEF_NUM_FN("cbrt", "cbrt")); 621 mixin(DEF_NUM_FN("ceil", "ceil")); 622 mixin(DEF_NUM_FN("cos", "cos")); 623 mixin(DEF_NUM_FN("floor", "floor")); 624 mixin(DEF_NUM_FN("round", "round")); 625 mixin(DEF_NUM_FN("sin", "sin")); 626 mixin(DEF_NUM_FN("sqrt", "sqrt")); 627 mixin(DEF_NUM_FN("tan", "tan")); 628 mixin(DEF_NUM_FN("log", "log")); 629 mixin(DEF_NUM_FN("log2", "log2")); 630 mixin(DEF_NUM_FN("exp", "exp")); 631 632 @WrenPrimitive("Num", "-") 633 bool num_negate(WrenVM* vm, Value* args) @nogc 634 { 635 return RETURN_NUM(args, -AS_NUM(args[0])); 636 } 637 638 @WrenPrimitive("Num", "%(_)") 639 bool num_mod(WrenVM* vm, Value* args) @nogc 640 { 641 import core.stdc.math : fmod; 642 if (!validateNum(vm, args[1], "Right operand")) return false; 643 return RETURN_NUM(args, fmod(AS_NUM(args[0]), AS_NUM(args[1]))); 644 } 645 646 @WrenPrimitive("Num", "==(_)") 647 bool num_eqeq(WrenVM* vm, Value* args) @nogc 648 { 649 if (!IS_NUM(args[1])) return RETURN_FALSE(args); 650 return RETURN_BOOL(args, AS_NUM(args[0]) == AS_NUM(args[1])); 651 } 652 653 @WrenPrimitive("Num", "!=(_)") 654 bool num_bangeq(WrenVM* vm, Value* args) @nogc 655 { 656 if (!IS_NUM(args[1])) return RETURN_TRUE(args); 657 return RETURN_BOOL(args, AS_NUM(args[0]) != AS_NUM(args[1])); 658 } 659 660 @WrenPrimitive("Num", "~") 661 bool num_bitwiseNot(WrenVM* vm, Value* args) @nogc 662 { 663 // Bitwise operators always work on 32-bit unsigned ints. 664 return RETURN_NUM(args, ~cast(uint)(AS_NUM(args[0]))); 665 } 666 667 @WrenPrimitive("Num", "..(_)") 668 bool num_dotDot(WrenVM* vm, Value* args) @nogc 669 { 670 if (!validateNum(vm, args[1], "Right hand side of range")) return false; 671 672 double from = AS_NUM(args[0]); 673 double to = AS_NUM(args[1]); 674 return RETURN_VAL(args, wrenNewRange(vm, from, to, true)); 675 } 676 677 @WrenPrimitive("Num", "...(_)") 678 bool num_dotDotDot(WrenVM* vm, Value* args) @nogc 679 { 680 if (!validateNum(vm, args[1], "Right hand side of range")) return false; 681 682 double from = AS_NUM(args[0]); 683 double to = AS_NUM(args[1]); 684 return RETURN_VAL(args, wrenNewRange(vm, from, to, false)); 685 } 686 687 @WrenPrimitive("Num", "atan2(_)") 688 bool num_atan2(WrenVM* vm, Value* args) @nogc 689 { 690 import core.stdc.math : atan2; 691 if (!validateNum(vm, args[1], "x value")) return false; 692 693 return RETURN_NUM(args, atan2(AS_NUM(args[0]), AS_NUM(args[1]))); 694 } 695 696 @WrenPrimitive("Num", "min(_)") 697 bool num_min(WrenVM* vm, Value* args) @nogc 698 { 699 if (!validateNum(vm, args[1], "Other value")) return false; 700 701 double value = AS_NUM(args[0]); 702 double other = AS_NUM(args[1]); 703 return RETURN_NUM(args, value <= other ? value : other); 704 } 705 706 @WrenPrimitive("Num", "max(_)") 707 bool num_max(WrenVM* vm, Value* args) @nogc 708 { 709 if (!validateNum(vm, args[1], "Other value")) return false; 710 711 double value = AS_NUM(args[0]); 712 double other = AS_NUM(args[1]); 713 return RETURN_NUM(args, value > other ? value : other); 714 } 715 716 @WrenPrimitive("Num", "clamp(_,_)") 717 bool num_clamp(WrenVM* vm, Value* args) @nogc 718 { 719 if (!validateNum(vm, args[1], "Min value")) return false; 720 if (!validateNum(vm, args[2], "Max value")) return false; 721 722 double value = AS_NUM(args[0]); 723 double min = AS_NUM(args[1]); 724 double max = AS_NUM(args[2]); 725 double result = (value < min) ? min : ((value > max) ? max : value); 726 return RETURN_NUM(args, result); 727 } 728 729 @WrenPrimitive("Num", "pow(_)") 730 bool num_pow(WrenVM* vm, Value* args) @nogc 731 { 732 import core.stdc.math : pow; 733 if (!validateNum(vm, args[1], "Power value")) return false; 734 735 return RETURN_NUM(args, pow(AS_NUM(args[0]), AS_NUM(args[1]))); 736 } 737 738 @WrenPrimitive("Num", "fraction") 739 bool num_fraction(WrenVM* vm, Value* args) @nogc 740 { 741 import core.stdc.math : modf; 742 743 double unused; 744 return RETURN_NUM(args, modf(AS_NUM(args[0]), &unused)); 745 } 746 747 @WrenPrimitive("Num", "isInfinity") 748 bool num_isInfinity(WrenVM* vm, Value* args) @nogc 749 { 750 import core.stdc.math : isinf; 751 return RETURN_BOOL(args, isinf(AS_NUM(args[0])) == 1); 752 } 753 754 @WrenPrimitive("Num", "isNan") 755 bool num_isNan(WrenVM* vm, Value* args) @nogc 756 { 757 import core.stdc.math : isnan; 758 return RETURN_BOOL(args, isnan(AS_NUM(args[0])) == 1); 759 } 760 761 @WrenPrimitive("Num", "sign") 762 bool num_sign(WrenVM* vm, Value* args) @nogc 763 { 764 double value = AS_NUM(args[0]); 765 if (value > 0) 766 { 767 return RETURN_NUM(args, 1); 768 } 769 else if (value < 0) 770 { 771 return RETURN_NUM(args, -1); 772 } 773 else 774 { 775 return RETURN_NUM(args, 0); 776 } 777 } 778 779 @WrenPrimitive("Num", "toString") 780 bool num_toString(WrenVM* vm, Value* args) @nogc 781 { 782 return RETURN_VAL(args, wrenNumToString(vm, AS_NUM(args[0]))); 783 } 784 785 @WrenPrimitive("Num", "truncate") 786 bool num_truncate(WrenVM* vm, Value* args) @nogc 787 { 788 import core.stdc.math : modf; 789 double integer; 790 modf(AS_NUM(args[0]), &integer); 791 return RETURN_NUM(args, integer); 792 } 793 794 /++ Object primitives +/ 795 @WrenPrimitive("Object metaclass", "same(_,_)") 796 bool object_same(WrenVM* vm, Value* args) @nogc 797 { 798 return RETURN_BOOL(args, wrenValuesEqual(args[1], args[2])); 799 } 800 801 @WrenPrimitive("Object", "!") 802 bool object_not(WrenVM* vm, Value* args) @nogc 803 { 804 return RETURN_VAL(args, FALSE_VAL); 805 } 806 807 @WrenPrimitive("Object", "==(_)") 808 bool object_eqeq(WrenVM* vm, Value* args) @nogc 809 { 810 return RETURN_BOOL(args, wrenValuesEqual(args[0], args[1])); 811 } 812 813 @WrenPrimitive("Object", "!=(_)") 814 bool object_bangeq(WrenVM* vm, Value* args) @nogc 815 { 816 return RETURN_BOOL(args, !wrenValuesEqual(args[0], args[1])); 817 } 818 819 @WrenPrimitive("Object", "is(_)") 820 bool object_is(WrenVM* vm, Value* args) @nogc 821 { 822 if (!IS_CLASS(args[1])) 823 { 824 return RETURN_ERROR(vm, "Right operand must be a class."); 825 } 826 827 ObjClass *classObj = wrenGetClass(vm, args[0]); 828 ObjClass *baseClassObj = AS_CLASS(args[1]); 829 830 // Walk the superclass chain looking for the class. 831 do 832 { 833 if (baseClassObj == classObj) { 834 return RETURN_BOOL(args, true); 835 } 836 837 classObj = classObj.superclass; 838 } 839 while (classObj != null); 840 841 return RETURN_BOOL(args, false); 842 } 843 844 @WrenPrimitive("Object", "toString") 845 bool object_toString(WrenVM* vm, Value* args) @nogc 846 { 847 if (!IS_OBJ(args[0])) 848 throw mallocNew!Error("Received `this` which is not an object for Object.toString"); 849 850 Obj* obj = AS_OBJ(args[0]); 851 Value name = OBJ_VAL(obj.classObj.name); 852 return RETURN_VAL(args, wrenStringFormat(vm, "instance of @", name)); 853 } 854 855 @WrenPrimitive("Object", "type") 856 bool object_type(WrenVM* vm, Value* args) @nogc 857 { 858 return RETURN_OBJ(args, wrenGetClass(vm, args[0])); 859 } 860 861 /++ Range primitives +/ 862 863 @WrenPrimitive("Range", "from") 864 bool range_from(WrenVM* vm, Value* args) @nogc 865 { 866 return RETURN_NUM(args, AS_RANGE(args[0]).from); 867 } 868 869 @WrenPrimitive("Range", "to") 870 bool range_to(WrenVM* vm, Value* args) @nogc 871 { 872 return RETURN_NUM(args, AS_RANGE(args[0]).to); 873 } 874 875 @WrenPrimitive("Range", "min") 876 bool range_min(WrenVM* vm, Value* args) @nogc 877 { 878 import core.stdc.math : fmin; 879 ObjRange* range = AS_RANGE(args[0]); 880 return RETURN_NUM(args, fmin(range.from, range.to)); 881 } 882 883 @WrenPrimitive("Range", "max") 884 bool range_max(WrenVM* vm, Value* args) @nogc 885 { 886 import core.stdc.math : fmax; 887 ObjRange* range = AS_RANGE(args[0]); 888 return RETURN_NUM(args, fmax(range.from, range.to)); 889 } 890 891 @WrenPrimitive("Range", "isInclusive") 892 bool range_isInclusive(WrenVM* vm, Value* args) @nogc 893 { 894 return RETURN_BOOL(args, AS_RANGE(args[0]).isInclusive); 895 } 896 897 @WrenPrimitive("Range", "iterate(_)") 898 bool range_iterate(WrenVM* vm, Value* args) @nogc 899 { 900 ObjRange* range = AS_RANGE(args[0]); 901 902 // Special case: empty range. 903 if (range.from == range.to && !range.isInclusive) return RETURN_FALSE(args); 904 905 // Start the iteration. 906 if (IS_NULL(args[1])) return RETURN_NUM(args, range.from); 907 908 if (!validateNum(vm, args[1], "Iterator")) return false; 909 910 double iterator = AS_NUM(args[1]); 911 912 // Iterate towards [to] from [from]. 913 if (range.from < range.to) 914 { 915 iterator++; 916 if (iterator > range.to) return RETURN_FALSE(args); 917 } 918 else 919 { 920 iterator--; 921 if (iterator < range.to) return RETURN_FALSE(args); 922 } 923 924 if (!range.isInclusive && iterator == range.to) return RETURN_FALSE(args); 925 926 return RETURN_NUM(args, iterator); 927 } 928 929 @WrenPrimitive("Range", "iteratorValue(_)") 930 bool range_iteratorValue(WrenVM* vm, Value* args) @nogc 931 { 932 // Assume the iterator is a number so that is the value of the range. 933 return RETURN_VAL(args, args[1]); 934 } 935 936 @WrenPrimitive("Range", "toString") 937 bool range_toString(WrenVM* vm, Value* args) @nogc 938 { 939 ObjRange* range = AS_RANGE(args[0]); 940 941 Value from = wrenNumToString(vm, range.from); 942 wrenPushRoot(vm, AS_OBJ(from)); 943 944 Value to = wrenNumToString(vm, range.to); 945 wrenPushRoot(vm, AS_OBJ(to)); 946 947 Value result = wrenStringFormat(vm, "@$@", from, 948 range.isInclusive ? "..".ptr : "...".ptr, to); 949 950 wrenPopRoot(vm); 951 wrenPopRoot(vm); 952 return RETURN_VAL(args, result); 953 } 954 955 /++ String primitives +/ 956 957 @WrenPrimitive("String", "fromCodePoint(_)", MethodType.METHOD_PRIMITIVE, true) 958 bool string_fromCodePoint(WrenVM* vm, Value* args) @nogc 959 { 960 if (!validateInt(vm, args[1], "Code point")) return false; 961 962 int codePoint = cast(int)AS_NUM(args[1]); 963 if (codePoint < 0) 964 { 965 return RETURN_ERROR(vm, "Code point cannot be negative."); 966 } 967 else if (codePoint > 0x10ffff) 968 { 969 return RETURN_ERROR(vm, "Code point cannot be greater than 0x10ffff."); 970 } 971 972 return RETURN_VAL(args, wrenStringFromCodePoint(vm, codePoint)); 973 } 974 975 @WrenPrimitive("String", "fromByte(_)", MethodType.METHOD_PRIMITIVE, true) 976 bool string_fromByte(WrenVM* vm, Value* args) @nogc 977 { 978 if (!validateInt(vm, args[1], "Byte")) return false; 979 int byte_ = cast(int) AS_NUM(args[1]); 980 if (byte_ < 0) 981 { 982 return RETURN_ERROR(vm, "Byte cannot be negative."); 983 } 984 else if (byte_ > 0xff) 985 { 986 return RETURN_ERROR(vm, "Byte cannot be greater than 0xff."); 987 } 988 return RETURN_VAL(args, wrenStringFromByte(vm, cast(ubyte)byte_)); 989 } 990 991 @WrenPrimitive("String", "byteAt(_)") 992 bool string_byteAt(WrenVM* vm, Value* args) @nogc 993 { 994 ObjString* string_ = AS_STRING(args[0]); 995 996 uint index = validateIndex(vm, args[1], string_.length, "Index"); 997 if (index == uint.max) return false; 998 999 return RETURN_NUM(args, cast(ubyte)string_.value[index]); 1000 } 1001 1002 @WrenPrimitive("String", "byteCount") 1003 bool string_byteCount(WrenVM* vm, Value* args) @nogc 1004 { 1005 return RETURN_NUM(args, AS_STRING(args[0]).length); 1006 } 1007 1008 @WrenPrimitive("String", "codePointAt(_)") 1009 bool string_codePointAt(WrenVM* vm, Value* args) @nogc 1010 { 1011 import wren.utils : wrenUtf8Decode; 1012 ObjString* string_ = AS_STRING(args[0]); 1013 1014 uint index = validateIndex(vm, args[1], string_.length, "Index"); 1015 if (index == uint.max) return false; 1016 1017 // If we are in the middle of a UTF-8 sequence, indicate that. 1018 const(ubyte)* bytes = cast(ubyte*)string_.value.ptr; 1019 if ((bytes[index] & 0xc0) == 0x80) return RETURN_NUM(args, -1); 1020 1021 // Decode the UTF-8 sequence. 1022 return RETURN_NUM(args, wrenUtf8Decode(cast(ubyte*)string_.value + index, 1023 string_.length - index)); 1024 } 1025 1026 @WrenPrimitive("String", "contains(_)") 1027 bool string_contains(WrenVM* vm, Value* args) @nogc 1028 { 1029 if (!validateString(vm, args[1], "Argument")) return false; 1030 1031 ObjString* string_ = AS_STRING(args[0]); 1032 ObjString* search = AS_STRING(args[1]); 1033 1034 return RETURN_BOOL(args, wrenStringFind(string_, search, 0) != uint.max); 1035 } 1036 1037 @WrenPrimitive("String", "endsWith(_)") 1038 bool string_endsWith(WrenVM* vm, Value* args) @nogc 1039 { 1040 import core.stdc.string : memcmp; 1041 if (!validateString(vm, args[1], "Argument")) return false; 1042 1043 ObjString* string_ = AS_STRING(args[0]); 1044 ObjString* search = AS_STRING(args[1]); 1045 1046 // Edge case: If the search string is longer then return false right away. 1047 if (search.length > string_.length) return RETURN_FALSE(args); 1048 1049 return RETURN_BOOL(args, memcmp(string_.value.ptr + string_.length - search.length, 1050 search.value.ptr, search.length) == 0); 1051 } 1052 1053 @WrenPrimitive("String", "indexOf(_)") 1054 bool string_indexOf1(WrenVM* vm, Value* args) @nogc 1055 { 1056 if (!validateString(vm, args[1], "Argument")) return false; 1057 1058 ObjString* string_ = AS_STRING(args[0]); 1059 ObjString* search = AS_STRING(args[1]); 1060 1061 uint index = wrenStringFind(string_, search, 0); 1062 return RETURN_NUM(args, index == uint.max ? -1 : cast(int)index); 1063 } 1064 1065 @WrenPrimitive("String", "indexOf(_,_)") 1066 bool string_indexOf2(WrenVM* vm, Value* args) @nogc 1067 { 1068 if (!validateString(vm, args[1], "Argument")) return false; 1069 1070 ObjString* string_ = AS_STRING(args[0]); 1071 ObjString* search = AS_STRING(args[1]); 1072 uint start = validateIndex(vm, args[2], string_.length, "Start"); 1073 if (start == uint.max) return false; 1074 1075 uint index = wrenStringFind(string_, search, start); 1076 return RETURN_NUM(args, index == uint.max ? -1 : cast(int)index); 1077 } 1078 1079 @WrenPrimitive("String", "iterate(_)") 1080 bool string_iterate(WrenVM* vm, Value* args) @nogc 1081 { 1082 ObjString* string_ = AS_STRING(args[0]); 1083 1084 // If we're starting the iteration, return the first index. 1085 if (IS_NULL(args[1])) 1086 { 1087 if (string_.length == 0) return RETURN_FALSE(args); 1088 return RETURN_NUM(args, 0); 1089 } 1090 1091 if (!validateInt(vm, args[1], "Iterator")) return false; 1092 1093 if (AS_NUM(args[1]) < 0) return RETURN_FALSE(args); 1094 uint index = cast(uint)AS_NUM(args[1]); 1095 1096 // Advance to the beginning of the next UTF-8 sequence. 1097 do 1098 { 1099 index++; 1100 if (index >= string_.length) return RETURN_FALSE(args); 1101 } while ((string_.value[index] & 0xc0) == 0x80); 1102 1103 return RETURN_NUM(args, index); 1104 } 1105 1106 @WrenPrimitive("String", "iterateByte(_)") 1107 bool string_iterateByte(WrenVM* vm, Value* args) @nogc 1108 { 1109 ObjString* string_ = AS_STRING(args[0]); 1110 1111 // If we're starting the iteration, return the first index. 1112 if (IS_NULL(args[1])) 1113 { 1114 if (string_.length == 0) return RETURN_FALSE(args); 1115 return RETURN_NUM(args, 0); 1116 } 1117 1118 if (!validateInt(vm, args[1], "Iterator")) return false; 1119 1120 if (AS_NUM(args[1]) < 0) return RETURN_FALSE(args); 1121 uint index = cast(uint)AS_NUM(args[1]); 1122 1123 // Advance to the next byte. 1124 index++; 1125 if (index >= string_.length) return RETURN_FALSE(args); 1126 1127 return RETURN_NUM(args, index); 1128 } 1129 1130 @WrenPrimitive("String", "iteratorValue(_)") 1131 bool string_iteratorValue(WrenVM* vm, Value* args) @nogc 1132 { 1133 ObjString* string_ = AS_STRING(args[0]); 1134 uint index = validateIndex(vm, args[1], string_.length, "Iterator"); 1135 if (index == uint.max) return false; 1136 1137 return RETURN_VAL(args, wrenStringCodePointAt(vm, string_, index)); 1138 } 1139 1140 @WrenPrimitive("String", "+(_)") 1141 bool string_plus(WrenVM* vm, Value* args) @nogc 1142 { 1143 if (!validateString(vm, args[1], "Right operand")) return false; 1144 return RETURN_VAL(args, wrenStringFormat(vm, "@@", args[0], args[1])); 1145 } 1146 1147 @WrenPrimitive("String", "[_]") 1148 bool string_subscript(WrenVM* vm, Value* args) @nogc 1149 { 1150 ObjString* string_ = AS_STRING(args[0]); 1151 1152 if (IS_NUM(args[1])) 1153 { 1154 int index = validateIndex(vm, args[1], string_.length, "Subscript"); 1155 if (index == -1) return false; 1156 1157 return RETURN_VAL(args, wrenStringCodePointAt(vm, string_, index)); 1158 } 1159 1160 if (!IS_RANGE(args[1])) 1161 { 1162 return RETURN_ERROR(vm, "Subscript must be a number or a range."); 1163 } 1164 1165 int step; 1166 uint count = string_.length; 1167 int start = calculateRange(vm, AS_RANGE(args[1]), &count, &step); 1168 if (start == -1) return false; 1169 1170 return RETURN_VAL(args, wrenNewStringFromRange(vm, string_, start, count, step)); 1171 } 1172 1173 @WrenPrimitive("String", "toString") 1174 bool string_toString(WrenVM* vm, Value* args) @nogc 1175 { 1176 return RETURN_VAL(args, args[0]); 1177 } 1178 1179 @WrenPrimitive("System", "clock") 1180 bool system_clock(WrenVM* vm, Value* args) @nogc 1181 { 1182 version (Posix) { 1183 import core.sys.posix.stdc.time : clock, CLOCKS_PER_SEC; 1184 return RETURN_NUM(args, cast(double)clock / CLOCKS_PER_SEC); 1185 } else { 1186 import core.time : convClockFreq, MonoTime; 1187 double t = convClockFreq(MonoTime.currTime.ticks, MonoTime.ticksPerSecond, 1_000_000) * 0.000001; 1188 return RETURN_NUM(args, t); 1189 } 1190 } 1191 1192 @WrenPrimitive("System", "gc()") 1193 bool system_gc(WrenVM* vm, Value* args) @nogc 1194 { 1195 wrenCollectGarbage(vm); 1196 return RETURN_NULL(args); 1197 } 1198 1199 @WrenPrimitive("System", "writeString_(_)") 1200 bool system_writeString(WrenVM* vm, Value* args) @nogc 1201 { 1202 if (vm.config.writeFn != null) 1203 { 1204 vm.config.writeFn(vm, AS_CSTRING(args[1])); 1205 } 1206 1207 return RETURN_VAL(args, args[1]); 1208 } 1209 1210 // Creates either the Object or Class class in the core module with [name]. 1211 static ObjClass* defineClass(WrenVM* vm, ObjModule* module_, const(char)* name) @nogc 1212 { 1213 ObjString* nameString = AS_STRING(wrenNewString(vm, name)); 1214 wrenPushRoot(vm, cast(Obj*)nameString); 1215 1216 ObjClass* classObj = wrenNewSingleClass(vm, 0, nameString); 1217 1218 wrenDefineVariable(vm, module_, name, nameString.length, OBJ_VAL(classObj), null); 1219 1220 wrenPopRoot(vm); 1221 return classObj; 1222 } 1223 1224 private void registerPrimitives(string className)(WrenVM* vm, ObjClass* classObj) { 1225 static foreach(_mem; __traits(allMembers, mixin(__MODULE__))) 1226 {{ 1227 import std.traits : getUDAs, hasUDA; 1228 alias member = __traits(getMember, mixin(__MODULE__), _mem); 1229 static if (hasUDA!(member, WrenPrimitive)) { 1230 enum primDef = getUDAs!(member, WrenPrimitive)[0]; 1231 static if (primDef.className == className) { 1232 PRIMITIVE!(primDef.primitiveName, member, primDef.methodType, primDef.registerToSuperClass)(vm, classObj); 1233 } 1234 } 1235 }} 1236 } 1237 1238 void wrenInitializeCore(WrenVM* vm) @nogc 1239 { 1240 ObjModule* coreModule = wrenNewModule(vm, null); 1241 wrenPushRoot(vm, cast(Obj*)coreModule); 1242 1243 // The core module's key is null in the module map. 1244 wrenMapSet(vm, vm.modules, NULL_VAL, OBJ_VAL(coreModule)); 1245 wrenPopRoot(vm); // coreModule. 1246 1247 // Define the root Object class. This has to be done a little specially 1248 // because it has no superclass. 1249 vm.objectClass = defineClass(vm, coreModule, "Object"); 1250 registerPrimitives!("Object")(vm, vm.objectClass); 1251 1252 // Now we can define Class, which is a subclass of Object. 1253 vm.classClass = defineClass(vm, coreModule, "Class"); 1254 wrenBindSuperclass(vm, vm.classClass, vm.objectClass); 1255 // TODO: define primitives 1256 1257 // Finally, we can define Object's metaclass which is a subclass of Class. 1258 ObjClass* objectMetaclass = defineClass(vm, coreModule, "Object metaclass"); 1259 1260 // Wire up the metaclass relationships now that all three classes are built. 1261 vm.objectClass.obj.classObj = objectMetaclass; 1262 objectMetaclass.obj.classObj = vm.classClass; 1263 vm.classClass.obj.classObj = vm.classClass; 1264 1265 // Do this after wiring up the metaclasses so objectMetaclass doesn't get 1266 // collected. 1267 wrenBindSuperclass(vm, objectMetaclass, vm.classClass); 1268 registerPrimitives!("Object metaclass")(vm, objectMetaclass); 1269 1270 // The core class diagram ends up looking like this, where single lines point 1271 // to a class's superclass, and double lines point to its metaclass: 1272 // 1273 // .------------------------------------. .====. 1274 // | .---------------. | # # 1275 // v | v | v # 1276 // .---------. .-------------------. .-------. # 1277 // | Object |==>| Object metaclass |==>| Class |==" 1278 // '---------' '-------------------' '-------' 1279 // ^ ^ ^ ^ ^ 1280 // | .--------------' # | # 1281 // | | # | # 1282 // .---------. .-------------------. # | # -. 1283 // | Base |==>| Base metaclass |======" | # | 1284 // '---------' '-------------------' | # | 1285 // ^ | # | 1286 // | .------------------' # | Example classes 1287 // | | # | 1288 // .---------. .-------------------. # | 1289 // | Derived |==>| Derived metaclass |==========" | 1290 // '---------' '-------------------' -' 1291 1292 // The rest of the classes can now be defined normally. 1293 wrenInterpret(vm, null, coreModuleSource.ptr); 1294 1295 vm.boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool")); 1296 registerPrimitives!("Bool")(vm, vm.boolClass); 1297 1298 vm.fiberClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fiber")); 1299 registerPrimitives!("Fiber")(vm, vm.fiberClass); 1300 1301 vm.fnClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Fn")); 1302 registerPrimitives!("Fn")(vm, vm.fnClass); 1303 1304 vm.nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null")); 1305 registerPrimitives!("Null")(vm, vm.nullClass); 1306 1307 vm.numClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Num")); 1308 registerPrimitives!("Num")(vm, vm.numClass); 1309 1310 vm.stringClass = AS_CLASS(wrenFindVariable(vm, coreModule, "String")); 1311 registerPrimitives!("String")(vm, vm.stringClass); 1312 1313 vm.listClass = AS_CLASS(wrenFindVariable(vm, coreModule, "List")); 1314 registerPrimitives!("List")(vm, vm.listClass); 1315 1316 vm.mapClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Map")); 1317 registerPrimitives!("Map")(vm, vm.mapClass); 1318 1319 vm.rangeClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Range")); 1320 registerPrimitives!("Range")(vm, vm.rangeClass); 1321 1322 ObjClass* systemClass = AS_CLASS(wrenFindVariable(vm, coreModule, "System")); 1323 registerPrimitives!("System")(vm, systemClass.obj.classObj); 1324 1325 1326 // While bootstrapping the core types and running the core module, a number 1327 // of string objects have been created, many of which were instantiated 1328 // before stringClass was stored in the VM. Some of them *must* be created 1329 // first -- the ObjClass for string itself has a reference to the ObjString 1330 // for its name. 1331 // 1332 // These all currently have a NULL classObj pointer, so go back and assign 1333 // them now that the string class is known. 1334 for (Obj* obj = vm.first; obj != null; obj = obj.next) 1335 { 1336 if (obj.type == ObjType.OBJ_STRING) obj.classObj = vm.stringClass; 1337 } 1338 }