| 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 |  | 
|---|