| 1 | #include "wren_primitive.h" |
| 2 | |
| 3 | #include <math.h> |
| 4 | |
| 5 | // Validates that [value] is an integer within `[0, count)`. Also allows |
| 6 | // negative indices which map backwards from the end. Returns the valid positive |
| 7 | // index value. If invalid, reports an error and returns `UINT32_MAX`. |
| 8 | static uint32_t validateIndexValue(WrenVM* vm, uint32_t count, double value, |
| 9 | const char* argName) |
| 10 | { |
| 11 | if (!validateIntValue(vm, value, argName)) return UINT32_MAX; |
| 12 | |
| 13 | // Negative indices count from the end. |
| 14 | if (value < 0) value = count + value; |
| 15 | |
| 16 | // Check bounds. |
| 17 | if (value >= 0 && value < count) return (uint32_t)value; |
| 18 | |
| 19 | vm->fiber->error = wrenStringFormat(vm, "$ out of bounds." , argName); |
| 20 | return UINT32_MAX; |
| 21 | } |
| 22 | |
| 23 | bool validateFn(WrenVM* vm, Value arg, const char* argName) |
| 24 | { |
| 25 | if (IS_CLOSURE(arg)) return true; |
| 26 | RETURN_ERROR_FMT("$ must be a function." , argName); |
| 27 | } |
| 28 | |
| 29 | bool validateNum(WrenVM* vm, Value arg, const char* argName) |
| 30 | { |
| 31 | if (IS_NUM(arg)) return true; |
| 32 | RETURN_ERROR_FMT("$ must be a number." , argName); |
| 33 | } |
| 34 | |
| 35 | bool validateIntValue(WrenVM* vm, double value, const char* argName) |
| 36 | { |
| 37 | if (trunc(value) == value) return true; |
| 38 | RETURN_ERROR_FMT("$ must be an integer." , argName); |
| 39 | } |
| 40 | |
| 41 | bool validateInt(WrenVM* vm, Value arg, const char* argName) |
| 42 | { |
| 43 | // Make sure it's a number first. |
| 44 | if (!validateNum(vm, arg, argName)) return false; |
| 45 | return validateIntValue(vm, AS_NUM(arg), argName); |
| 46 | } |
| 47 | |
| 48 | bool validateKey(WrenVM* vm, Value arg) |
| 49 | { |
| 50 | if (wrenMapIsValidKey(arg)) return true; |
| 51 | |
| 52 | RETURN_ERROR("Key must be a value type." ); |
| 53 | } |
| 54 | |
| 55 | uint32_t validateIndex(WrenVM* vm, Value arg, uint32_t count, |
| 56 | const char* argName) |
| 57 | { |
| 58 | if (!validateNum(vm, arg, argName)) return UINT32_MAX; |
| 59 | return validateIndexValue(vm, count, AS_NUM(arg), argName); |
| 60 | } |
| 61 | |
| 62 | bool validateString(WrenVM* vm, Value arg, const char* argName) |
| 63 | { |
| 64 | if (IS_STRING(arg)) return true; |
| 65 | RETURN_ERROR_FMT("$ must be a string." , argName); |
| 66 | } |
| 67 | |
| 68 | uint32_t calculateRange(WrenVM* vm, ObjRange* range, uint32_t* length, |
| 69 | int* step) |
| 70 | { |
| 71 | *step = 0; |
| 72 | |
| 73 | // Edge case: an empty range is allowed at the end of a sequence. This way, |
| 74 | // list[0..-1] and list[0...list.count] can be used to copy a list even when |
| 75 | // empty. |
| 76 | if (range->from == *length && |
| 77 | range->to == (range->isInclusive ? -1.0 : (double)*length)) |
| 78 | { |
| 79 | *length = 0; |
| 80 | return 0; |
| 81 | } |
| 82 | |
| 83 | uint32_t from = validateIndexValue(vm, *length, range->from, "Range start" ); |
| 84 | if (from == UINT32_MAX) return UINT32_MAX; |
| 85 | |
| 86 | // Bounds check the end manually to handle exclusive ranges. |
| 87 | double value = range->to; |
| 88 | if (!validateIntValue(vm, value, "Range end" )) return UINT32_MAX; |
| 89 | |
| 90 | // Negative indices count from the end. |
| 91 | if (value < 0) value = *length + value; |
| 92 | |
| 93 | // Convert the exclusive range to an inclusive one. |
| 94 | if (!range->isInclusive) |
| 95 | { |
| 96 | // An exclusive range with the same start and end points is empty. |
| 97 | if (value == from) |
| 98 | { |
| 99 | *length = 0; |
| 100 | return from; |
| 101 | } |
| 102 | |
| 103 | // Shift the endpoint to make it inclusive, handling both increasing and |
| 104 | // decreasing ranges. |
| 105 | value += value >= from ? -1 : 1; |
| 106 | } |
| 107 | |
| 108 | // Check bounds. |
| 109 | if (value < 0 || value >= *length) |
| 110 | { |
| 111 | vm->fiber->error = CONST_STRING(vm, "Range end out of bounds." ); |
| 112 | return UINT32_MAX; |
| 113 | } |
| 114 | |
| 115 | uint32_t to = (uint32_t)value; |
| 116 | *length = abs((int)(from - to)) + 1; |
| 117 | *step = from < to ? 1 : -1; |
| 118 | return from; |
| 119 | } |
| 120 | |