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`.
8static 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
23bool 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
29bool 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
35bool 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
41bool 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
48bool validateKey(WrenVM* vm, Value arg)
49{
50 if (wrenMapIsValidKey(arg)) return true;
51
52 RETURN_ERROR("Key must be a value type.");
53}
54
55uint32_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
62bool 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
68uint32_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