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