Coverage Report

Created: 2026-02-23 20:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/alexjokela/projects/lattice/src/array_ops.c
Line
Count
Source
1
#include "array_ops.h"
2
#include <stdlib.h>
3
#include <string.h>
4
5
/* ── Sort ── */
6
7
/* Static error flag for qsort comparator (qsort can't propagate errors) */
8
static int sort_error = 0;
9
static ValueType sort_type = VAL_UNIT;
10
11
9
static int sort_comparator(const void *a, const void *b) {
12
9
    const LatValue *va = (const LatValue *)a;
13
9
    const LatValue *vb = (const LatValue *)b;
14
15
9
    if (va->type != vb->type) {
16
0
        sort_error = 1;
17
0
        return 0;
18
0
    }
19
20
9
    switch (va->type) {
21
3
        case VAL_INT: {
22
3
            int64_t ai = va->as.int_val, bi = vb->as.int_val;
23
3
            return (ai > bi) - (ai < bi);
24
0
        }
25
3
        case VAL_FLOAT: {
26
3
            double af = va->as.float_val, bf = vb->as.float_val;
27
3
            return (af > bf) - (af < bf);
28
0
        }
29
3
        case VAL_STR:
30
3
            return strcmp(va->as.str_val, vb->as.str_val);
31
0
        default:
32
0
            sort_error = 1;
33
0
            return 0;
34
9
    }
35
9
}
36
37
5
LatValue array_sort(const LatValue *arr, char **err) {
38
5
    size_t n = arr->as.array.len;
39
5
    if (n == 0) {
40
1
        *err = NULL;
41
1
        LatValue empty;
42
1
        return value_array(&empty, 0);
43
1
    }
44
45
    /* Validate element types - must be homogeneous and comparable */
46
4
    sort_type = arr->as.array.elems[0].type;
47
4
    if (sort_type != VAL_INT && sort_type != VAL_FLOAT && sort_type != VAL_STR) {
48
0
        *err = strdup(".sort() only supports Int, Float, or String arrays");
49
0
        return value_unit();
50
0
    }
51
10
    for (size_t i = 1; i < n; i++) {
52
7
        if (arr->as.array.elems[i].type != sort_type) {
53
1
            *err = strdup(".sort() requires all elements to be the same type");
54
1
            return value_unit();
55
1
        }
56
7
    }
57
58
    /* Deep-clone elements into a working buffer */
59
3
    LatValue *buf = malloc(n * sizeof(LatValue));
60
12
    for (size_t i = 0; i < n; i++) {
61
9
        buf[i] = value_deep_clone(&arr->as.array.elems[i]);
62
9
    }
63
64
3
    sort_error = 0;
65
3
    qsort(buf, n, sizeof(LatValue), sort_comparator);
66
67
3
    if (sort_error) {
68
0
        for (size_t i = 0; i < n; i++) value_free(&buf[i]);
69
0
        free(buf);
70
0
        *err = strdup(".sort() encountered incomparable types");
71
0
        return value_unit();
72
0
    }
73
74
3
    *err = NULL;
75
3
    LatValue result = value_array(buf, n);
76
    /* value_array does a shallow memcpy, so it now owns the element data.
77
     * Only free the container array, not the individual elements. */
78
3
    free(buf);
79
3
    return result;
80
3
}
81
82
/* ── Flat ── */
83
84
8
LatValue array_flat(const LatValue *arr) {
85
8
    size_t n = arr->as.array.len;
86
87
    /* First pass: count total elements */
88
8
    size_t total = 0;
89
26
    for (size_t i = 0; i < n; i++) {
90
18
        if (arr->as.array.elems[i].type == VAL_ARRAY) {
91
10
            total += arr->as.array.elems[i].as.array.len;
92
10
        } else {
93
8
            total += 1;
94
8
        }
95
18
    }
96
97
8
    if (total == 0) {
98
2
        LatValue empty;
99
2
        return value_array(&empty, 0);
100
2
    }
101
102
6
    LatValue *buf = malloc(total * sizeof(LatValue));
103
6
    size_t pos = 0;
104
24
    for (size_t i = 0; i < n; i++) {
105
18
        if (arr->as.array.elems[i].type == VAL_ARRAY) {
106
10
            LatValue *inner = arr->as.array.elems[i].as.array.elems;
107
10
            size_t inner_len = arr->as.array.elems[i].as.array.len;
108
26
            for (size_t j = 0; j < inner_len; j++) {
109
16
                buf[pos++] = value_deep_clone(&inner[j]);
110
16
            }
111
10
        } else {
112
8
            buf[pos++] = value_deep_clone(&arr->as.array.elems[i]);
113
8
        }
114
18
    }
115
116
6
    LatValue result = value_array(buf, pos);
117
6
    free(buf);
118
6
    return result;
119
8
}
120
121
/* ── Slice ── */
122
123
8
LatValue array_slice(const LatValue *arr, int64_t start, int64_t end, char **err) {
124
8
    int64_t len = (int64_t)arr->as.array.len;
125
126
    /* Clamp to [0, len] */
127
8
    if (start < 0) start = 0;
128
8
    if (start > len) start = len;
129
8
    if (end < 0) end = 0;
130
8
    if (end > len) end = len;
131
8
    if (end < start) end = start;
132
133
8
    size_t count = (size_t)(end - start);
134
8
    if (count == 0) {
135
2
        *err = NULL;
136
2
        LatValue empty;
137
2
        return value_array(&empty, 0);
138
2
    }
139
140
6
    LatValue *buf = malloc(count * sizeof(LatValue));
141
22
    for (size_t i = 0; i < count; i++) {
142
16
        buf[i] = value_deep_clone(&arr->as.array.elems[start + (int64_t)i]);
143
16
    }
144
145
    *err = NULL;
146
6
    LatValue result = value_array(buf, count);
147
6
    free(buf);
148
6
    return result;
149
8
}