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/runtime.c
Line
Count
Source
1
#include "runtime.h"
2
#include "stackvm.h"       /* For StackVM, StackCallFrame, ObjUpvalue, stackvm_run, stackvm_track_chunk, Chunk types */
3
#include "regvm.h"         /* For RegVM, RegChunk, regvm_run, regvm_track_chunk */
4
#include "stackopcode.h"
5
#include "stackcompiler.h"
6
#include "intern.h"
7
#include "builtins.h"
8
#include "lattice.h"
9
#include "math_ops.h"
10
#include "fs_ops.h"
11
#include "path_ops.h"
12
#include "net.h"
13
#include "tls.h"
14
#include "http.h"
15
#include "json.h"
16
#include "toml_ops.h"
17
#include "yaml_ops.h"
18
#include "crypto_ops.h"
19
#include "regex_ops.h"
20
#include "time_ops.h"
21
#include "datetime_ops.h"
22
#include "env_ops.h"
23
#include "process_ops.h"
24
#include "format_ops.h"
25
#include "type_ops.h"
26
#include "string_ops.h"
27
#include "array_ops.h"
28
#include "channel.h"
29
#include "ext.h"
30
#include "lexer.h"
31
#include "parser.h"
32
#include "stackcompiler.h"
33
#include "latc.h"
34
#include <stdlib.h>
35
#include <limits.h>
36
#include <libgen.h>
37
#include <string.h>
38
#include <stdio.h>
39
#include <stdarg.h>
40
#ifndef __EMSCRIPTEN__
41
#include <pthread.h>
42
#endif
43
#include "memory.h"
44
45
/* Native function pointer for StackVM builtins. */
46
typedef LatValue (*VMNativeFn)(LatValue *args, int arg_count);
47
48
/* Sentinel to distinguish native C functions from compiled closures. */
49
276k
#define VM_NATIVE_MARKER ((struct Expr **)(uintptr_t)0x1)
50
180
#define VM_EXT_MARKER    ((struct Expr **)(uintptr_t)0x2)
51
52
/* Thread-local runtime pointer for native function dispatch. */
53
static _Thread_local LatRuntime *current_rt = NULL;
54
55
3.85k
void lat_runtime_set_current(LatRuntime *rt) { current_rt = rt; }
56
1.72k
LatRuntime *lat_runtime_current(void) { return current_rt; }
57
58
59
/* ── Phase system functions ── */
60
61
458
void rt_record_history(LatRuntime *rt, const char *name, LatValue *val) {
62
481
    for (size_t i = 0; i < rt->tracked_count; i++) {
63
83
        if (strcmp(rt->tracked_vars[i].name, name) != 0) continue;
64
60
        if (rt->tracked_vars[i].snap_count >= rt->tracked_vars[i].snap_cap) {
65
26
            rt->tracked_vars[i].snap_cap = rt->tracked_vars[i].snap_cap ? rt->tracked_vars[i].snap_cap * 2 : 4;
66
26
            rt->tracked_vars[i].snapshots = realloc(rt->tracked_vars[i].snapshots,
67
26
                rt->tracked_vars[i].snap_cap * sizeof(*rt->tracked_vars[i].snapshots));
68
26
        }
69
60
        size_t si = rt->tracked_vars[i].snap_count++;
70
60
        const char *phase_name = builtin_phase_of_str(val);
71
60
        rt->tracked_vars[i].snapshots[si].phase = strdup(phase_name);
72
60
        rt->tracked_vars[i].snapshots[si].value = value_deep_clone(val);
73
60
        rt->tracked_vars[i].snapshots[si].line = rt->current_line ? rt->current_line(rt->active_vm) : 0;
74
60
        rt->tracked_vars[i].snapshots[si].fn_name = NULL;
75
60
        return;
76
83
    }
77
458
}
78
79
763
void rt_fire_reactions(LatRuntime *rt, const char *name, const char *phase) {
80
765
    for (size_t i = 0; i < rt->reaction_count; i++) {
81
18
        if (strcmp(rt->reactions[i].var_name, name) != 0) continue;
82
16
        LatValue cur;
83
16
        bool found = rt->get_var_by_name(rt->active_vm, name, &cur);
84
16
        if (!found) return;
85
32
        for (size_t j = 0; j < rt->reactions[i].cb_count; j++) {
86
18
            LatValue *cb = &rt->reactions[i].callbacks[j];
87
18
            LatValue args[2];
88
18
            args[0] = value_string(phase);
89
18
            args[1] = value_deep_clone(&cur);
90
18
            LatValue result = rt->call_closure(rt->active_vm, cb, args, 2);
91
18
            value_free(&args[0]);
92
18
            value_free(&args[1]);
93
18
            value_free(&result);
94
18
            if (rt->error) {
95
2
                char *wrapped = NULL;
96
2
                (void)asprintf(&wrapped, "reaction error: %s", rt->error);
97
2
                free(rt->error);
98
2
                rt->error = wrapped;
99
2
                value_free(&cur);
100
2
                return;
101
2
            }
102
18
        }
103
14
        value_free(&cur);
104
14
        return;
105
16
    }
106
763
}
107
108
423
void rt_freeze_cascade(LatRuntime *rt, const char *target_name) {
109
449
    for (size_t bi = 0; bi < rt->bond_count; bi++) {
110
48
        if (strcmp(rt->bonds[bi].target, target_name) != 0) continue;
111
46
        for (size_t di = 0; di < rt->bonds[bi].dep_count; di++) {
112
26
            const char *dep = rt->bonds[bi].deps[di];
113
26
            const char *strategy = rt->bonds[bi].dep_strategies ? rt->bonds[bi].dep_strategies[di] : "mirror";
114
26
            LatValue dval;
115
26
            if (!rt->get_var_by_name(rt->active_vm, dep, &dval)) continue;
116
26
            if (dval.type == VAL_CHANNEL) { value_free(&dval); continue; }
117
118
26
            if (strcmp(strategy, "mirror") == 0) {
119
20
                if (dval.phase == VTAG_CRYSTAL) { value_free(&dval); continue; }
120
20
                LatValue frozen = value_freeze(dval);
121
20
                rt->set_var_by_name(rt->active_vm, dep, value_deep_clone(&frozen));
122
20
                value_free(&frozen);
123
20
                rt_fire_reactions(rt, dep, "crystal");
124
20
                if (rt->error) return;
125
20
                rt_freeze_cascade(rt, dep);
126
20
                if (rt->error) return;
127
20
            } else if (strcmp(strategy, "inverse") == 0) {
128
2
                if (dval.phase != VTAG_CRYSTAL && dval.phase != VTAG_SUBLIMATED) { value_free(&dval); continue; }
129
2
                LatValue thawed = value_thaw(&dval);
130
2
                value_free(&dval);
131
2
                rt->set_var_by_name(rt->active_vm, dep, value_deep_clone(&thawed));
132
2
                value_free(&thawed);
133
2
                rt_fire_reactions(rt, dep, "fluid");
134
2
                if (rt->error) return;
135
4
            } else if (strcmp(strategy, "gate") == 0) {
136
4
                if (dval.phase != VTAG_CRYSTAL) {
137
2
                    value_free(&dval);
138
2
                    char *err = NULL;
139
2
                    (void)asprintf(&err, "gate bond: '%s' must be crystal before '%s' can freeze", dep, target_name);
140
2
                    rt->error = err;
141
2
                    return;
142
2
                }
143
2
                value_free(&dval);
144
2
            } else {
145
0
                value_free(&dval);
146
0
            }
147
26
        }
148
        /* Consume the bond entry (one-shot) */
149
44
        for (size_t di = 0; di < rt->bonds[bi].dep_count; di++) {
150
24
            free(rt->bonds[bi].deps[di]);
151
24
            if (rt->bonds[bi].dep_strategies) free(rt->bonds[bi].dep_strategies[di]);
152
24
        }
153
20
        free(rt->bonds[bi].deps);
154
20
        free(rt->bonds[bi].dep_strategies);
155
20
        free(rt->bonds[bi].target);
156
20
        rt->bonds[bi] = rt->bonds[--rt->bond_count];
157
20
        break;
158
22
    }
159
423
}
160
161
407
char *rt_validate_seeds(LatRuntime *rt, const char *name, LatValue *val, bool consume) {
162
409
    for (size_t si = 0; si < rt->seed_count; si++) {
163
6
        if (strcmp(rt->seeds[si].var_name, name) != 0) continue;
164
6
        LatValue check_val = value_deep_clone(val);
165
6
        LatValue result = rt->call_closure(rt->active_vm, &rt->seeds[si].contract, &check_val, 1);
166
6
        value_free(&check_val);
167
6
        if (rt->error) {
168
0
            char *msg = NULL;
169
0
            char *inner = rt->error;
170
0
            rt->error = NULL;
171
0
            (void)asprintf(&msg, "seed contract failed: %s", inner);
172
0
            free(inner);
173
0
            value_free(&result);
174
0
            return msg;
175
0
        }
176
6
        if (!value_is_truthy(&result)) {
177
4
            value_free(&result);
178
4
            if (consume) {
179
2
                free(rt->seeds[si].var_name);
180
2
                value_free(&rt->seeds[si].contract);
181
2
                rt->seeds[si] = rt->seeds[--rt->seed_count];
182
2
            }
183
4
            return strdup("grow() seed contract returned false");
184
4
        }
185
2
        value_free(&result);
186
2
        if (consume) {
187
2
            free(rt->seeds[si].var_name);
188
2
            value_free(&rt->seeds[si].contract);
189
2
            rt->seeds[si] = rt->seeds[--rt->seed_count];
190
2
            si--;
191
2
        }
192
2
    }
193
403
    return NULL;
194
407
}
195
196
/* ── Native builtins ── */
197
198
/* ── Native builtins ── */
199
200
354
static LatValue native_to_string(LatValue *args, int arg_count) {
201
354
    if (arg_count != 1) return value_string("to_string() expects 1 argument");
202
354
    char *s = builtin_to_string(&args[0]);
203
354
    LatValue r = value_string(s);
204
354
    free(s);
205
354
    return r;
206
354
}
207
208
144
static LatValue native_typeof(LatValue *args, int arg_count) {
209
144
    if (arg_count != 1) return value_string("typeof() expects 1 argument");
210
144
    return value_string(builtin_typeof_str(&args[0]));
211
144
}
212
213
394
static LatValue native_len(LatValue *args, int arg_count) {
214
394
    if (arg_count != 1) return value_int(0);
215
394
    LatValue *v = &args[0];
216
394
    if (v->type == VAL_REF) v = &v->as.ref.ref->value;
217
394
    if (v->type == VAL_ARRAY) return value_int((int64_t)v->as.array.len);
218
152
    if (v->type == VAL_STR) return value_int((int64_t)strlen(v->as.str_val));
219
4
    if (v->type == VAL_MAP) return value_int((int64_t)lat_map_len(v->as.map.map));
220
2
    if (v->type == VAL_BUFFER) return value_int((int64_t)v->as.buffer.len);
221
0
    return value_int(0);
222
2
}
223
224
4
static LatValue native_parse_int(LatValue *args, int arg_count) {
225
4
    if (arg_count != 1 || args[0].type != VAL_STR) return value_nil();
226
4
    bool ok;
227
4
    int64_t v = builtin_parse_int(args[0].as.str_val, &ok);
228
4
    return ok ? value_int(v) : value_nil();
229
4
}
230
231
2
static LatValue native_parse_float(LatValue *args, int arg_count) {
232
2
    if (arg_count != 1 || args[0].type != VAL_STR) return value_nil();
233
2
    bool ok;
234
2
    double v = builtin_parse_float(args[0].as.str_val, &ok);
235
2
    return ok ? value_float(v) : value_nil();
236
2
}
237
238
8
static LatValue native_ord(LatValue *args, int arg_count) {
239
8
    if (arg_count != 1 || args[0].type != VAL_STR) return value_int(-1);
240
8
    return value_int(builtin_ord(args[0].as.str_val));
241
8
}
242
243
4
static LatValue native_chr(LatValue *args, int arg_count) {
244
4
    if (arg_count != 1 || args[0].type != VAL_INT) return value_string("");
245
4
    char *s = builtin_chr(args[0].as.int_val);
246
4
    LatValue r = value_string(s);
247
4
    free(s);
248
4
    return r;
249
4
}
250
251
6
static LatValue native_abs(LatValue *args, int arg_count) {
252
6
    if (arg_count != 1) return value_int(0);
253
6
    if (args[0].type == VAL_INT) return value_int(args[0].as.int_val < 0 ? -args[0].as.int_val : args[0].as.int_val);
254
2
    if (args[0].type == VAL_FLOAT) return value_float(args[0].as.float_val < 0 ? -args[0].as.float_val : args[0].as.float_val);
255
0
    return value_int(0);
256
2
}
257
258
5
static LatValue native_floor(LatValue *args, int arg_count) {
259
5
    if (arg_count != 1) return value_int(0);
260
5
    if (args[0].type == VAL_FLOAT) return value_int((int64_t)args[0].as.float_val);
261
0
    if (args[0].type == VAL_INT) return args[0];
262
0
    return value_int(0);
263
0
}
264
265
2
static LatValue native_ceil(LatValue *args, int arg_count) {
266
2
    if (arg_count != 1) return value_int(0);
267
2
    if (args[0].type == VAL_FLOAT) {
268
2
        double v = args[0].as.float_val;
269
2
        int64_t i = (int64_t)v;
270
2
        return value_int(v > (double)i ? i + 1 : i);
271
2
    }
272
0
    if (args[0].type == VAL_INT) return args[0];
273
0
    return value_int(0);
274
0
}
275
276
0
static LatValue native_exit(LatValue *args, int arg_count) {
277
0
    int code = 0;
278
0
    if (arg_count >= 1 && args[0].type == VAL_INT) code = (int)args[0].as.int_val;
279
0
    exit(code);
280
0
    return value_unit();
281
0
}
282
283
2
static LatValue native_error(LatValue *args, int arg_count) {
284
2
    if (arg_count < 1) return value_nil();
285
    /* Create an error map: {"tag" => "err", "value" => arg} */
286
2
    LatValue map_val = value_map_new();
287
2
    LatValue tag = value_string("err");
288
2
    lat_map_set(map_val.as.map.map, "tag", &tag);
289
2
    LatValue val = value_deep_clone(&args[0]);
290
2
    lat_map_set(map_val.as.map.map, "value", &val);
291
2
    return map_val;
292
2
}
293
294
6
static LatValue native_is_error(LatValue *args, int arg_count) {
295
6
    if (arg_count < 1) return value_bool(false);
296
6
    if (args[0].type != VAL_MAP) return value_bool(false);
297
2
    LatValue *tag = lat_map_get(args[0].as.map.map, "tag");
298
2
    if (!tag || tag->type != VAL_STR) return value_bool(false);
299
2
    return value_bool(strcmp(tag->as.str_val, "err") == 0);
300
2
}
301
302
822
static LatValue native_map_new(LatValue *args, int arg_count) {
303
822
    (void)args; (void)arg_count;
304
822
    return value_map_new();
305
822
}
306
307
8
static LatValue native_set_new(LatValue *args, int arg_count) {
308
8
    (void)args; (void)arg_count;
309
8
    return value_set_new();
310
8
}
311
312
24
static LatValue native_set_from(LatValue *args, int arg_count) {
313
24
    if (arg_count != 1 || args[0].type != VAL_ARRAY) return value_set_new();
314
24
    LatValue set = value_set_new();
315
84
    for (size_t i = 0; i < args[0].as.array.len; i++) {
316
60
        char *key = value_display(&args[0].as.array.elems[i]);
317
60
        LatValue clone = value_deep_clone(&args[0].as.array.elems[i]);
318
60
        lat_map_set(set.as.set.map, key, &clone);
319
60
        free(key);
320
60
    }
321
24
    return set;
322
24
}
323
324
28
static LatValue native_channel_new(LatValue *args, int arg_count) {
325
28
    (void)args; (void)arg_count;
326
28
    LatChannel *ch = channel_new();
327
28
    LatValue val = value_channel(ch);
328
28
    channel_release(ch);
329
28
    return val;
330
28
}
331
332
/* ── Buffer constructors ── */
333
334
16
static LatValue native_buffer_new(LatValue *args, int arg_count) {
335
16
    if (arg_count != 1 || args[0].type != VAL_INT) return value_buffer_alloc(0);
336
16
    int64_t size = args[0].as.int_val;
337
16
    if (size < 0) size = 0;
338
16
    return value_buffer_alloc((size_t)size);
339
16
}
340
341
16
static LatValue native_buffer_from(LatValue *args, int arg_count) {
342
16
    if (arg_count != 1 || args[0].type != VAL_ARRAY) return value_buffer(NULL, 0);
343
16
    size_t len = args[0].as.array.len;
344
16
    uint8_t *data = malloc(len > 0 ? len : 1);
345
68
    for (size_t i = 0; i < len; i++) {
346
52
        if (args[0].as.array.elems[i].type == VAL_INT)
347
52
            data[i] = (uint8_t)(args[0].as.array.elems[i].as.int_val & 0xFF);
348
0
        else
349
0
            data[i] = 0;
350
52
    }
351
16
    LatValue buf = value_buffer(data, len);
352
16
    free(data);
353
16
    return buf;
354
16
}
355
356
4
static LatValue native_buffer_from_string(LatValue *args, int arg_count) {
357
4
    if (arg_count != 1 || args[0].type != VAL_STR) return value_buffer(NULL, 0);
358
4
    const char *s = args[0].as.str_val;
359
4
    size_t len = strlen(s);
360
4
    return value_buffer((const uint8_t *)s, len);
361
4
}
362
363
30
static LatValue native_ref_new(LatValue *args, int arg_count) {
364
30
    if (arg_count != 1) return value_nil();
365
30
    return value_ref(args[0]);
366
30
}
367
368
0
static LatValue native_read_file_bytes(LatValue *args, int ac) {
369
0
    if (ac != 1 || args[0].type != VAL_STR) return value_nil();
370
0
    FILE *f = fopen(args[0].as.str_val, "rb");
371
0
    if (!f) return value_nil();
372
0
    fseek(f, 0, SEEK_END);
373
0
    long flen = ftell(f);
374
0
    fseek(f, 0, SEEK_SET);
375
0
    if (flen < 0) { fclose(f); return value_nil(); }
376
0
    uint8_t *data = malloc((size_t)flen);
377
0
    size_t nread = fread(data, 1, (size_t)flen, f);
378
0
    fclose(f);
379
0
    LatValue buf = value_buffer(data, nread);
380
0
    free(data);
381
0
    return buf;
382
0
}
383
384
0
static LatValue native_write_file_bytes(LatValue *args, int ac) {
385
0
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_BUFFER) return value_bool(false);
386
0
    FILE *f = fopen(args[0].as.str_val, "wb");
387
0
    if (!f) return value_bool(false);
388
0
    size_t written = fwrite(args[1].as.buffer.data, 1, args[1].as.buffer.len, f);
389
0
    fclose(f);
390
0
    return value_bool(written == args[1].as.buffer.len);
391
0
}
392
393
/* ── Phase 6: Phase system native functions ── */
394
395
28
static LatValue native_track(LatValue *args, int ac) {
396
28
    if (ac != 1 || args[0].type != VAL_STR || !current_rt) return value_unit();
397
28
    const char *name = args[0].as.str_val;
398
    /* Check if already tracked */
399
28
    for (size_t i = 0; i < current_rt->tracked_count; i++) {
400
0
        if (strcmp(current_rt->tracked_vars[i].name, name) == 0) return value_unit();
401
0
    }
402
    /* Find the variable's current value (try env first, then locals) */
403
28
    LatValue val;
404
28
    bool found = env_get(current_rt->env, name, &val);
405
28
    if (!found) found = current_rt->find_local_value(current_rt->active_vm, name, &val);
406
28
    if (!found) {
407
2
        char *msg = NULL;
408
2
        (void)asprintf(&msg, "track: undefined variable '%s'", name);
409
2
        current_rt->error = msg;
410
2
        return value_unit();
411
2
    }
412
    /* Register tracking */
413
26
    if (current_rt->tracked_count >= current_rt->tracked_cap) {
414
26
        current_rt->tracked_cap = current_rt->tracked_cap ? current_rt->tracked_cap * 2 : 4;
415
26
        current_rt->tracked_vars = realloc(current_rt->tracked_vars,
416
26
            current_rt->tracked_cap * sizeof(*current_rt->tracked_vars));
417
26
    }
418
26
    size_t idx = current_rt->tracked_count++;
419
26
    current_rt->tracking_active = true;
420
26
    current_rt->tracked_vars[idx].name = strdup(name);
421
26
    current_rt->tracked_vars[idx].snapshots = NULL;
422
26
    current_rt->tracked_vars[idx].snap_count = 0;
423
26
    current_rt->tracked_vars[idx].snap_cap = 0;
424
    /* Record initial snapshot */
425
26
    rt_record_history(current_rt, name, &val);
426
26
    value_free(&val);
427
26
    return value_unit();
428
28
}
429
430
16
static LatValue native_phases(LatValue *args, int ac) {
431
16
    if (ac != 1 || args[0].type != VAL_STR || !current_rt) {
432
0
        return value_array(NULL, 0);
433
0
    }
434
16
    const char *name = args[0].as.str_val;
435
16
    for (size_t i = 0; i < current_rt->tracked_count; i++) {
436
14
        if (strcmp(current_rt->tracked_vars[i].name, name) != 0) continue;
437
14
        size_t n = current_rt->tracked_vars[i].snap_count;
438
14
        LatValue *elems = malloc(n * sizeof(LatValue));
439
46
        for (size_t j = 0; j < n; j++) {
440
32
            LatValue m = value_map_new();
441
32
            LatValue phase_val = value_string(current_rt->tracked_vars[i].snapshots[j].phase);
442
32
            LatValue val_clone = value_deep_clone(&current_rt->tracked_vars[i].snapshots[j].value);
443
32
            LatValue line_val = value_int(current_rt->tracked_vars[i].snapshots[j].line);
444
32
            LatValue fn_val = current_rt->tracked_vars[i].snapshots[j].fn_name
445
32
                ? value_string(current_rt->tracked_vars[i].snapshots[j].fn_name)
446
32
                : value_nil();
447
32
            lat_map_set(m.as.map.map, "phase", &phase_val);
448
32
            lat_map_set(m.as.map.map, "value", &val_clone);
449
32
            lat_map_set(m.as.map.map, "line", &line_val);
450
32
            lat_map_set(m.as.map.map, "fn", &fn_val);
451
32
            elems[j] = m;
452
32
        }
453
14
        LatValue arr = value_array(elems, n);
454
14
        free(elems);
455
14
        return arr;
456
14
    }
457
2
    return value_array(NULL, 0);
458
16
}
459
460
/// @builtin history(name: String) -> Array
461
/// @category Temporal
462
/// Returns the full enriched timeline of a tracked variable as an array of Maps
463
/// with keys: phase, value, line, fn.
464
/// @example history(x)  // [{phase: "fluid", value: 10, line: 3, fn: "main"}, ...]
465
8
static LatValue native_history(LatValue *args, int ac) {
466
8
    if (ac != 1 || args[0].type != VAL_STR || !current_rt)
467
0
        return value_array(NULL, 0);
468
8
    const char *name = args[0].as.str_val;
469
8
    for (size_t i = 0; i < current_rt->tracked_count; i++) {
470
6
        if (strcmp(current_rt->tracked_vars[i].name, name) != 0) continue;
471
6
        size_t n = current_rt->tracked_vars[i].snap_count;
472
6
        LatValue *elems = malloc(n * sizeof(LatValue));
473
20
        for (size_t j = 0; j < n; j++) {
474
14
            LatValue m = value_map_new();
475
14
            LatValue phase_val = value_string(current_rt->tracked_vars[i].snapshots[j].phase);
476
14
            LatValue val_clone = value_deep_clone(&current_rt->tracked_vars[i].snapshots[j].value);
477
14
            LatValue line_val = value_int(current_rt->tracked_vars[i].snapshots[j].line);
478
14
            LatValue fn_val = current_rt->tracked_vars[i].snapshots[j].fn_name
479
14
                ? value_string(current_rt->tracked_vars[i].snapshots[j].fn_name)
480
14
                : value_nil();
481
14
            lat_map_set(m.as.map.map, "phase", &phase_val);
482
14
            lat_map_set(m.as.map.map, "value", &val_clone);
483
14
            lat_map_set(m.as.map.map, "line", &line_val);
484
14
            lat_map_set(m.as.map.map, "fn", &fn_val);
485
14
            elems[j] = m;
486
14
        }
487
6
        LatValue arr = value_array(elems, n);
488
6
        free(elems);
489
6
        return arr;
490
6
    }
491
2
    return value_array(NULL, 0);
492
8
}
493
494
14
static LatValue native_rewind(LatValue *args, int ac) {
495
14
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_INT || !current_rt)
496
0
        return value_nil();
497
14
    const char *name = args[0].as.str_val;
498
14
    int64_t steps = args[1].as.int_val;
499
14
    for (size_t i = 0; i < current_rt->tracked_count; i++) {
500
14
        if (strcmp(current_rt->tracked_vars[i].name, name) != 0) continue;
501
14
        int64_t idx = (int64_t)current_rt->tracked_vars[i].snap_count - 1 - steps;
502
14
        if (idx < 0 || idx >= (int64_t)current_rt->tracked_vars[i].snap_count)
503
2
            return value_nil();
504
12
        return value_deep_clone(&current_rt->tracked_vars[i].snapshots[idx].value);
505
14
    }
506
0
    return value_nil();
507
14
}
508
509
12
static LatValue native_pressurize(LatValue *args, int ac) {
510
12
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR || !current_rt)
511
0
        return value_unit();
512
12
    const char *name = args[0].as.str_val;
513
12
    const char *mode = args[1].as.str_val;
514
    /* Validate mode */
515
12
    if (strcmp(mode, "no_grow") != 0 && strcmp(mode, "no_shrink") != 0 &&
516
12
        strcmp(mode, "no_resize") != 0 && strcmp(mode, "read_heavy") != 0)
517
0
        return value_unit();
518
    /* Update existing or add new */
519
12
    for (size_t i = 0; i < current_rt->pressure_count; i++) {
520
0
        if (strcmp(current_rt->pressures[i].name, name) == 0) {
521
0
            free(current_rt->pressures[i].mode);
522
0
            current_rt->pressures[i].mode = strdup(mode);
523
0
            return value_unit();
524
0
        }
525
0
    }
526
12
    if (current_rt->pressure_count >= current_rt->pressure_cap) {
527
12
        current_rt->pressure_cap = current_rt->pressure_cap ? current_rt->pressure_cap * 2 : 4;
528
12
        current_rt->pressures = realloc(current_rt->pressures,
529
12
            current_rt->pressure_cap * sizeof(*current_rt->pressures));
530
12
    }
531
12
    size_t idx = current_rt->pressure_count++;
532
12
    current_rt->pressures[idx].name = strdup(name);
533
12
    current_rt->pressures[idx].mode = strdup(mode);
534
12
    return value_unit();
535
12
}
536
537
2
static LatValue native_depressurize(LatValue *args, int ac) {
538
2
    if (ac != 1 || args[0].type != VAL_STR || !current_rt) return value_unit();
539
2
    const char *name = args[0].as.str_val;
540
2
    for (size_t i = 0; i < current_rt->pressure_count; i++) {
541
2
        if (strcmp(current_rt->pressures[i].name, name) == 0) {
542
2
            free(current_rt->pressures[i].name);
543
2
            free(current_rt->pressures[i].mode);
544
2
            current_rt->pressures[i] = current_rt->pressures[--current_rt->pressure_count];
545
2
            return value_unit();
546
2
        }
547
2
    }
548
0
    return value_unit();
549
2
}
550
551
4
static LatValue native_pressure_of(LatValue *args, int ac) {
552
4
    if (ac != 1 || args[0].type != VAL_STR || !current_rt) return value_nil();
553
4
    const char *name = args[0].as.str_val;
554
4
    for (size_t i = 0; i < current_rt->pressure_count; i++) {
555
2
        if (strcmp(current_rt->pressures[i].name, name) == 0)
556
2
            return value_string(current_rt->pressures[i].mode);
557
2
    }
558
2
    return value_nil();
559
4
}
560
/* Full grow() implementation as native function */
561
2
static LatValue native_grow(LatValue *args, int ac) {
562
2
    if (ac != 1 || args[0].type != VAL_STR || !current_rt) return value_nil();
563
2
    const char *vname = args[0].as.str_val;
564
2
    LatValue val;
565
2
    if (!current_rt->get_var_by_name(current_rt->active_vm, vname, &val)) return value_nil();
566
567
    /* Validate and consume all seeds */
568
2
    char *err = rt_validate_seeds(current_rt, vname, &val, true);
569
2
    if (err) {
570
1
        value_free(&val);
571
        /* Report error through StackVM */
572
1
        current_rt->error = err;
573
1
        return value_nil();
574
1
    }
575
576
    /* Freeze */
577
1
    LatValue frozen = value_freeze(val);
578
1
    LatValue ret = value_deep_clone(&frozen);
579
1
    current_rt->set_var_by_name(current_rt->active_vm, vname, value_deep_clone(&frozen));
580
1
    rt_record_history(current_rt, vname, &frozen);
581
1
    value_free(&frozen);
582
583
    /* Cascade and reactions */
584
585
1
    rt_freeze_cascade(current_rt, vname);
586
1
    rt_fire_reactions(current_rt, vname, "crystal");
587
588
1
    return ret;
589
2
}
590
591
58
static LatValue native_phase_of(LatValue *args, int arg_count) {
592
58
    if (arg_count != 1) return value_string("unknown");
593
58
    return value_string(builtin_phase_of_str(&args[0]));
594
58
}
595
596
184
static LatValue native_assert(LatValue *args, int arg_count) {
597
184
    if (arg_count < 1) return value_unit();
598
184
    bool ok = false;
599
184
    if (args[0].type == VAL_BOOL) ok = args[0].as.bool_val;
600
0
    else if (args[0].type == VAL_INT) ok = args[0].as.int_val != 0;
601
0
    else ok = args[0].type != VAL_NIL;
602
184
    if (!ok) {
603
6
        const char *msg = (arg_count >= 2 && args[1].type == VAL_STR) ? args[1].as.str_val : "assertion failed";
604
        /* Use StackVM error mechanism instead of exit() so tests can catch failures */
605
6
        if (current_rt) {
606
6
            char *err = NULL;
607
6
            (void)asprintf(&err, "assertion failed: %s", msg);
608
6
            current_rt->error = err;
609
6
        } else {
610
0
            fprintf(stderr, "assertion failed: %s\n", msg);
611
0
            exit(1);
612
0
        }
613
6
    }
614
184
    return value_unit();
615
184
}
616
617
4
static LatValue native_version(LatValue *args, int arg_count) {
618
4
    (void)args; (void)arg_count;
619
4
    return value_string(LATTICE_VERSION);
620
4
}
621
622
0
static LatValue native_input(LatValue *args, int arg_count) {
623
0
    const char *prompt = NULL;
624
0
    if (arg_count >= 1 && args[0].type == VAL_STR) prompt = args[0].as.str_val;
625
0
    char *line = builtin_input(prompt);
626
0
    if (!line) return value_nil();
627
0
    LatValue r = value_string(line);
628
0
    free(line);
629
0
    return r;
630
0
}
631
632
/* ── Math natives (via math_ops.h) ── */
633
634
#define MATH1(cname, mathfn) \
635
57
static LatValue cname(LatValue *args, int ac) { \
636
57
    if (ac != 1) return value_nil(); \
637
57
    char *err = NULL; \
638
57
    LatValue r = mathfn(&args[0], &err); \
639
57
    if (err) { current_rt->error = err; return value_nil(); } \
640
57
    return r; \
641
57
}
642
643
#define MATH2(cname, mathfn) \
644
18
static LatValue cname(LatValue *args, int ac) { \
645
18
    if (ac != 2) return value_nil(); \
646
18
    char *err = NULL; \
647
18
    LatValue r = mathfn(&args[0], &args[1], &err); \
648
18
    if (err) { current_rt->error = err; return value_nil(); } \
649
18
    return r; \
650
18
}
651
652
#define MATH3(cname, mathfn) \
653
10
static LatValue cname(LatValue *args, int ac) { \
654
10
    if (ac != 3) return value_nil(); \
655
10
    char *err = NULL; \
656
10
    LatValue r = mathfn(&args[0], &args[1], &args[2], &err); \
657
10
    if (err) { current_rt->error = err; return value_nil(); } \
658
10
    return r; \
659
10
}
660
661
4
MATH1(native_round, math_round)
662
9
MATH1(native_sqrt, math_sqrt)
663
4
MATH2(native_pow, math_pow)
664
4
MATH2(native_min, math_min)
665
4
MATH2(native_max, math_max)
666
2
MATH1(native_log, math_log)
667
2
MATH1(native_log2, math_log2)
668
2
MATH1(native_log10, math_log10)
669
6
MATH1(native_sin, math_sin)
670
2
MATH1(native_cos, math_cos)
671
2
MATH1(native_tan, math_tan)
672
2
MATH1(native_asin, math_asin)
673
2
MATH1(native_acos, math_acos)
674
2
MATH1(native_atan, math_atan)
675
2
MATH2(native_atan2, math_atan2)
676
2
MATH1(native_exp, math_exp)
677
6
MATH1(native_sign, math_sign)
678
2
MATH2(native_gcd, math_gcd)
679
2
MATH2(native_lcm, math_lcm)
680
4
MATH1(native_is_nan, math_is_nan)
681
4
MATH1(native_is_inf, math_is_inf)
682
2
MATH1(native_sinh, math_sinh)
683
2
MATH1(native_cosh, math_cosh)
684
2
MATH1(native_tanh, math_tanh)
685
4
MATH3(native_lerp, math_lerp)
686
6
MATH3(native_clamp, math_clamp)
687
688
2
static LatValue native_random(LatValue *args, int ac) {
689
2
    (void)args; (void)ac;
690
2
    return math_random();
691
2
}
692
2
static LatValue native_random_int(LatValue *args, int ac) {
693
2
    if (ac != 2) return value_nil();
694
2
    char *err = NULL;
695
2
    LatValue r = math_random_int(&args[0], &args[1], &err);
696
2
    if (err) { current_rt->error = err; return value_nil(); }
697
2
    return r;
698
2
}
699
5
static LatValue native_math_pi(LatValue *args, int ac) {
700
5
    (void)args; (void)ac;
701
5
    return math_pi();
702
5
}
703
4
static LatValue native_math_e(LatValue *args, int ac) {
704
4
    (void)args; (void)ac;
705
4
    return math_e();
706
4
}
707
708
#undef MATH1
709
#undef MATH2
710
#undef MATH3
711
712
/* ── File system natives ── */
713
714
8
static LatValue native_read_file(LatValue *args, int ac) {
715
8
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("read_file() expects (path: String)"); return value_nil(); }
716
8
    char *contents = builtin_read_file(args[0].as.str_val);
717
8
    if (!contents) return value_nil();
718
8
    return value_string_owned(contents);
719
8
}
720
26
static LatValue native_write_file(LatValue *args, int ac) {
721
26
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) { current_rt->error = strdup("write_file() expects (path: String, data: String)"); return value_bool(false); }
722
26
    return value_bool(builtin_write_file(args[0].as.str_val, args[1].as.str_val));
723
26
}
724
16
static LatValue native_file_exists(LatValue *args, int ac) {
725
16
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("file_exists() expects (path: String)"); return value_bool(false); }
726
14
    return value_bool(fs_file_exists(args[0].as.str_val));
727
16
}
728
26
static LatValue native_delete_file(LatValue *args, int ac) {
729
26
    if (ac != 1 || args[0].type != VAL_STR) {
730
2
        current_rt->error = strdup("delete_file: expected (path: Str)");
731
2
        return value_bool(false);
732
2
    }
733
24
    char *err = NULL;
734
24
    bool ok = fs_delete_file(args[0].as.str_val, &err);
735
24
    if (err) { current_rt->error = err; return value_bool(false); }
736
22
    return value_bool(ok);
737
24
}
738
6
static LatValue native_list_dir(LatValue *args, int ac) {
739
6
    if (ac != 1 || args[0].type != VAL_STR) {
740
2
        current_rt->error = strdup("list_dir: expected (path: Str)");
741
2
        return value_array(NULL, 0);
742
2
    }
743
4
    char *err = NULL;
744
4
    size_t count = 0;
745
4
    char **entries = fs_list_dir(args[0].as.str_val, &count, &err);
746
4
    if (err) { current_rt->error = err; return value_array(NULL, 0); }
747
2
    if (!entries) return value_array(NULL, 0);
748
2
    LatValue *elems = malloc((count > 0 ? count : 1) * sizeof(LatValue));
749
3.25k
    for (size_t i = 0; i < count; i++) {
750
3.24k
        elems[i] = value_string(entries[i]);
751
3.24k
        free(entries[i]);
752
3.24k
    }
753
2
    free(entries);
754
2
    LatValue r = value_array(elems, count);
755
2
    free(elems);
756
2
    return r;
757
2
}
758
6
static LatValue native_append_file(LatValue *args, int ac) {
759
6
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) { current_rt->error = strdup("append_file() expects (path: String, data: String)"); return value_bool(false); }
760
4
    char *err = NULL;
761
4
    bool ok = fs_append_file(args[0].as.str_val, args[1].as.str_val, &err);
762
4
    if (err) { current_rt->error = err; return value_bool(false); }
763
4
    return value_bool(ok);
764
4
}
765
6
static LatValue native_mkdir(LatValue *args, int ac) {
766
6
    if (ac != 1 || args[0].type != VAL_STR) return value_bool(false);
767
6
    char *err = NULL;
768
6
    bool ok = fs_mkdir(args[0].as.str_val, &err);
769
6
    if (err) { current_rt->error = err; return value_bool(false); }
770
6
    return value_bool(ok);
771
6
}
772
2
static LatValue native_fs_rename(LatValue *args, int ac) {
773
2
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
774
0
        current_rt->error = strdup("rename: expected (from: Str, to: Str)");
775
0
        return value_bool(false);
776
0
    }
777
2
    char *err = NULL;
778
2
    bool ok = fs_rename(args[0].as.str_val, args[1].as.str_val, &err);
779
2
    if (err) { current_rt->error = err; return value_bool(false); }
780
2
    return value_bool(ok);
781
2
}
782
10
static LatValue native_is_dir(LatValue *args, int ac) {
783
10
    if (ac != 1 || args[0].type != VAL_STR) return value_bool(false);
784
10
    return value_bool(fs_is_dir(args[0].as.str_val));
785
10
}
786
6
static LatValue native_is_file(LatValue *args, int ac) {
787
6
    if (ac != 1 || args[0].type != VAL_STR) return value_bool(false);
788
6
    return value_bool(fs_is_file(args[0].as.str_val));
789
6
}
790
8
static LatValue native_rmdir(LatValue *args, int ac) {
791
8
    if (ac != 1 || args[0].type != VAL_STR) {
792
0
        current_rt->error = strdup("rmdir: expected (path: Str)");
793
0
        return value_bool(false);
794
0
    }
795
8
    char *err = NULL;
796
8
    bool ok = fs_rmdir(args[0].as.str_val, &err);
797
8
    if (err) { current_rt->error = err; return value_bool(false); }
798
6
    return value_bool(ok);
799
8
}
800
4
static LatValue native_glob(LatValue *args, int ac) {
801
4
    if (ac != 1 || args[0].type != VAL_STR) return value_array(NULL, 0);
802
4
    char *err = NULL;
803
4
    size_t count = 0;
804
4
    char **matches = fs_glob(args[0].as.str_val, &count, &err);
805
4
    if (err) { current_rt->error = err; return value_array(NULL, 0); }
806
4
    if (!matches) return value_array(NULL, 0);
807
2
    LatValue *elems = malloc((count > 0 ? count : 1) * sizeof(LatValue));
808
6
    for (size_t i = 0; i < count; i++) {
809
4
        elems[i] = value_string(matches[i]);
810
4
        free(matches[i]);
811
4
    }
812
2
    free(matches);
813
2
    LatValue r = value_array(elems, count);
814
2
    free(elems);
815
2
    return r;
816
4
}
817
6
static LatValue native_stat(LatValue *args, int ac) {
818
6
    if (ac != 1 || args[0].type != VAL_STR) {
819
0
        current_rt->error = strdup("stat: expected (path: Str)");
820
0
        return value_nil();
821
0
    }
822
6
    int64_t sz, mt, md;
823
6
    const char *tp;
824
6
    char *err = NULL;
825
6
    if (!fs_stat(args[0].as.str_val, &sz, &mt, &md, &tp, &err)) {
826
2
        if (err) current_rt->error = err;
827
2
        return value_nil();
828
2
    }
829
4
    LatValue map = value_map_new();
830
4
    LatValue v_sz = value_int(sz); lat_map_set(map.as.map.map, "size", &v_sz);
831
4
    LatValue v_mt = value_int(mt); lat_map_set(map.as.map.map, "mtime", &v_mt);
832
4
    LatValue v_md = value_int(md); lat_map_set(map.as.map.map, "mode", &v_md);
833
4
    LatValue v_tp = value_string(tp); lat_map_set(map.as.map.map, "type", &v_tp);
834
4
    LatValue v_pm = value_int(md); lat_map_set(map.as.map.map, "permissions", &v_pm);
835
4
    return map;
836
6
}
837
4
static LatValue native_copy_file(LatValue *args, int ac) {
838
4
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
839
0
        current_rt->error = strdup("copy_file: expected (src: Str, dst: Str)");
840
0
        return value_bool(false);
841
0
    }
842
4
    char *err = NULL;
843
4
    bool ok = fs_copy_file(args[0].as.str_val, args[1].as.str_val, &err);
844
4
    if (err) { current_rt->error = err; return value_bool(false); }
845
2
    return value_bool(ok);
846
4
}
847
4
static LatValue native_realpath(LatValue *args, int ac) {
848
4
    if (ac != 1 || args[0].type != VAL_STR) {
849
0
        current_rt->error = strdup("realpath: expected (path: Str)");
850
0
        return value_nil();
851
0
    }
852
4
    char *err = NULL;
853
4
    char *r = fs_realpath(args[0].as.str_val, &err);
854
4
    if (err) { current_rt->error = err; return value_nil(); }
855
2
    if (!r) return value_nil();
856
2
    return value_string_owned(r);
857
2
}
858
2
static LatValue native_tempdir(LatValue *args, int ac) {
859
2
    (void)args; (void)ac;
860
2
    char *err = NULL; char *r = fs_tempdir(&err);
861
2
    if (err) { current_rt->error = err; return value_nil(); }
862
2
    if (!r) return value_nil();
863
2
    return value_string_owned(r);
864
2
}
865
4
static LatValue native_tempfile(LatValue *args, int ac) {
866
4
    (void)args; (void)ac;
867
4
    char *err = NULL; char *r = fs_tempfile(&err);
868
4
    if (err) { current_rt->error = err; return value_nil(); }
869
4
    if (!r) return value_nil();
870
4
    return value_string_owned(r);
871
4
}
872
2
static LatValue native_chmod(LatValue *args, int ac) {
873
2
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_INT) return value_bool(false);
874
2
    char *err = NULL;
875
2
    bool ok = fs_chmod(args[0].as.str_val, (int)args[1].as.int_val, &err);
876
2
    if (err) { current_rt->error = err; return value_bool(false); }
877
2
    return value_bool(ok);
878
2
}
879
4
static LatValue native_file_size(LatValue *args, int ac) {
880
4
    if (ac != 1 || args[0].type != VAL_STR) {
881
0
        current_rt->error = strdup("file_size: expected (path: Str)");
882
0
        return value_int(-1);
883
0
    }
884
4
    char *err = NULL;
885
4
    int64_t sz = fs_file_size(args[0].as.str_val, &err);
886
4
    if (err) { current_rt->error = err; return value_int(-1); }
887
2
    return value_int(sz);
888
4
}
889
890
/* ── Path natives ── */
891
892
13
static LatValue native_path_join(LatValue *args, int ac) {
893
13
    if (ac < 1) { current_rt->error = strdup("path_join() expects at least 1 argument"); return value_string(""); }
894
37
    for (int i = 0; i < ac; i++) {
895
26
        if (args[i].type != VAL_STR) { current_rt->error = strdup("path_join() expects (String...)"); return value_string(""); }
896
26
    }
897
11
    const char **parts = malloc((size_t)ac * sizeof(char *));
898
35
    for (int i = 0; i < ac; i++)
899
24
        parts[i] = args[i].as.str_val;
900
11
    char *r = path_join(parts, (size_t)ac);
901
11
    free(parts); return value_string_owned(r);
902
13
}
903
10
static LatValue native_path_dir(LatValue *args, int ac) {
904
10
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("path_dir() expects (path: String)"); return value_string("."); }
905
8
    return value_string_owned(path_dir(args[0].as.str_val));
906
10
}
907
8
static LatValue native_path_base(LatValue *args, int ac) {
908
8
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("path_base() expects (path: String)"); return value_string(""); }
909
6
    return value_string_owned(path_base(args[0].as.str_val));
910
8
}
911
12
static LatValue native_path_ext(LatValue *args, int ac) {
912
12
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("path_ext() expects (path: String)"); return value_string(""); }
913
10
    return value_string_owned(path_ext(args[0].as.str_val));
914
12
}
915
916
/* ── Network TCP natives ── */
917
918
4
static LatValue native_tcp_listen(LatValue *args, int ac) {
919
4
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_INT) {
920
2
        current_rt->error = strdup("tcp_listen: expected (host: Str, port: Int)");
921
2
        return value_int(-1);
922
2
    }
923
2
    char *err = NULL;
924
2
    int fd = net_tcp_listen(args[0].as.str_val, (int)args[1].as.int_val, &err);
925
2
    if (err) { current_rt->error = err; return value_int(-1); }
926
2
    return value_int(fd);
927
2
}
928
0
static LatValue native_tcp_accept(LatValue *args, int ac) {
929
0
    if (ac != 1 || args[0].type != VAL_INT) {
930
0
        current_rt->error = strdup("tcp_accept: expected (fd: Int)");
931
0
        return value_int(-1);
932
0
    }
933
0
    char *err = NULL;
934
0
    int fd = net_tcp_accept((int)args[0].as.int_val, &err);
935
0
    if (err) { current_rt->error = err; return value_int(-1); }
936
0
    return value_int(fd);
937
0
}
938
0
static LatValue native_tcp_connect(LatValue *args, int ac) {
939
0
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_INT) {
940
0
        current_rt->error = strdup("tcp_connect: expected (host: Str, port: Int)");
941
0
        return value_int(-1);
942
0
    }
943
0
    char *err = NULL;
944
0
    int fd = net_tcp_connect(args[0].as.str_val, (int)args[1].as.int_val, &err);
945
0
    if (err) { current_rt->error = err; return value_int(-1); }
946
0
    return value_int(fd);
947
0
}
948
2
static LatValue native_tcp_read(LatValue *args, int ac) {
949
2
    if (ac != 1 || args[0].type != VAL_INT) {
950
2
        current_rt->error = strdup("tcp_read: expected (fd: Int)");
951
2
        return value_string("");
952
2
    }
953
0
    char *err = NULL;
954
0
    char *data = net_tcp_read((int)args[0].as.int_val, &err);
955
0
    if (err) { current_rt->error = err; return value_string(""); }
956
0
    if (!data) return value_string("");
957
0
    return value_string_owned(data);
958
0
}
959
0
static LatValue native_tcp_read_bytes(LatValue *args, int ac) {
960
0
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_INT) return value_string("");
961
0
    char *err = NULL;
962
0
    char *data = net_tcp_read_bytes((int)args[0].as.int_val, (size_t)args[1].as.int_val, &err);
963
0
    if (err) { current_rt->error = err; return value_string(""); }
964
0
    if (!data) return value_string("");
965
0
    return value_string_owned(data);
966
0
}
967
0
static LatValue native_tcp_write(LatValue *args, int ac) {
968
0
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_STR) return value_bool(false);
969
0
    char *err = NULL;
970
0
    bool ok = net_tcp_write((int)args[0].as.int_val, args[1].as.str_val, strlen(args[1].as.str_val), &err);
971
0
    if (err) { current_rt->error = err; return value_bool(false); }
972
0
    return value_bool(ok);
973
0
}
974
2
static LatValue native_tcp_close(LatValue *args, int ac) {
975
2
    if (ac != 1 || args[0].type != VAL_INT) return value_unit();
976
2
    net_tcp_close((int)args[0].as.int_val); return value_unit();
977
2
}
978
0
static LatValue native_tcp_peer_addr(LatValue *args, int ac) {
979
0
    if (ac != 1 || args[0].type != VAL_INT) return value_nil();
980
0
    char *err = NULL;
981
0
    char *addr = net_tcp_peer_addr((int)args[0].as.int_val, &err);
982
0
    if (err) { current_rt->error = err; return value_nil(); }
983
0
    if (!addr) return value_nil();
984
0
    return value_string_owned(addr);
985
0
}
986
0
static LatValue native_tcp_set_timeout(LatValue *args, int ac) {
987
0
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_INT) return value_bool(false);
988
0
    char *err = NULL;
989
0
    bool ok = net_tcp_set_timeout((int)args[0].as.int_val, (int)args[1].as.int_val, &err);
990
0
    if (err) { current_rt->error = err; return value_bool(false); }
991
0
    return value_bool(ok);
992
0
}
993
994
/* ── TLS natives ── */
995
996
2
static LatValue native_tls_connect(LatValue *args, int ac) {
997
2
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_INT) {
998
2
        current_rt->error = strdup("tls_connect: expected (host: Str, port: Int)");
999
2
        return value_int(-1);
1000
2
    }
1001
0
    char *err = NULL;
1002
0
    int fd = net_tls_connect(args[0].as.str_val, (int)args[1].as.int_val, &err);
1003
0
    if (err) { current_rt->error = err; return value_int(-1); }
1004
0
    return value_int(fd);
1005
0
}
1006
2
static LatValue native_tls_read(LatValue *args, int ac) {
1007
2
    if (ac != 1 || args[0].type != VAL_INT) { current_rt->error = strdup("tls_read() expects (fd: Int)"); return value_string(""); }
1008
0
    char *err = NULL;
1009
0
    char *data = net_tls_read((int)args[0].as.int_val, &err);
1010
0
    if (err) { current_rt->error = err; return value_string(""); }
1011
0
    if (!data) return value_string("");
1012
0
    return value_string_owned(data);
1013
0
}
1014
0
static LatValue native_tls_read_bytes(LatValue *args, int ac) {
1015
0
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_INT) { current_rt->error = strdup("tls_read_bytes() expects (fd: Int, n: Int)"); return value_string(""); }
1016
0
    char *err = NULL;
1017
0
    char *data = net_tls_read_bytes((int)args[0].as.int_val, (size_t)args[1].as.int_val, &err);
1018
0
    if (err) { current_rt->error = err; return value_string(""); }
1019
0
    if (!data) return value_string("");
1020
0
    return value_string_owned(data);
1021
0
}
1022
0
static LatValue native_tls_write(LatValue *args, int ac) {
1023
0
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_STR) { current_rt->error = strdup("tls_write() expects (fd: Int, data: String)"); return value_bool(false); }
1024
0
    char *err = NULL;
1025
0
    bool ok = net_tls_write((int)args[0].as.int_val, args[1].as.str_val, strlen(args[1].as.str_val), &err);
1026
0
    if (err) { current_rt->error = err; return value_bool(false); }
1027
0
    return value_bool(ok);
1028
0
}
1029
0
static LatValue native_tls_close(LatValue *args, int ac) {
1030
0
    if (ac != 1 || args[0].type != VAL_INT) { current_rt->error = strdup("tls_close() expects (fd: Int)"); return value_unit(); }
1031
0
    net_tls_close((int)args[0].as.int_val); return value_unit();
1032
0
}
1033
2
static LatValue native_tls_available(LatValue *args, int ac) {
1034
2
    (void)args; (void)ac; return value_bool(net_tls_available());
1035
2
}
1036
1037
/* ── HTTP natives ── */
1038
1039
0
static LatValue vm_build_http_response(HttpResponse *resp) {
1040
0
    LatValue map = value_map_new();
1041
0
    LatValue st = value_int(resp->status_code);
1042
0
    lat_map_set(map.as.map.map, "status", &st);
1043
0
    LatValue bd = value_string(resp->body ? resp->body : "");
1044
0
    lat_map_set(map.as.map.map, "body", &bd);
1045
0
    LatValue hdr = value_map_new();
1046
0
    for (size_t i = 0; i < resp->header_count; i++) {
1047
0
        LatValue hv = value_string(resp->header_values[i]);
1048
0
        lat_map_set(hdr.as.map.map, resp->header_keys[i], &hv);
1049
0
    }
1050
0
    lat_map_set(map.as.map.map, "headers", &hdr);
1051
0
    http_response_free(resp);
1052
0
    return map;
1053
0
}
1054
6
static LatValue native_http_get(LatValue *args, int ac) {
1055
6
    if (ac != 1 || args[0].type != VAL_STR) {
1056
4
        current_rt->error = strdup("http_get() expects (url: String)");
1057
4
        return value_nil();
1058
4
    }
1059
2
    HttpRequest req = {0}; req.method = "GET"; req.url = args[0].as.str_val;
1060
2
    char *err = NULL;
1061
2
    HttpResponse *resp = http_execute(&req, &err);
1062
2
    if (!resp) { current_rt->error = err ? err : strdup("http_get: request failed"); return value_nil(); }
1063
0
    return vm_build_http_response(resp);
1064
2
}
1065
4
static LatValue native_http_post(LatValue *args, int ac) {
1066
4
    if (ac < 1 || ac > 2 || args[0].type != VAL_STR) {
1067
2
        current_rt->error = strdup("http_post() expects (url: String, options?: Map)");
1068
2
        return value_nil();
1069
2
    }
1070
2
    HttpRequest req = {0}; req.method = "POST"; req.url = args[0].as.str_val;
1071
2
    if (ac >= 2 && args[1].type == VAL_STR) {
1072
0
        req.body = args[1].as.str_val; req.body_len = strlen(args[1].as.str_val);
1073
2
    } else if (ac >= 2 && args[1].type == VAL_MAP) {
1074
0
        LatValue *bv = lat_map_get(args[1].as.map.map, "body");
1075
0
        if (bv && bv->type == VAL_STR) { req.body = bv->as.str_val; req.body_len = strlen(bv->as.str_val); }
1076
0
    }
1077
2
    char *err = NULL;
1078
2
    HttpResponse *resp = http_execute(&req, &err);
1079
2
    if (!resp) { current_rt->error = err ? err : strdup("http_post: request failed"); return value_nil(); }
1080
0
    return vm_build_http_response(resp);
1081
2
}
1082
6
static LatValue native_http_request(LatValue *args, int ac) {
1083
6
    if (ac < 2 || ac > 3 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
1084
4
        current_rt->error = strdup("http_request() expects (method: String, url: String, options?: Map)");
1085
4
        return value_nil();
1086
4
    }
1087
2
    HttpRequest req = {0};
1088
2
    req.method = args[0].as.str_val;
1089
2
    req.url = args[1].as.str_val;
1090
2
    if (ac == 3 && args[2].type == VAL_MAP) {
1091
0
        LatValue *bv = lat_map_get(args[2].as.map.map, "body");
1092
0
        if (bv && bv->type == VAL_STR) { req.body = bv->as.str_val; req.body_len = strlen(bv->as.str_val); }
1093
0
    }
1094
2
    char *err = NULL;
1095
2
    HttpResponse *resp = http_execute(&req, &err);
1096
2
    if (!resp) { current_rt->error = err ? err : strdup("http_request: request failed"); return value_nil(); }
1097
0
    return vm_build_http_response(resp);
1098
2
}
1099
1100
/* ── JSON/TOML/YAML natives ── */
1101
1102
33
static LatValue native_json_parse(LatValue *args, int ac) {
1103
33
    if (ac != 1 || args[0].type != VAL_STR) {
1104
0
        current_rt->error = strdup("json_parse: expected (str: Str)");
1105
0
        return value_nil();
1106
0
    }
1107
33
    char *err = NULL;
1108
33
    LatValue r = json_parse(args[0].as.str_val, &err);
1109
33
    if (err) { current_rt->error = err; return value_nil(); }
1110
31
    return r;
1111
33
}
1112
16
static LatValue native_json_stringify(LatValue *args, int ac) {
1113
16
    if (ac != 1) {
1114
2
        current_rt->error = strdup("json_stringify: expected 1 argument");
1115
2
        return value_nil();
1116
2
    }
1117
14
    char *err = NULL;
1118
14
    char *r = json_stringify(&args[0], &err);
1119
14
    if (err) { current_rt->error = err; return value_nil(); }
1120
14
    return value_string_owned(r);
1121
14
}
1122
12
static LatValue native_toml_parse(LatValue *args, int ac) {
1123
12
    if (ac != 1 || args[0].type != VAL_STR) {
1124
2
        current_rt->error = strdup("toml_parse() expects (String)");
1125
2
        return value_nil();
1126
2
    }
1127
10
    char *err = NULL;
1128
10
    LatValue r = toml_ops_parse(args[0].as.str_val, &err);
1129
10
    if (err) { current_rt->error = err; return value_nil(); }
1130
10
    return r;
1131
10
}
1132
4
static LatValue native_toml_stringify(LatValue *args, int ac) {
1133
4
    if (ac != 1) {
1134
0
        current_rt->error = strdup("toml_stringify: expected 1 argument");
1135
0
        return value_nil();
1136
0
    }
1137
4
    char *err = NULL;
1138
4
    char *r = toml_ops_stringify(&args[0], &err);
1139
4
    if (err) { current_rt->error = err; return value_nil(); }
1140
2
    if (!r) return value_nil();
1141
2
    return value_string_owned(r);
1142
2
}
1143
14
static LatValue native_yaml_parse(LatValue *args, int ac) {
1144
14
    if (ac != 1 || args[0].type != VAL_STR) {
1145
2
        current_rt->error = strdup("yaml_parse() expects (String)");
1146
2
        return value_nil();
1147
2
    }
1148
12
    char *err = NULL;
1149
12
    LatValue r = yaml_ops_parse(args[0].as.str_val, &err);
1150
12
    if (err) { current_rt->error = err; return value_nil(); }
1151
12
    return r;
1152
12
}
1153
4
static LatValue native_yaml_stringify(LatValue *args, int ac) {
1154
4
    if (ac != 1) {
1155
0
        current_rt->error = strdup("yaml_stringify: expected 1 argument");
1156
0
        return value_nil();
1157
0
    }
1158
4
    if (args[0].type != VAL_MAP && args[0].type != VAL_ARRAY) {
1159
2
        current_rt->error = strdup("yaml_stringify: value must be a Map or Array");
1160
2
        return value_nil();
1161
2
    }
1162
2
    char *err = NULL;
1163
2
    char *r = yaml_ops_stringify(&args[0], &err);
1164
2
    if (err) { current_rt->error = err; return value_nil(); }
1165
2
    if (!r) return value_nil();
1166
2
    return value_string_owned(r);
1167
2
}
1168
1169
/* ── Crypto natives ── */
1170
1171
6
static LatValue native_sha256(LatValue *args, int ac) {
1172
6
    if (ac != 1 || args[0].type != VAL_STR) {
1173
2
        current_rt->error = strdup("sha256: expected (str: Str)");
1174
2
        return value_nil();
1175
2
    }
1176
4
    char *err = NULL;
1177
4
    char *r = crypto_sha256(args[0].as.str_val, strlen(args[0].as.str_val), &err);
1178
4
    if (err) { current_rt->error = err; return value_nil(); }
1179
4
    return value_string_owned(r);
1180
4
}
1181
6
static LatValue native_md5(LatValue *args, int ac) {
1182
6
    if (ac != 1 || args[0].type != VAL_STR) {
1183
2
        current_rt->error = strdup("md5: expected (str: Str)");
1184
2
        return value_nil();
1185
2
    }
1186
4
    char *err = NULL;
1187
4
    char *r = crypto_md5(args[0].as.str_val, strlen(args[0].as.str_val), &err);
1188
4
    if (err) { current_rt->error = err; return value_nil(); }
1189
4
    return value_string_owned(r);
1190
4
}
1191
18
static LatValue native_base64_encode(LatValue *args, int ac) {
1192
18
    if (ac != 1 || args[0].type != VAL_STR) {
1193
2
        current_rt->error = strdup("base64_encode: expected (str: Str)");
1194
2
        return value_nil();
1195
2
    }
1196
16
    return value_string_owned(crypto_base64_encode(args[0].as.str_val, strlen(args[0].as.str_val)));
1197
18
}
1198
12
static LatValue native_base64_decode(LatValue *args, int ac) {
1199
12
    if (ac != 1 || args[0].type != VAL_STR) {
1200
2
        current_rt->error = strdup("base64_decode: expected (str: Str)");
1201
2
        return value_nil();
1202
2
    }
1203
10
    char *err = NULL; size_t dl = 0;
1204
10
    char *r = crypto_base64_decode(args[0].as.str_val, strlen(args[0].as.str_val), &dl, &err);
1205
10
    if (err) { current_rt->error = err; return value_nil(); }
1206
8
    return value_string_owned(r);
1207
10
}
1208
1209
/* ── Regex natives ── */
1210
1211
8
static LatValue native_regex_match(LatValue *args, int ac) {
1212
8
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
1213
0
        current_rt->error = strdup("regex_match: expected (pattern: Str, input: Str)");
1214
0
        return value_bool(false);
1215
0
    }
1216
8
    char *err = NULL;
1217
8
    LatValue r = regex_match(args[0].as.str_val, args[1].as.str_val, &err);
1218
8
    if (err) { current_rt->error = err; return value_bool(false); }
1219
6
    return r;
1220
8
}
1221
9
static LatValue native_regex_find_all(LatValue *args, int ac) {
1222
9
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
1223
0
        current_rt->error = strdup("regex_find_all: expected (pattern: Str, input: Str)");
1224
0
        return value_array(NULL, 0);
1225
0
    }
1226
9
    char *err = NULL;
1227
9
    LatValue r = regex_find_all(args[0].as.str_val, args[1].as.str_val, &err);
1228
9
    if (err) { current_rt->error = err; return value_array(NULL, 0); }
1229
9
    return r;
1230
9
}
1231
8
static LatValue native_regex_replace(LatValue *args, int ac) {
1232
8
    if (ac != 3 || args[0].type != VAL_STR || args[1].type != VAL_STR || args[2].type != VAL_STR)
1233
0
        return value_nil();
1234
8
    char *err = NULL;
1235
8
    char *r = regex_replace(args[0].as.str_val, args[1].as.str_val, args[2].as.str_val, &err);
1236
8
    if (err) { current_rt->error = err; return value_nil(); }
1237
8
    if (!r) return value_nil();
1238
8
    return value_string_owned(r);
1239
8
}
1240
1241
/* ── Time/DateTime natives ── */
1242
1243
37
static LatValue native_time(LatValue *args, int ac) {
1244
37
    (void)args;
1245
37
    if (ac != 0) { current_rt->error = strdup("time() expects no arguments"); return value_int(0); }
1246
35
    return value_int(time_now_ms());
1247
37
}
1248
4
static LatValue native_sleep(LatValue *args, int ac) {
1249
4
    if (ac != 1 || args[0].type != VAL_INT) { current_rt->error = strdup("sleep() expects (ms: Int)"); return value_unit(); }
1250
2
    char *err = NULL; time_sleep_ms(args[0].as.int_val, &err);
1251
2
    if (err) { current_rt->error = err; return value_unit(); }
1252
2
    return value_unit();
1253
2
}
1254
10
static LatValue native_time_format(LatValue *args, int ac) {
1255
10
    if (ac != 2 || args[0].type != VAL_INT || args[1].type != VAL_STR) {
1256
2
        current_rt->error = strdup("time_format: expected (timestamp: Int, format: Str)");
1257
2
        return value_nil();
1258
2
    }
1259
8
    char *err = NULL;
1260
8
    char *r = datetime_format(args[0].as.int_val, args[1].as.str_val, &err);
1261
8
    if (err) { current_rt->error = err; return value_nil(); }
1262
8
    return value_string_owned(r);
1263
8
}
1264
10
static LatValue native_time_parse(LatValue *args, int ac) {
1265
10
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
1266
2
        current_rt->error = strdup("time_parse: expected (str: Str, format: Str)");
1267
2
        return value_nil();
1268
2
    }
1269
8
    char *err = NULL;
1270
8
    int64_t r = datetime_parse(args[0].as.str_val, args[1].as.str_val, &err);
1271
8
    if (err) { current_rt->error = err; return value_nil(); }
1272
6
    return value_int(r);
1273
8
}
1274
1275
/* ── Environment natives ── */
1276
1277
8
static LatValue native_env(LatValue *args, int ac) {
1278
8
    if (ac != 1 || args[0].type != VAL_STR) { current_rt->error = strdup("env() expects (key: String)"); return value_unit(); }
1279
6
    char *val = envvar_get(args[0].as.str_val);
1280
6
    if (!val) return value_unit();
1281
4
    return value_string_owned(val);
1282
6
}
1283
6
static LatValue native_env_set(LatValue *args, int ac) {
1284
6
    if (ac != 2 || args[0].type != VAL_STR || args[1].type != VAL_STR) {
1285
2
        current_rt->error = strdup("env_set: expected (key: Str, value: Str)");
1286
2
        return value_bool(false);
1287
2
    }
1288
4
    char *err = NULL;
1289
4
    bool ok = envvar_set(args[0].as.str_val, args[1].as.str_val, &err);
1290
4
    if (err) { current_rt->error = err; return value_bool(false); }
1291
4
    return value_bool(ok);
1292
4
}
1293
2
static LatValue native_env_keys(LatValue *args, int ac) {
1294
2
    (void)args; (void)ac;
1295
2
    char **keys = NULL; size_t count = 0;
1296
2
    envvar_keys(&keys, &count);
1297
2
    LatValue *elems = malloc((count > 0 ? count : 1) * sizeof(LatValue));
1298
152
    for (size_t i = 0; i < count; i++) { elems[i] = value_string(keys[i]); free(keys[i]); }
1299
2
    free(keys);
1300
2
    LatValue r = value_array(elems, count); free(elems);
1301
2
    return r;
1302
2
}
1303
1304
/* ── Process natives ── */
1305
1306
2
static LatValue native_cwd(LatValue *args, int ac) {
1307
2
    (void)args; (void)ac;
1308
2
    char *err = NULL; char *r = process_cwd(&err);
1309
2
    if (err) { current_rt->error = err; return value_nil(); }
1310
2
    if (!r) return value_nil();
1311
2
    return value_string_owned(r);
1312
2
}
1313
2
static LatValue native_exec_cmd(LatValue *args, int ac) {
1314
2
    if (ac != 1 || args[0].type != VAL_STR) return value_nil();
1315
2
    char *err = NULL;
1316
2
    LatValue r = process_exec(args[0].as.str_val, &err);
1317
2
    if (err) { current_rt->error = err; return value_nil(); }
1318
2
    return r;
1319
2
}
1320
6
static LatValue native_shell(LatValue *args, int ac) {
1321
6
    if (ac != 1 || args[0].type != VAL_STR) return value_nil();
1322
6
    char *err = NULL;
1323
6
    LatValue r = process_shell(args[0].as.str_val, &err);
1324
6
    if (err) { current_rt->error = err; return value_nil(); }
1325
6
    return r;
1326
6
}
1327
4
static LatValue native_platform(LatValue *args, int ac) {
1328
4
    (void)args; (void)ac; return value_string(process_platform());
1329
4
}
1330
2
static LatValue native_hostname(LatValue *args, int ac) {
1331
2
    (void)args; (void)ac;
1332
2
    char *err = NULL; char *r = process_hostname(&err);
1333
2
    if (err) { current_rt->error = err; return value_nil(); }
1334
2
    if (!r) return value_nil();
1335
2
    return value_string_owned(r);
1336
2
}
1337
2
static LatValue native_pid(LatValue *args, int ac) {
1338
2
    (void)args; (void)ac; return value_int(process_pid());
1339
2
}
1340
1341
/* ── Type/utility natives ── */
1342
1343
0
static LatValue native_to_int(LatValue *args, int ac) {
1344
0
    if (ac != 1) return value_nil();
1345
0
    char *err = NULL;
1346
0
    LatValue r = type_to_int(&args[0], &err);
1347
0
    if (err) { current_rt->error = err; return value_nil(); }
1348
0
    return r;
1349
0
}
1350
0
static LatValue native_to_float(LatValue *args, int ac) {
1351
0
    if (ac != 1) return value_nil();
1352
0
    char *err = NULL;
1353
0
    LatValue r = type_to_float(&args[0], &err);
1354
0
    if (err) { current_rt->error = err; return value_nil(); }
1355
0
    return r;
1356
0
}
1357
2
static LatValue native_struct_name(LatValue *args, int ac) {
1358
2
    if (ac != 1 || args[0].type != VAL_STRUCT) return value_nil();
1359
2
    return value_string(args[0].as.strct.name);
1360
2
}
1361
2
static LatValue native_struct_fields(LatValue *args, int ac) {
1362
2
    if (ac != 1 || args[0].type != VAL_STRUCT) return value_array(NULL, 0);
1363
2
    size_t fc = args[0].as.strct.field_count;
1364
2
    LatValue *elems = malloc((fc > 0 ? fc : 1) * sizeof(LatValue));
1365
6
    for (size_t i = 0; i < fc; i++)
1366
4
        elems[i] = value_string(args[0].as.strct.field_names[i]);
1367
2
    LatValue r = value_array(elems, fc); free(elems);
1368
2
    return r;
1369
2
}
1370
4
static LatValue native_struct_to_map(LatValue *args, int ac) {
1371
4
    if (ac != 1 || args[0].type != VAL_STRUCT) return value_nil();
1372
4
    LatValue map = value_map_new();
1373
12
    for (size_t i = 0; i < args[0].as.strct.field_count; i++) {
1374
8
        LatValue v = value_deep_clone(&args[0].as.strct.field_values[i]);
1375
8
        lat_map_set(map.as.map.map, args[0].as.strct.field_names[i], &v);
1376
8
    }
1377
4
    return map;
1378
4
}
1379
24
static LatValue native_repr(LatValue *args, int ac) {
1380
24
    if (ac != 1) return value_nil();
1381
    /* Check for custom repr closure on structs */
1382
24
    if (args[0].type == VAL_STRUCT) {
1383
16
        for (size_t i = 0; i < args[0].as.strct.field_count; i++) {
1384
14
            if (strcmp(args[0].as.strct.field_names[i], "repr") == 0 &&
1385
14
                args[0].as.strct.field_values[i].type == VAL_CLOSURE) {
1386
4
                LatValue self = value_deep_clone(&args[0]);
1387
4
                LatValue result = current_rt->call_closure(current_rt->active_vm, &args[0].as.strct.field_values[i], &self, 1);
1388
4
                value_free(&self);
1389
4
                if (result.type == VAL_STR) return result;
1390
2
                value_free(&result);
1391
2
                break; /* fall through to default */
1392
4
            }
1393
14
        }
1394
6
    }
1395
22
    return value_string_owned(value_repr(&args[0]));
1396
24
}
1397
52
static LatValue native_format(LatValue *args, int ac) {
1398
52
    if (ac < 1 || args[0].type != VAL_STR) {
1399
2
        current_rt->error = strdup("format: expected (fmt: Str, ...)");
1400
2
        return value_nil();
1401
2
    }
1402
50
    char *err = NULL;
1403
50
    char *r = format_string(args[0].as.str_val, args + 1, (size_t)(ac - 1), &err);
1404
50
    if (err) { current_rt->error = err; return value_nil(); }
1405
48
    return value_string_owned(r);
1406
50
}
1407
14
static LatValue native_range(LatValue *args, int ac) {
1408
14
    if (ac < 2 || ac > 3 || args[0].type != VAL_INT || args[1].type != VAL_INT) {
1409
0
        current_rt->error = strdup("range() expects (start: Int, end: Int, step?: Int)");
1410
0
        return value_array(NULL, 0);
1411
0
    }
1412
14
    int64_t rstart = args[0].as.int_val, rend = args[1].as.int_val;
1413
14
    int64_t rstep = (rstart <= rend) ? 1 : -1;
1414
14
    if (ac == 3) {
1415
8
        if (args[2].type != VAL_INT) { current_rt->error = strdup("range() step must be Int"); return value_array(NULL, 0); }
1416
8
        rstep = args[2].as.int_val;
1417
8
    }
1418
14
    if (rstep == 0) { current_rt->error = strdup("range() step cannot be zero"); return value_array(NULL, 0); }
1419
12
    size_t rcount = 0;
1420
12
    if (rstep > 0 && rstart < rend)
1421
4
        rcount = (size_t)((rend - rstart + rstep - 1) / rstep);
1422
8
    else if (rstep < 0 && rstart > rend)
1423
4
        rcount = (size_t)((rstart - rend + (-rstep) - 1) / (-rstep));
1424
12
    LatValue *relems = malloc((rcount > 0 ? rcount : 1) * sizeof(LatValue));
1425
12
    int64_t rcur = rstart;
1426
50
    for (size_t i = 0; i < rcount; i++) { relems[i] = value_int(rcur); rcur += rstep; }
1427
12
    LatValue r = value_array(relems, rcount); free(relems);
1428
12
    return r;
1429
14
}
1430
4
static LatValue native_print_raw(LatValue *args, int ac) {
1431
8
    for (int i = 0; i < ac; i++) {
1432
4
        if (i > 0) printf(" ");
1433
4
        if (args[i].type == VAL_STR) printf("%s", args[i].as.str_val);
1434
0
        else { char *s = value_display(&args[i]); printf("%s", s); free(s); }
1435
4
    }
1436
4
    fflush(stdout); return value_unit();
1437
4
}
1438
2
static LatValue native_eprint(LatValue *args, int ac) {
1439
4
    for (int i = 0; i < ac; i++) {
1440
2
        if (i > 0) fprintf(stderr, " ");
1441
2
        if (args[i].type == VAL_STR) fprintf(stderr, "%s", args[i].as.str_val);
1442
0
        else { char *s = value_display(&args[i]); fprintf(stderr, "%s", s); free(s); }
1443
2
    }
1444
2
    fprintf(stderr, "\n"); return value_unit();
1445
2
}
1446
4
static LatValue native_identity(LatValue *args, int ac) {
1447
4
    if (ac != 1) return value_nil();
1448
4
    return value_deep_clone(&args[0]);
1449
4
}
1450
4
static LatValue native_debug_assert(LatValue *args, int ac) {
1451
4
    if (ac < 1) return value_unit();
1452
4
    bool ok = (args[0].type == VAL_BOOL) ? args[0].as.bool_val :
1453
4
              (args[0].type == VAL_INT) ? args[0].as.int_val != 0 :
1454
0
              args[0].type != VAL_NIL;
1455
4
    if (!ok) {
1456
2
        const char *msg = (ac >= 2 && args[1].type == VAL_STR) ? args[1].as.str_val : "debug assertion failed";
1457
2
        if (current_rt) {
1458
2
            char *err = NULL;
1459
2
            (void)asprintf(&err, "debug assertion failed: %s", msg);
1460
2
            current_rt->error = err;
1461
2
        } else {
1462
0
            fprintf(stderr, "debug assertion failed: %s\n", msg);
1463
0
            exit(1);
1464
0
        }
1465
2
    }
1466
4
    return value_unit();
1467
4
}
1468
1469
2
static LatValue native_panic(LatValue *args, int ac) {
1470
2
    const char *msg = (ac >= 1 && args[0].type == VAL_STR) ? args[0].as.str_val : "panic";
1471
2
    current_rt->error = strdup(msg);
1472
2
    return value_unit();
1473
2
}
1474
1475
/* Native require: load and execute a file in the global scope (no isolation) */
1476
9
static LatValue native_require(LatValue *args, int arg_count) {
1477
9
    if (arg_count < 1 || args[0].type != VAL_STR) {
1478
0
        current_rt->error = strdup("require: expected a string argument");
1479
0
        return value_bool(false);
1480
0
    }
1481
9
    const char *raw_path = args[0].as.str_val;
1482
1483
    /* Resolve file path: append .lat if not present */
1484
9
    size_t plen = strlen(raw_path);
1485
9
    char *file_path;
1486
9
    if (plen >= 4 && strcmp(raw_path + plen - 4, ".lat") == 0) {
1487
2
        file_path = strdup(raw_path);
1488
7
    } else {
1489
7
        file_path = malloc(plen + 5);
1490
7
        memcpy(file_path, raw_path, plen);
1491
7
        memcpy(file_path + plen, ".lat", 5);
1492
7
    }
1493
1494
    /* Resolve to absolute path: try CWD first, then script_dir */
1495
9
    char resolved[PATH_MAX];
1496
9
    bool found = (realpath(file_path, resolved) != NULL);
1497
9
    if (!found && current_rt->script_dir && file_path[0] != '/') {
1498
0
        char script_rel[PATH_MAX];
1499
0
        snprintf(script_rel, sizeof(script_rel), "%s/%s",
1500
0
                 current_rt->script_dir, file_path);
1501
0
        found = (realpath(script_rel, resolved) != NULL);
1502
0
    }
1503
9
    if (!found) {
1504
1
        (void)asprintf(&current_rt->error, "require: cannot find '%s'", raw_path);
1505
1
        free(file_path);
1506
1
        return value_bool(false);
1507
1
    }
1508
8
    free(file_path);
1509
1510
    /* Dedup: skip if already required */
1511
8
    if (lat_map_get(&current_rt->required_files, resolved)) {
1512
2
        return value_bool(true);
1513
2
    }
1514
1515
    /* Mark as loaded before execution (prevents circular requires) */
1516
8
    bool loaded = true;
1517
6
    lat_map_set(&current_rt->required_files, resolved, &loaded);
1518
1519
    /* Read the file */
1520
6
    char *source = builtin_read_file(resolved);
1521
6
    if (!source) {
1522
0
        (void)asprintf(&current_rt->error, "require: cannot read '%s'", resolved);
1523
0
        return value_bool(false);
1524
0
    }
1525
1526
    /* Lex */
1527
6
    Lexer req_lex = lexer_new(source);
1528
6
    char *lex_err = NULL;
1529
6
    LatVec req_toks = lexer_tokenize(&req_lex, &lex_err);
1530
6
    free(source);
1531
6
    if (lex_err) {
1532
0
        (void)asprintf(&current_rt->error, "require '%s': %s", resolved, lex_err);
1533
0
        free(lex_err);
1534
0
        lat_vec_free(&req_toks);
1535
0
        return value_bool(false);
1536
0
    }
1537
1538
    /* Parse */
1539
6
    Parser req_parser = parser_new(&req_toks);
1540
6
    char *parse_err = NULL;
1541
6
    Program req_prog = parser_parse(&req_parser, &parse_err);
1542
6
    if (parse_err) {
1543
0
        (void)asprintf(&current_rt->error, "require '%s': %s", resolved, parse_err);
1544
0
        free(parse_err);
1545
0
        program_free(&req_prog);
1546
0
        for (size_t ti = 0; ti < req_toks.len; ti++)
1547
0
            token_free(lat_vec_get(&req_toks, ti));
1548
0
        lat_vec_free(&req_toks);
1549
0
        return value_bool(false);
1550
0
    }
1551
1552
    /* Dispatch to the active VM backend */
1553
6
    if (current_rt->backend == RT_BACKEND_REG_VM) {
1554
0
        char *comp_err = NULL;
1555
0
        RegChunk *rchunk = reg_compile_module(&req_prog, &comp_err);
1556
0
        program_free(&req_prog);
1557
0
        for (size_t ti = 0; ti < req_toks.len; ti++)
1558
0
            token_free(lat_vec_get(&req_toks, ti));
1559
0
        lat_vec_free(&req_toks);
1560
0
        if (!rchunk) {
1561
0
            (void)asprintf(&current_rt->error, "require '%s': %s", resolved,
1562
0
                    comp_err ? comp_err : "compile error");
1563
0
            free(comp_err);
1564
0
            return value_bool(false);
1565
0
        }
1566
0
        RegVM *rvm = (RegVM *)current_rt->active_vm;
1567
0
        regvm_track_chunk(rvm, rchunk);
1568
0
        LatValue req_result;
1569
0
        RegVMResult rr = regvm_run(rvm, rchunk, &req_result);
1570
0
        if (rr != REGVM_OK) {
1571
0
            (void)asprintf(&current_rt->error, "require '%s': runtime error: %s", resolved,
1572
0
                    rvm->error ? rvm->error : "(unknown)");
1573
0
            return value_bool(false);
1574
0
        }
1575
0
        value_free(&req_result);
1576
6
    } else {
1577
6
        char *comp_err = NULL;
1578
6
        Chunk *req_chunk = stack_compile_module(&req_prog, &comp_err);
1579
6
        program_free(&req_prog);
1580
107
        for (size_t ti = 0; ti < req_toks.len; ti++)
1581
101
            token_free(lat_vec_get(&req_toks, ti));
1582
6
        lat_vec_free(&req_toks);
1583
6
        if (!req_chunk) {
1584
0
            (void)asprintf(&current_rt->error, "require '%s': %s", resolved,
1585
0
                    comp_err ? comp_err : "compile error");
1586
0
            free(comp_err);
1587
0
            return value_bool(false);
1588
0
        }
1589
6
        StackVM *vm = (StackVM *)current_rt->active_vm;
1590
        /* Track the chunk for proper lifetime management */
1591
6
        if (vm->fn_chunk_count >= vm->fn_chunk_cap) {
1592
5
            vm->fn_chunk_cap = vm->fn_chunk_cap ? vm->fn_chunk_cap * 2 : 8;
1593
5
            vm->fn_chunks = realloc(vm->fn_chunks,
1594
5
                                    vm->fn_chunk_cap * sizeof(Chunk *));
1595
5
        }
1596
6
        vm->fn_chunks[vm->fn_chunk_count++] = req_chunk;
1597
6
        LatValue req_result;
1598
6
        StackVMResult req_r = stackvm_run(vm, req_chunk, &req_result);
1599
6
        if (req_r != STACKVM_OK) {
1600
0
            (void)asprintf(&current_rt->error, "require '%s': runtime error: %s", resolved,
1601
0
                    vm->error ? vm->error : "(unknown)");
1602
0
            return value_bool(false);
1603
0
        }
1604
6
        value_free(&req_result);
1605
6
    }
1606
1607
6
    return value_bool(true);
1608
6
}
1609
1610
/* Native require_ext: load a native extension (.dylib/.so) and return a Map */
1611
36
static LatValue native_require_ext(LatValue *args, int arg_count) {
1612
36
    if (arg_count < 1 || args[0].type != VAL_STR) {
1613
4
        current_rt->error = strdup("require_ext: expected a string argument");
1614
4
        return value_nil();
1615
4
    }
1616
32
    const char *ext_name = args[0].as.str_val;
1617
1618
    /* Check cache */
1619
32
    LatValue *cached = (LatValue *)lat_map_get(&current_rt->loaded_extensions, ext_name);
1620
32
    if (cached) {
1621
0
        return value_deep_clone(cached);
1622
0
    }
1623
1624
    /* Load extension */
1625
32
    char *ext_err = NULL;
1626
32
    LatValue ext_map = ext_load(NULL, ext_name, &ext_err);
1627
32
    if (ext_err) {
1628
2
        (void)asprintf(&current_rt->error, "require_ext: %s", ext_err);
1629
2
        free(ext_err);
1630
2
        return value_nil();
1631
2
    }
1632
1633
    /* Mark extension closures with VM_EXT_MARKER so the StackVM dispatches them
1634
     * through ext_call_native() instead of treating native_fn as a Chunk*. */
1635
30
    if (ext_map.type == VAL_MAP && ext_map.as.map.map) {
1636
510
        for (size_t i = 0; i < ext_map.as.map.map->cap; i++) {
1637
480
            if (ext_map.as.map.map->entries[i].state != MAP_OCCUPIED) continue;
1638
180
            LatValue *v = (LatValue *)ext_map.as.map.map->entries[i].value;
1639
180
            if (v->type == VAL_CLOSURE && v->as.closure.native_fn && !v->as.closure.body) {
1640
180
                v->as.closure.default_values = VM_EXT_MARKER;
1641
180
            }
1642
180
        }
1643
30
    }
1644
1645
    /* Cache the extension */
1646
30
    LatValue cached_copy = value_deep_clone(&ext_map);
1647
30
    lat_map_set(&current_rt->loaded_extensions, ext_name, &cached_copy);
1648
1649
30
    return ext_map;
1650
32
}
1651
1652
/* ── Missing native builtins ── */
1653
1654
2
static LatValue native_args(LatValue *args, int arg_count) {
1655
2
    (void)args; (void)arg_count;
1656
2
    int ac = current_rt->prog_argc;
1657
2
    char **av = current_rt->prog_argv;
1658
2
    LatValue *elems = NULL;
1659
2
    if (ac > 0) {
1660
0
        elems = malloc((size_t)ac * sizeof(LatValue));
1661
0
        for (int i = 0; i < ac; i++)
1662
0
            elems[i] = value_string(av[i]);
1663
0
    }
1664
2
    LatValue arr = value_array(elems, (size_t)ac);
1665
2
    free(elems);
1666
2
    return arr;
1667
2
}
1668
1669
6
static LatValue native_struct_from_map(LatValue *args, int arg_count) {
1670
6
    if (arg_count < 2 || args[0].type != VAL_STR || args[1].type != VAL_MAP) {
1671
0
        current_rt->error = strdup("struct_from_map: expected (name: Str, fields: Map)");
1672
0
        return value_nil();
1673
0
    }
1674
6
    const char *sname = args[0].as.str_val;
1675
    /* Look up struct field names from env metadata */
1676
6
    char meta_key[256];
1677
6
    snprintf(meta_key, sizeof(meta_key), "__struct_%s", sname);
1678
6
    LatValue meta;
1679
6
    if (!env_get(current_rt->env, meta_key, &meta)) {
1680
2
        (void)asprintf(&current_rt->error, "struct_from_map: unknown struct '%s'", sname);
1681
2
        return value_nil();
1682
2
    }
1683
4
    if (meta.type != VAL_ARRAY) { value_free(&meta); return value_nil(); }
1684
4
    size_t fc = meta.as.array.len;
1685
4
    char **names = malloc(fc * sizeof(char *));
1686
4
    LatValue *vals = malloc(fc * sizeof(LatValue));
1687
12
    for (size_t j = 0; j < fc; j++) {
1688
8
        names[j] = meta.as.array.elems[j].as.str_val;
1689
8
        LatValue *found = (LatValue *)lat_map_get(args[1].as.map.map, names[j]);
1690
8
        vals[j] = found ? value_deep_clone(found) : value_nil();
1691
8
    }
1692
4
    LatValue st = value_struct(sname, names, vals, fc);
1693
4
    free(names);
1694
4
    free(vals);
1695
4
    value_free(&meta);
1696
4
    return st;
1697
4
}
1698
1699
4
static LatValue native_url_encode(LatValue *args, int arg_count) {
1700
4
    if (arg_count < 1 || args[0].type != VAL_STR) return value_nil();
1701
4
    const char *src = args[0].as.str_val;
1702
4
    size_t slen = strlen(src);
1703
4
    size_t cap = slen * 3 + 1;
1704
4
    char *out = malloc(cap);
1705
4
    size_t j = 0;
1706
52
    for (size_t i = 0; i < slen; i++) {
1707
48
        unsigned char c = (unsigned char)src[i];
1708
48
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
1709
48
            (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') {
1710
40
            out[j++] = (char)c;
1711
40
        } else {
1712
8
            snprintf(out + j, 4, "%%%02X", c);
1713
8
            j += 3;
1714
8
        }
1715
48
    }
1716
4
    out[j] = '\0';
1717
4
    return value_string_owned(out);
1718
4
}
1719
1720
4
static LatValue native_url_decode(LatValue *args, int arg_count) {
1721
4
    if (arg_count < 1 || args[0].type != VAL_STR) return value_nil();
1722
4
    const char *src = args[0].as.str_val;
1723
4
    size_t slen = strlen(src);
1724
4
    char *out = malloc(slen + 1);
1725
4
    size_t j = 0;
1726
40
    for (size_t i = 0; i < slen; i++) {
1727
36
        if (src[i] == '%' && i + 2 < slen) {
1728
2
            char hex[3] = { src[i+1], src[i+2], '\0' };
1729
2
            char *end = NULL;
1730
2
            unsigned long val = strtoul(hex, &end, 16);
1731
2
            if (end == hex + 2) {
1732
2
                out[j++] = (char)val;
1733
2
                i += 2;
1734
2
            } else {
1735
0
                out[j++] = src[i];
1736
0
            }
1737
34
        } else if (src[i] == '+') {
1738
2
            out[j++] = ' ';
1739
32
        } else {
1740
32
            out[j++] = src[i];
1741
32
        }
1742
36
    }
1743
4
    out[j] = '\0';
1744
4
    return value_string_owned(out);
1745
4
}
1746
1747
6
static LatValue native_csv_parse(LatValue *args, int arg_count) {
1748
6
    if (arg_count < 1 || args[0].type != VAL_STR) return value_nil();
1749
6
    const char *input = args[0].as.str_val;
1750
6
    size_t pos = 0, input_len = strlen(input);
1751
6
    size_t rows_cap = 8, rows_len = 0;
1752
6
    LatValue *rows = malloc(rows_cap * sizeof(LatValue));
1753
1754
18
    while (pos < input_len) {
1755
12
        size_t fields_cap = 8, fields_len = 0;
1756
12
        LatValue *fields = malloc(fields_cap * sizeof(LatValue));
1757
28
        for (;;) {
1758
28
            size_t field_cap = 64, field_len = 0;
1759
28
            char *field = malloc(field_cap);
1760
28
            if (pos < input_len && input[pos] == '"') {
1761
4
                pos++;
1762
34
                for (;;) {
1763
34
                    if (pos >= input_len) break;
1764
34
                    if (input[pos] == '"') {
1765
4
                        if (pos + 1 < input_len && input[pos + 1] == '"') {
1766
0
                            if (field_len + 1 >= field_cap) { field_cap *= 2; field = realloc(field, field_cap); }
1767
0
                            field[field_len++] = '"'; pos += 2;
1768
4
                        } else { pos++; break; }
1769
30
                    } else {
1770
30
                        if (field_len + 1 >= field_cap) { field_cap *= 2; field = realloc(field, field_cap); }
1771
30
                        field[field_len++] = input[pos++];
1772
30
                    }
1773
34
                }
1774
24
            } else {
1775
60
                while (pos < input_len && input[pos] != ',' && input[pos] != '\n' && input[pos] != '\r') {
1776
36
                    if (field_len + 1 >= field_cap) { field_cap *= 2; field = realloc(field, field_cap); }
1777
36
                    field[field_len++] = input[pos++];
1778
36
                }
1779
24
            }
1780
28
            field[field_len] = '\0';
1781
28
            if (fields_len >= fields_cap) { fields_cap *= 2; fields = realloc(fields, fields_cap * sizeof(LatValue)); }
1782
28
            fields[fields_len++] = value_string_owned(field);
1783
28
            if (pos < input_len && input[pos] == ',') { pos++; } else break;
1784
28
        }
1785
12
        if (pos < input_len && input[pos] == '\r') pos++;
1786
12
        if (pos < input_len && input[pos] == '\n') pos++;
1787
12
        LatValue row = value_array(fields, fields_len);
1788
12
        free(fields);
1789
12
        if (rows_len >= rows_cap) { rows_cap *= 2; rows = realloc(rows, rows_cap * sizeof(LatValue)); }
1790
12
        rows[rows_len++] = row;
1791
12
    }
1792
6
    LatValue result = value_array(rows, rows_len);
1793
6
    free(rows);
1794
6
    return result;
1795
6
}
1796
1797
4
static LatValue native_csv_stringify(LatValue *args, int arg_count) {
1798
4
    if (arg_count < 1 || args[0].type != VAL_ARRAY) return value_nil();
1799
4
    LatValue *data = &args[0];
1800
4
    size_t out_cap = 256, out_len = 0;
1801
4
    char *out = malloc(out_cap);
1802
12
    for (size_t r = 0; r < data->as.array.len; r++) {
1803
8
        LatValue *row = &data->as.array.elems[r];
1804
8
        if (row->type != VAL_ARRAY) { free(out); return value_nil(); }
1805
24
        for (size_t c = 0; c < row->as.array.len; c++) {
1806
16
            if (c > 0) {
1807
8
                if (out_len + 1 >= out_cap) { out_cap *= 2; out = realloc(out, out_cap); }
1808
8
                out[out_len++] = ',';
1809
8
            }
1810
16
            char *field_str = value_display(&row->as.array.elems[c]);
1811
16
            size_t flen = strlen(field_str);
1812
16
            bool needs_quote = false;
1813
52
            for (size_t k = 0; k < flen; k++) {
1814
36
                if (field_str[k] == ',' || field_str[k] == '"' || field_str[k] == '\n' || field_str[k] == '\r') {
1815
0
                    needs_quote = true; break;
1816
0
                }
1817
36
            }
1818
16
            if (needs_quote) {
1819
0
                size_t extra = 0;
1820
0
                for (size_t k = 0; k < flen; k++) { if (field_str[k] == '"') extra++; }
1821
0
                size_t needed = flen + extra + 2;
1822
0
                while (out_len + needed >= out_cap) { out_cap *= 2; out = realloc(out, out_cap); }
1823
0
                out[out_len++] = '"';
1824
0
                for (size_t k = 0; k < flen; k++) {
1825
0
                    if (field_str[k] == '"') out[out_len++] = '"';
1826
0
                    out[out_len++] = field_str[k];
1827
0
                }
1828
0
                out[out_len++] = '"';
1829
16
            } else {
1830
16
                while (out_len + flen >= out_cap) { out_cap *= 2; out = realloc(out, out_cap); }
1831
16
                memcpy(out + out_len, field_str, flen);
1832
16
                out_len += flen;
1833
16
            }
1834
16
            free(field_str);
1835
16
        }
1836
8
        if (out_len + 1 >= out_cap) { out_cap *= 2; out = realloc(out, out_cap); }
1837
8
        out[out_len++] = '\n';
1838
8
    }
1839
4
    out[out_len] = '\0';
1840
4
    return value_string_owned(out);
1841
4
}
1842
1843
8
static LatValue native_is_complete(LatValue *args, int arg_count) {
1844
8
    if (arg_count < 1 || args[0].type != VAL_STR) return value_bool(false);
1845
8
    const char *source = args[0].as.str_val;
1846
8
    Lexer lex = lexer_new(source);
1847
8
    char *lex_err = NULL;
1848
8
    LatVec toks = lexer_tokenize(&lex, &lex_err);
1849
8
    if (lex_err) { free(lex_err); lat_vec_free(&toks); return value_bool(false); }
1850
8
    int depth = 0;
1851
46
    for (size_t j = 0; j < toks.len; j++) {
1852
38
        Token *t = lat_vec_get(&toks, j);
1853
38
        switch (t->type) {
1854
8
            case TOK_LBRACE: case TOK_LPAREN: case TOK_LBRACKET: depth++; break;
1855
6
            case TOK_RBRACE: case TOK_RPAREN: case TOK_RBRACKET: depth--; break;
1856
24
            default: break;
1857
38
        }
1858
38
    }
1859
46
    for (size_t j = 0; j < toks.len; j++) token_free(lat_vec_get(&toks, j));
1860
8
    lat_vec_free(&toks);
1861
8
    return value_bool(depth <= 0);
1862
8
}
1863
1864
0
static LatValue native_float_to_bits(LatValue *args, int arg_count) {
1865
0
    if (arg_count != 1 || args[0].type != VAL_FLOAT) return value_nil();
1866
0
    double d = args[0].as.float_val;
1867
0
    uint64_t bits; memcpy(&bits, &d, 8);
1868
0
    return value_int((int64_t)bits);
1869
0
}
1870
1871
0
static LatValue native_bits_to_float(LatValue *args, int arg_count) {
1872
0
    if (arg_count != 1 || args[0].type != VAL_INT) return value_nil();
1873
0
    uint64_t bits = (uint64_t)args[0].as.int_val;
1874
0
    double d; memcpy(&d, &bits, 8);
1875
0
    return value_float(d);
1876
0
}
1877
1878
2
static LatValue native_tokenize(LatValue *args, int arg_count) {
1879
2
    if (arg_count < 1 || args[0].type != VAL_STR) return value_nil();
1880
2
    const char *source = args[0].as.str_val;
1881
2
    Lexer lex = lexer_new(source);
1882
2
    char *lex_err = NULL;
1883
2
    LatVec toks = lexer_tokenize(&lex, &lex_err);
1884
2
    if (lex_err) { free(lex_err); lat_vec_free(&toks); return value_nil(); }
1885
2
    size_t tok_count = toks.len > 0 ? toks.len - 1 : 0;
1886
2
    LatValue *elems = malloc((tok_count > 0 ? tok_count : 1) * sizeof(LatValue));
1887
10
    for (size_t j = 0; j < tok_count; j++) {
1888
8
        Token *t = lat_vec_get(&toks, j);
1889
8
        const char *type_str = token_type_name(t->type);
1890
8
        char *text;
1891
8
        if (t->type == TOK_IDENT || t->type == TOK_STRING_LIT || t->type == TOK_MODE_DIRECTIVE ||
1892
8
            t->type == TOK_INTERP_START || t->type == TOK_INTERP_MID || t->type == TOK_INTERP_END) {
1893
2
            text = strdup(t->as.str_val);
1894
6
        } else if (t->type == TOK_INT_LIT) {
1895
2
            (void)asprintf(&text, "%lld", (long long)t->as.int_val);
1896
4
        } else if (t->type == TOK_FLOAT_LIT) {
1897
0
            (void)asprintf(&text, "%g", t->as.float_val);
1898
4
        } else {
1899
4
            text = strdup(token_type_name(t->type));
1900
4
        }
1901
8
        char *fnames[3] = { "type", "text", "line" };
1902
8
        LatValue fvals[3];
1903
8
        fvals[0] = value_string(type_str);
1904
8
        fvals[1] = value_string_owned(text);
1905
8
        fvals[2] = value_int((int64_t)t->line);
1906
8
        elems[j] = value_struct("Token", fnames, fvals, 3);
1907
8
    }
1908
12
    for (size_t j = 0; j < toks.len; j++) token_free(lat_vec_get(&toks, j));
1909
2
    lat_vec_free(&toks);
1910
2
    LatValue arr = value_array(elems, tok_count);
1911
2
    free(elems);
1912
2
    return arr;
1913
2
}
1914
1915
26
static LatValue native_lat_eval(LatValue *args, int arg_count) {
1916
26
    if (arg_count < 1 || args[0].type != VAL_STR) return value_nil();
1917
26
    const char *source = args[0].as.str_val;
1918
26
    Lexer lex = lexer_new(source);
1919
26
    char *lex_err = NULL;
1920
26
    LatVec toks = lexer_tokenize(&lex, &lex_err);
1921
26
    if (lex_err) {
1922
0
        (void)asprintf(&current_rt->error, "lat_eval: %s", lex_err);
1923
0
        free(lex_err); lat_vec_free(&toks);
1924
0
        return value_nil();
1925
0
    }
1926
26
    Parser parser = parser_new(&toks);
1927
26
    char *parse_err = NULL;
1928
26
    Program prog = parser_parse(&parser, &parse_err);
1929
26
    if (parse_err) {
1930
0
        (void)asprintf(&current_rt->error, "lat_eval: %s", parse_err);
1931
0
        free(parse_err); program_free(&prog);
1932
0
        for (size_t j = 0; j < toks.len; j++) token_free(lat_vec_get(&toks, j));
1933
0
        lat_vec_free(&toks);
1934
0
        return value_nil();
1935
0
    }
1936
1937
26
    if (current_rt->backend == RT_BACKEND_REG_VM) {
1938
13
        char *comp_err = NULL;
1939
13
        RegChunk *rchunk = reg_compile_repl(&prog, &comp_err);
1940
13
        program_free(&prog);
1941
104
        for (size_t j = 0; j < toks.len; j++) token_free(lat_vec_get(&toks, j));
1942
13
        lat_vec_free(&toks);
1943
13
        if (!rchunk) {
1944
0
            (void)asprintf(&current_rt->error, "lat_eval: %s", comp_err ? comp_err : "compile error");
1945
0
            free(comp_err);
1946
0
            return value_nil();
1947
0
        }
1948
13
        RegVM *rvm = (RegVM *)current_rt->active_vm;
1949
13
        regvm_track_chunk(rvm, rchunk);
1950
13
        LatValue result;
1951
13
        RegVMResult rr = regvm_run(rvm, rchunk, &result);
1952
13
        if (rr != REGVM_OK) return value_nil();
1953
13
        return result;
1954
13
    }
1955
1956
13
    char *comp_err = NULL;
1957
13
    Chunk *chunk = stack_compile_repl(&prog, &comp_err);
1958
13
    program_free(&prog);
1959
104
    for (size_t j = 0; j < toks.len; j++) token_free(lat_vec_get(&toks, j));
1960
13
    lat_vec_free(&toks);
1961
13
    if (!chunk) {
1962
0
        (void)asprintf(&current_rt->error, "lat_eval: %s", comp_err ? comp_err : "compile error");
1963
0
        free(comp_err);
1964
0
        return value_nil();
1965
0
    }
1966
13
    StackVM *vm = (StackVM *)current_rt->active_vm;
1967
13
    if (vm->fn_chunk_count >= vm->fn_chunk_cap) {
1968
6
        vm->fn_chunk_cap = vm->fn_chunk_cap ? vm->fn_chunk_cap * 2 : 8;
1969
6
        vm->fn_chunks = realloc(vm->fn_chunks, vm->fn_chunk_cap * sizeof(Chunk *));
1970
6
    }
1971
13
    vm->fn_chunks[vm->fn_chunk_count++] = chunk;
1972
13
    LatValue result;
1973
13
    StackVMResult r = stackvm_run(vm, chunk, &result);
1974
13
    if (r != STACKVM_OK) return value_nil();
1975
13
    return result;
1976
13
}
1977
1978
4
static LatValue native_pipe(LatValue *args, int arg_count) {
1979
4
    if (arg_count < 2) return value_nil();
1980
4
    (void)0; /* uses dispatch */
1981
4
    LatValue current = value_deep_clone(&args[0]);
1982
14
    for (int i = 1; i < arg_count; i++) {
1983
10
        if (args[i].type != VAL_CLOSURE) { value_free(&current); return value_nil(); }
1984
10
        LatValue result = current_rt->call_closure(current_rt->active_vm, &args[i], &current, 1);
1985
10
        value_free(&current);
1986
10
        current = result;
1987
10
    }
1988
4
    return current;
1989
4
}
1990
1991
2
static LatValue native_compose(LatValue *args, int arg_count) {
1992
2
    if (arg_count < 2 || args[0].type != VAL_CLOSURE || args[1].type != VAL_CLOSURE)
1993
0
        return value_nil();
1994
2
    StackVM *vm = (StackVM *)current_rt->active_vm;
1995
1996
    /* Store f and g in env with unique names so the composed closure can find them */
1997
2
    static int compose_counter = 0;
1998
2
    char f_name[64], g_name[64];
1999
2
    snprintf(f_name, sizeof(f_name), "__compose_f_%d", compose_counter);
2000
2
    snprintf(g_name, sizeof(g_name), "__compose_g_%d", compose_counter);
2001
2
    compose_counter++;
2002
2
    env_define(current_rt->env, f_name, value_deep_clone(&args[0]));
2003
2
    env_define(current_rt->env, g_name, value_deep_clone(&args[1]));
2004
2005
    /* Build chunk: GET_GLOBAL f, GET_GLOBAL g, GET_LOCAL 1(x), CALL 1, CALL 1, RETURN */
2006
2
    Chunk *chunk = chunk_new();
2007
2
    size_t f_idx = chunk_add_constant(chunk, value_string(f_name));
2008
2
    size_t g_idx = chunk_add_constant(chunk, value_string(g_name));
2009
2
    chunk_write(chunk, OP_GET_GLOBAL, 0);
2010
2
    chunk_write(chunk, (uint8_t)f_idx, 0);
2011
2
    chunk_write(chunk, OP_GET_GLOBAL, 0);
2012
2
    chunk_write(chunk, (uint8_t)g_idx, 0);
2013
2
    chunk_write(chunk, OP_GET_LOCAL, 0);
2014
2
    chunk_write(chunk, 1, 0);
2015
2
    chunk_write(chunk, OP_CALL, 0);
2016
2
    chunk_write(chunk, 1, 0);
2017
2
    chunk_write(chunk, OP_CALL, 0);
2018
2
    chunk_write(chunk, 1, 0);
2019
2
    chunk_write(chunk, OP_RETURN, 0);
2020
2021
2
    if (vm->fn_chunk_count >= vm->fn_chunk_cap) {
2022
1
        vm->fn_chunk_cap = vm->fn_chunk_cap ? vm->fn_chunk_cap * 2 : 8;
2023
1
        vm->fn_chunks = realloc(vm->fn_chunks, vm->fn_chunk_cap * sizeof(Chunk *));
2024
1
    }
2025
2
    vm->fn_chunks[vm->fn_chunk_count++] = chunk;
2026
2027
    /* Build a compiled closure with 1 parameter */
2028
2
    char **params = malloc(sizeof(char *));
2029
2
    params[0] = strdup("x");
2030
2
    LatValue closure = value_closure(params, 1, NULL, NULL, NULL, false);
2031
2
    closure.as.closure.native_fn = (void *)chunk;
2032
2
    free(params);
2033
2
    return closure;
2034
2
}
2035
2036
/* ── Bytecode compilation/loading builtins ── */
2037
2038
0
static char *latc_read_file(const char *path) {
2039
0
    FILE *f = fopen(path, "r");
2040
0
    if (!f) return NULL;
2041
0
    fseek(f, 0, SEEK_END);
2042
0
    long len = ftell(f);
2043
0
    fseek(f, 0, SEEK_SET);
2044
0
    if (len < 0) { fclose(f); return NULL; }
2045
0
    char *buf = malloc((size_t)len + 1);
2046
0
    if (!buf) { fclose(f); return NULL; }
2047
0
    size_t n = fread(buf, 1, (size_t)len, f);
2048
0
    buf[n] = '\0';
2049
0
    fclose(f);
2050
0
    return buf;
2051
0
}
2052
2053
0
static LatValue native_compile_file(LatValue *args, int ac) {
2054
0
    if (ac != 1 || args[0].type != VAL_STR) return value_nil();
2055
0
    const char *path = args[0].as.str_val;
2056
2057
0
    char *source = latc_read_file(path);
2058
0
    if (!source) return value_nil();
2059
2060
0
    Lexer lex = lexer_new(source);
2061
0
    char *lex_err = NULL;
2062
0
    LatVec tokens = lexer_tokenize(&lex, &lex_err);
2063
0
    if (lex_err) {
2064
0
        free(lex_err);
2065
0
        lat_vec_free(&tokens);
2066
0
        free(source);
2067
0
        return value_nil();
2068
0
    }
2069
2070
0
    Parser parser = parser_new(&tokens);
2071
0
    char *parse_err = NULL;
2072
0
    Program prog = parser_parse(&parser, &parse_err);
2073
0
    if (parse_err) {
2074
0
        free(parse_err);
2075
0
        program_free(&prog);
2076
0
        for (size_t i = 0; i < tokens.len; i++)
2077
0
            token_free(lat_vec_get(&tokens, i));
2078
0
        lat_vec_free(&tokens);
2079
0
        free(source);
2080
0
        return value_nil();
2081
0
    }
2082
2083
0
    char *comp_err = NULL;
2084
0
    Chunk *chunk = stack_compile(&prog, &comp_err);
2085
0
    program_free(&prog);
2086
0
    for (size_t i = 0; i < tokens.len; i++)
2087
0
        token_free(lat_vec_get(&tokens, i));
2088
0
    lat_vec_free(&tokens);
2089
0
    free(source);
2090
2091
0
    if (!chunk) {
2092
0
        free(comp_err);
2093
0
        return value_nil();
2094
0
    }
2095
2096
0
    size_t buf_len;
2097
0
    uint8_t *buf = chunk_serialize(chunk, &buf_len);
2098
0
    chunk_free(chunk);
2099
2100
0
    LatValue result = value_buffer(buf, buf_len);
2101
0
    free(buf);
2102
0
    return result;
2103
0
}
2104
2105
0
static LatValue native_load_bytecode(LatValue *args, int ac) {
2106
0
    if (ac != 1 || args[0].type != VAL_STR) return value_nil();
2107
0
    const char *path = args[0].as.str_val;
2108
2109
0
    char *err = NULL;
2110
0
    Chunk *chunk = chunk_load(path, &err);
2111
0
    if (!chunk) {
2112
0
        free(err);
2113
0
        return value_nil();
2114
0
    }
2115
2116
0
    if (!current_rt->active_vm) {
2117
0
        chunk_free(chunk);
2118
0
        return value_nil();
2119
0
    }
2120
2121
    /* Stack-VM bytecode (.latc) can only run on the stack VM */
2122
0
    StackVM *vm = (StackVM *)current_rt->active_vm;
2123
0
    LatValue result;
2124
0
    StackVMResult res = stackvm_run(vm, chunk, &result);
2125
0
    chunk_free(chunk);
2126
2127
0
    if (res != STACKVM_OK) {
2128
0
        free(vm->error);
2129
0
        vm->error = NULL;
2130
0
        return value_nil();
2131
0
    }
2132
0
    return result;
2133
0
}
2134
2135
/* ── Built-in module helpers ── */
2136
2137
546
static void mod_set_native(LatValue *mod, const char *name, VMNativeFn fn) {
2138
546
    LatValue v;
2139
546
    v.type = VAL_CLOSURE;
2140
546
    v.phase = VTAG_UNPHASED;
2141
546
    v.as.closure.param_names = NULL;
2142
546
    v.as.closure.param_count = 0;
2143
546
    v.as.closure.body = NULL;
2144
546
    v.as.closure.captured_env = NULL;
2145
546
    v.as.closure.default_values = VM_NATIVE_MARKER;
2146
546
    v.as.closure.has_variadic = false;
2147
546
    v.as.closure.native_fn = (void *)fn;
2148
546
    v.region_id = REGION_NONE;
2149
546
    lat_map_set(mod->as.map.map, name, &v);
2150
546
}
2151
2152
12
static LatValue build_math_module(void) {
2153
12
    LatValue m = value_map_new();
2154
    /* Trig */
2155
12
    mod_set_native(&m, "sin", native_sin);
2156
12
    mod_set_native(&m, "cos", native_cos);
2157
12
    mod_set_native(&m, "tan", native_tan);
2158
12
    mod_set_native(&m, "asin", native_asin);
2159
12
    mod_set_native(&m, "acos", native_acos);
2160
12
    mod_set_native(&m, "atan", native_atan);
2161
12
    mod_set_native(&m, "atan2", native_atan2);
2162
12
    mod_set_native(&m, "sinh", native_sinh);
2163
12
    mod_set_native(&m, "cosh", native_cosh);
2164
12
    mod_set_native(&m, "tanh", native_tanh);
2165
    /* Core */
2166
12
    mod_set_native(&m, "sqrt", native_sqrt);
2167
12
    mod_set_native(&m, "pow", native_pow);
2168
12
    mod_set_native(&m, "abs", native_abs);
2169
12
    mod_set_native(&m, "floor", native_floor);
2170
12
    mod_set_native(&m, "ceil", native_ceil);
2171
12
    mod_set_native(&m, "round", native_round);
2172
12
    mod_set_native(&m, "log", native_log);
2173
12
    mod_set_native(&m, "log2", native_log2);
2174
12
    mod_set_native(&m, "log10", native_log10);
2175
12
    mod_set_native(&m, "exp", native_exp);
2176
12
    mod_set_native(&m, "min", native_min);
2177
12
    mod_set_native(&m, "max", native_max);
2178
12
    mod_set_native(&m, "clamp", native_clamp);
2179
12
    mod_set_native(&m, "sign", native_sign);
2180
12
    mod_set_native(&m, "gcd", native_gcd);
2181
12
    mod_set_native(&m, "lcm", native_lcm);
2182
12
    mod_set_native(&m, "lerp", native_lerp);
2183
12
    mod_set_native(&m, "is_nan", native_is_nan);
2184
12
    mod_set_native(&m, "is_inf", native_is_inf);
2185
12
    mod_set_native(&m, "random", native_random);
2186
12
    mod_set_native(&m, "random_int", native_random_int);
2187
    /* Constants (as zero-arg closures) */
2188
12
    mod_set_native(&m, "PI", native_math_pi);
2189
12
    mod_set_native(&m, "E", native_math_e);
2190
12
    return m;
2191
12
}
2192
2193
3
static LatValue build_fs_module(void) {
2194
3
    LatValue m = value_map_new();
2195
3
    mod_set_native(&m, "read_file", native_read_file);
2196
3
    mod_set_native(&m, "write_file", native_write_file);
2197
3
    mod_set_native(&m, "read_file_bytes", native_read_file_bytes);
2198
3
    mod_set_native(&m, "write_file_bytes", native_write_file_bytes);
2199
3
    mod_set_native(&m, "append_file", native_append_file);
2200
3
    mod_set_native(&m, "copy_file", native_copy_file);
2201
3
    mod_set_native(&m, "file_exists", native_file_exists);
2202
3
    mod_set_native(&m, "is_file", native_is_file);
2203
3
    mod_set_native(&m, "is_dir", native_is_dir);
2204
3
    mod_set_native(&m, "file_size", native_file_size);
2205
3
    mod_set_native(&m, "delete_file", native_delete_file);
2206
3
    mod_set_native(&m, "mkdir", native_mkdir);
2207
3
    mod_set_native(&m, "rmdir", native_rmdir);
2208
3
    mod_set_native(&m, "rename", native_fs_rename);
2209
3
    mod_set_native(&m, "chmod", native_chmod);
2210
3
    mod_set_native(&m, "list_dir", native_list_dir);
2211
3
    mod_set_native(&m, "glob", native_glob);
2212
3
    mod_set_native(&m, "stat", native_stat);
2213
3
    mod_set_native(&m, "realpath", native_realpath);
2214
3
    mod_set_native(&m, "tempdir", native_tempdir);
2215
3
    mod_set_native(&m, "tempfile", native_tempfile);
2216
3
    return m;
2217
3
}
2218
2219
3
static LatValue build_path_module(void) {
2220
3
    LatValue m = value_map_new();
2221
3
    mod_set_native(&m, "join", native_path_join);
2222
3
    mod_set_native(&m, "dir", native_path_dir);
2223
3
    mod_set_native(&m, "base", native_path_base);
2224
3
    mod_set_native(&m, "ext", native_path_ext);
2225
3
    return m;
2226
3
}
2227
2228
3
static LatValue build_json_module(void) {
2229
3
    LatValue m = value_map_new();
2230
3
    mod_set_native(&m, "parse", native_json_parse);
2231
3
    mod_set_native(&m, "stringify", native_json_stringify);
2232
3
    return m;
2233
3
}
2234
2235
0
static LatValue build_toml_module(void) {
2236
0
    LatValue m = value_map_new();
2237
0
    mod_set_native(&m, "parse", native_toml_parse);
2238
0
    mod_set_native(&m, "stringify", native_toml_stringify);
2239
0
    return m;
2240
0
}
2241
2242
0
static LatValue build_yaml_module(void) {
2243
0
    LatValue m = value_map_new();
2244
0
    mod_set_native(&m, "parse", native_yaml_parse);
2245
0
    mod_set_native(&m, "stringify", native_yaml_stringify);
2246
0
    return m;
2247
0
}
2248
2249
3
static LatValue build_crypto_module(void) {
2250
3
    LatValue m = value_map_new();
2251
3
    mod_set_native(&m, "sha256", native_sha256);
2252
3
    mod_set_native(&m, "md5", native_md5);
2253
3
    mod_set_native(&m, "base64_encode", native_base64_encode);
2254
3
    mod_set_native(&m, "base64_decode", native_base64_decode);
2255
3
    mod_set_native(&m, "url_encode", native_url_encode);
2256
3
    mod_set_native(&m, "url_decode", native_url_decode);
2257
3
    return m;
2258
3
}
2259
2260
0
static LatValue build_http_module(void) {
2261
0
    LatValue m = value_map_new();
2262
0
    mod_set_native(&m, "get", native_http_get);
2263
0
    mod_set_native(&m, "post", native_http_post);
2264
0
    mod_set_native(&m, "request", native_http_request);
2265
0
    return m;
2266
0
}
2267
2268
0
static LatValue build_net_module(void) {
2269
0
    LatValue m = value_map_new();
2270
    /* TCP */
2271
0
    mod_set_native(&m, "tcp_listen", native_tcp_listen);
2272
0
    mod_set_native(&m, "tcp_accept", native_tcp_accept);
2273
0
    mod_set_native(&m, "tcp_connect", native_tcp_connect);
2274
0
    mod_set_native(&m, "tcp_read", native_tcp_read);
2275
0
    mod_set_native(&m, "tcp_read_bytes", native_tcp_read_bytes);
2276
0
    mod_set_native(&m, "tcp_write", native_tcp_write);
2277
0
    mod_set_native(&m, "tcp_close", native_tcp_close);
2278
0
    mod_set_native(&m, "tcp_peer_addr", native_tcp_peer_addr);
2279
0
    mod_set_native(&m, "tcp_set_timeout", native_tcp_set_timeout);
2280
    /* TLS */
2281
0
    mod_set_native(&m, "tls_connect", native_tls_connect);
2282
0
    mod_set_native(&m, "tls_read", native_tls_read);
2283
0
    mod_set_native(&m, "tls_read_bytes", native_tls_read_bytes);
2284
0
    mod_set_native(&m, "tls_write", native_tls_write);
2285
0
    mod_set_native(&m, "tls_close", native_tls_close);
2286
0
    mod_set_native(&m, "tls_available", native_tls_available);
2287
0
    return m;
2288
0
}
2289
2290
3
static LatValue build_os_module(void) {
2291
3
    LatValue m = value_map_new();
2292
3
    mod_set_native(&m, "exec", native_exec_cmd);
2293
3
    mod_set_native(&m, "shell", native_shell);
2294
3
    mod_set_native(&m, "env", native_env);
2295
3
    mod_set_native(&m, "env_set", native_env_set);
2296
3
    mod_set_native(&m, "env_keys", native_env_keys);
2297
3
    mod_set_native(&m, "cwd", native_cwd);
2298
3
    mod_set_native(&m, "platform", native_platform);
2299
3
    mod_set_native(&m, "hostname", native_hostname);
2300
3
    mod_set_native(&m, "pid", native_pid);
2301
3
    mod_set_native(&m, "args", native_args);
2302
3
    return m;
2303
3
}
2304
2305
3
static LatValue build_time_module(void) {
2306
3
    LatValue m = value_map_new();
2307
3
    mod_set_native(&m, "now", native_time);
2308
3
    mod_set_native(&m, "sleep", native_sleep);
2309
3
    mod_set_native(&m, "format", native_time_format);
2310
3
    mod_set_native(&m, "parse", native_time_parse);
2311
3
    return m;
2312
3
}
2313
2314
3
static LatValue build_regex_module(void) {
2315
3
    LatValue m = value_map_new();
2316
3
    mod_set_native(&m, "match", native_regex_match);
2317
3
    mod_set_native(&m, "find_all", native_regex_find_all);
2318
3
    mod_set_native(&m, "replace", native_regex_replace);
2319
3
    return m;
2320
3
}
2321
2322
282
bool rt_try_builtin_import(const char *name, LatValue *out) {
2323
    /* Paths with directory separators or leading dot are never built-in */
2324
282
    if (strchr(name, '/') || strchr(name, '\\') || name[0] == '.') return false;
2325
2326
36
    if (strcmp(name, "math") == 0)   { *out = build_math_module();   return true; }
2327
24
    if (strcmp(name, "fs") == 0)     { *out = build_fs_module();     return true; }
2328
21
    if (strcmp(name, "path") == 0)   { *out = build_path_module();   return true; }
2329
18
    if (strcmp(name, "json") == 0)   { *out = build_json_module();   return true; }
2330
15
    if (strcmp(name, "toml") == 0)   { *out = build_toml_module();   return true; }
2331
15
    if (strcmp(name, "yaml") == 0)   { *out = build_yaml_module();   return true; }
2332
15
    if (strcmp(name, "crypto") == 0) { *out = build_crypto_module(); return true; }
2333
12
    if (strcmp(name, "http") == 0)   { *out = build_http_module();   return true; }
2334
12
    if (strcmp(name, "net") == 0)    { *out = build_net_module();    return true; }
2335
12
    if (strcmp(name, "os") == 0)     { *out = build_os_module();     return true; }
2336
9
    if (strcmp(name, "time") == 0)   { *out = build_time_module();   return true; }
2337
6
    if (strcmp(name, "regex") == 0)  { *out = build_regex_module();  return true; }
2338
3
    return false;
2339
6
}
2340
2341
/* ── Registration helper ── */
2342
2343
276k
static void rt_register_native(LatRuntime *rt, const char *name, VMNativeFn fn, int arity) {
2344
276k
    (void)arity;
2345
276k
    LatValue v;
2346
276k
    v.type = VAL_CLOSURE;
2347
276k
    v.phase = VTAG_UNPHASED;
2348
276k
    v.as.closure.param_names = NULL;
2349
276k
    v.as.closure.param_count = 0;
2350
276k
    v.as.closure.body = NULL;
2351
276k
    v.as.closure.captured_env = NULL;
2352
276k
    v.as.closure.default_values = VM_NATIVE_MARKER;
2353
276k
    v.as.closure.has_variadic = false;
2354
276k
    v.as.closure.native_fn = (void *)fn;
2355
276k
    v.region_id = REGION_NONE;
2356
276k
    env_define(rt->env, name, v);
2357
276k
}
2358
2359
/* ── Runtime lifecycle ── */
2360
2361
1.70k
void lat_runtime_init(LatRuntime *rt) {
2362
1.70k
    memset(rt, 0, sizeof(LatRuntime));
2363
1.70k
    rt->env = env_new();
2364
1.70k
    rt->struct_meta = NULL;
2365
1.70k
    rt->error = NULL;
2366
1.70k
    rt->tracked_vars = NULL;
2367
1.70k
    rt->tracked_count = 0;
2368
1.70k
    rt->tracked_cap = 0;
2369
1.70k
    rt->tracking_active = false;
2370
1.70k
    rt->pressures = NULL;
2371
1.70k
    rt->pressure_count = 0;
2372
1.70k
    rt->pressure_cap = 0;
2373
1.70k
    rt->reactions = NULL;
2374
1.70k
    rt->reaction_count = 0;
2375
1.70k
    rt->reaction_cap = 0;
2376
1.70k
    rt->bonds = NULL;
2377
1.70k
    rt->bond_count = 0;
2378
1.70k
    rt->bond_cap = 0;
2379
1.70k
    rt->seeds = NULL;
2380
1.70k
    rt->seed_count = 0;
2381
1.70k
    rt->seed_cap = 0;
2382
1.70k
    rt->module_cache = lat_map_new(sizeof(LatValue));
2383
1.70k
    rt->required_files = lat_map_new(sizeof(bool));
2384
1.70k
    rt->loaded_extensions = lat_map_new(sizeof(LatValue));
2385
1.70k
    rt->script_dir = NULL;
2386
1.70k
    rt->prog_argc = 0;
2387
1.70k
    rt->prog_argv = NULL;
2388
1.70k
    rt->active_vm = NULL;
2389
1.70k
    rt->call_closure = NULL;
2390
1.70k
    rt->find_local_value = NULL;
2391
1.70k
    rt->current_line = NULL;
2392
1.70k
    rt->get_var_by_name = NULL;
2393
1.70k
    rt->set_var_by_name = NULL;
2394
2395
    /* Register builtin functions */
2396
1.70k
    rt_register_native(rt, "to_string", native_to_string, 1);
2397
1.70k
    rt_register_native(rt, "typeof", native_typeof, 1);
2398
1.70k
    rt_register_native(rt, "len", native_len, 1);
2399
1.70k
    rt_register_native(rt, "parse_int", native_parse_int, 1);
2400
1.70k
    rt_register_native(rt, "parse_float", native_parse_float, 1);
2401
1.70k
    rt_register_native(rt, "ord", native_ord, 1);
2402
1.70k
    rt_register_native(rt, "chr", native_chr, 1);
2403
1.70k
    rt_register_native(rt, "abs", native_abs, 1);
2404
1.70k
    rt_register_native(rt, "floor", native_floor, 1);
2405
1.70k
    rt_register_native(rt, "ceil", native_ceil, 1);
2406
1.70k
    rt_register_native(rt, "exit", native_exit, 1);
2407
1.70k
    rt_register_native(rt, "error", native_error, 1);
2408
1.70k
    rt_register_native(rt, "is_error", native_is_error, 1);
2409
1.70k
    rt_register_native(rt, "Map::new", native_map_new, 0);
2410
1.70k
    rt_register_native(rt, "Set::new", native_set_new, 0);
2411
1.70k
    rt_register_native(rt, "Set::from", native_set_from, 1);
2412
1.70k
    rt_register_native(rt, "Channel::new", native_channel_new, 0);
2413
1.70k
    rt_register_native(rt, "Buffer::new", native_buffer_new, 1);
2414
1.70k
    rt_register_native(rt, "Buffer::from", native_buffer_from, 1);
2415
1.70k
    rt_register_native(rt, "Buffer::from_string", native_buffer_from_string, 1);
2416
1.70k
    rt_register_native(rt, "Ref::new", native_ref_new, 1);
2417
1.70k
    rt_register_native(rt, "phase_of", native_phase_of, 1);
2418
1.70k
    rt_register_native(rt, "assert", native_assert, 2);
2419
1.70k
    rt_register_native(rt, "version", native_version, 0);
2420
1.70k
    rt_register_native(rt, "input", native_input, 1);
2421
2422
    /* Phase system */
2423
1.70k
    rt_register_native(rt, "track", native_track, 1);
2424
1.70k
    rt_register_native(rt, "phases", native_phases, 1);
2425
1.70k
    rt_register_native(rt, "history", native_history, 1);
2426
1.70k
    rt_register_native(rt, "rewind", native_rewind, 2);
2427
1.70k
    rt_register_native(rt, "pressurize", native_pressurize, 2);
2428
1.70k
    rt_register_native(rt, "depressurize", native_depressurize, 1);
2429
1.70k
    rt_register_native(rt, "pressure_of", native_pressure_of, 1);
2430
1.70k
    rt_register_native(rt, "grow", native_grow, 1);
2431
2432
    /* Math */
2433
1.70k
    rt_register_native(rt, "round", native_round, 1);
2434
1.70k
    rt_register_native(rt, "sqrt", native_sqrt, 1);
2435
1.70k
    rt_register_native(rt, "pow", native_pow, 2);
2436
1.70k
    rt_register_native(rt, "min", native_min, 2);
2437
1.70k
    rt_register_native(rt, "max", native_max, 2);
2438
1.70k
    rt_register_native(rt, "random", native_random, 0);
2439
1.70k
    rt_register_native(rt, "random_int", native_random_int, 2);
2440
1.70k
    rt_register_native(rt, "log", native_log, 1);
2441
1.70k
    rt_register_native(rt, "log2", native_log2, 1);
2442
1.70k
    rt_register_native(rt, "log10", native_log10, 1);
2443
1.70k
    rt_register_native(rt, "sin", native_sin, 1);
2444
1.70k
    rt_register_native(rt, "cos", native_cos, 1);
2445
1.70k
    rt_register_native(rt, "tan", native_tan, 1);
2446
1.70k
    rt_register_native(rt, "asin", native_asin, 1);
2447
1.70k
    rt_register_native(rt, "acos", native_acos, 1);
2448
1.70k
    rt_register_native(rt, "atan", native_atan, 1);
2449
1.70k
    rt_register_native(rt, "atan2", native_atan2, 2);
2450
1.70k
    rt_register_native(rt, "exp", native_exp, 1);
2451
1.70k
    rt_register_native(rt, "sign", native_sign, 1);
2452
1.70k
    rt_register_native(rt, "gcd", native_gcd, 2);
2453
1.70k
    rt_register_native(rt, "lcm", native_lcm, 2);
2454
1.70k
    rt_register_native(rt, "is_nan", native_is_nan, 1);
2455
1.70k
    rt_register_native(rt, "is_inf", native_is_inf, 1);
2456
1.70k
    rt_register_native(rt, "sinh", native_sinh, 1);
2457
1.70k
    rt_register_native(rt, "cosh", native_cosh, 1);
2458
1.70k
    rt_register_native(rt, "tanh", native_tanh, 1);
2459
1.70k
    rt_register_native(rt, "lerp", native_lerp, 3);
2460
1.70k
    rt_register_native(rt, "clamp", native_clamp, 3);
2461
1.70k
    rt_register_native(rt, "math_pi", native_math_pi, 0);
2462
1.70k
    rt_register_native(rt, "math_e", native_math_e, 0);
2463
2464
    /* File system */
2465
1.70k
    rt_register_native(rt, "read_file", native_read_file, 1);
2466
1.70k
    rt_register_native(rt, "write_file", native_write_file, 2);
2467
1.70k
    rt_register_native(rt, "read_file_bytes", native_read_file_bytes, 1);
2468
1.70k
    rt_register_native(rt, "write_file_bytes", native_write_file_bytes, 2);
2469
1.70k
    rt_register_native(rt, "file_exists", native_file_exists, 1);
2470
1.70k
    rt_register_native(rt, "delete_file", native_delete_file, 1);
2471
1.70k
    rt_register_native(rt, "list_dir", native_list_dir, 1);
2472
1.70k
    rt_register_native(rt, "append_file", native_append_file, 2);
2473
1.70k
    rt_register_native(rt, "mkdir", native_mkdir, 1);
2474
1.70k
    rt_register_native(rt, "rename", native_fs_rename, 2);
2475
1.70k
    rt_register_native(rt, "is_dir", native_is_dir, 1);
2476
1.70k
    rt_register_native(rt, "is_file", native_is_file, 1);
2477
1.70k
    rt_register_native(rt, "rmdir", native_rmdir, 1);
2478
1.70k
    rt_register_native(rt, "glob", native_glob, 1);
2479
1.70k
    rt_register_native(rt, "stat", native_stat, 1);
2480
1.70k
    rt_register_native(rt, "copy_file", native_copy_file, 2);
2481
1.70k
    rt_register_native(rt, "realpath", native_realpath, 1);
2482
1.70k
    rt_register_native(rt, "tempdir", native_tempdir, 0);
2483
1.70k
    rt_register_native(rt, "tempfile", native_tempfile, 0);
2484
1.70k
    rt_register_native(rt, "chmod", native_chmod, 2);
2485
1.70k
    rt_register_native(rt, "file_size", native_file_size, 1);
2486
2487
    /* Bytecode compilation/loading */
2488
1.70k
    rt_register_native(rt, "compile_file", native_compile_file, 1);
2489
1.70k
    rt_register_native(rt, "load_bytecode", native_load_bytecode, 1);
2490
2491
    /* Path */
2492
1.70k
    rt_register_native(rt, "path_join", native_path_join, -1);
2493
1.70k
    rt_register_native(rt, "path_dir", native_path_dir, 1);
2494
1.70k
    rt_register_native(rt, "path_base", native_path_base, 1);
2495
1.70k
    rt_register_native(rt, "path_ext", native_path_ext, 1);
2496
2497
    /* Network TCP */
2498
1.70k
    rt_register_native(rt, "tcp_listen", native_tcp_listen, 2);
2499
1.70k
    rt_register_native(rt, "tcp_accept", native_tcp_accept, 1);
2500
1.70k
    rt_register_native(rt, "tcp_connect", native_tcp_connect, 2);
2501
1.70k
    rt_register_native(rt, "tcp_read", native_tcp_read, 1);
2502
1.70k
    rt_register_native(rt, "tcp_read_bytes", native_tcp_read_bytes, 2);
2503
1.70k
    rt_register_native(rt, "tcp_write", native_tcp_write, 2);
2504
1.70k
    rt_register_native(rt, "tcp_close", native_tcp_close, 1);
2505
1.70k
    rt_register_native(rt, "tcp_peer_addr", native_tcp_peer_addr, 1);
2506
1.70k
    rt_register_native(rt, "tcp_set_timeout", native_tcp_set_timeout, 2);
2507
2508
    /* TLS */
2509
1.70k
    rt_register_native(rt, "tls_connect", native_tls_connect, 2);
2510
1.70k
    rt_register_native(rt, "tls_read", native_tls_read, 1);
2511
1.70k
    rt_register_native(rt, "tls_read_bytes", native_tls_read_bytes, 2);
2512
1.70k
    rt_register_native(rt, "tls_write", native_tls_write, 2);
2513
1.70k
    rt_register_native(rt, "tls_close", native_tls_close, 1);
2514
1.70k
    rt_register_native(rt, "tls_available", native_tls_available, 0);
2515
2516
    /* HTTP */
2517
1.70k
    rt_register_native(rt, "http_get", native_http_get, 1);
2518
1.70k
    rt_register_native(rt, "http_post", native_http_post, 2);
2519
1.70k
    rt_register_native(rt, "http_request", native_http_request, 3);
2520
2521
    /* JSON/TOML/YAML */
2522
1.70k
    rt_register_native(rt, "json_parse", native_json_parse, 1);
2523
1.70k
    rt_register_native(rt, "json_stringify", native_json_stringify, 1);
2524
1.70k
    rt_register_native(rt, "toml_parse", native_toml_parse, 1);
2525
1.70k
    rt_register_native(rt, "toml_stringify", native_toml_stringify, 1);
2526
1.70k
    rt_register_native(rt, "yaml_parse", native_yaml_parse, 1);
2527
1.70k
    rt_register_native(rt, "yaml_stringify", native_yaml_stringify, 1);
2528
2529
    /* Crypto */
2530
1.70k
    rt_register_native(rt, "sha256", native_sha256, 1);
2531
1.70k
    rt_register_native(rt, "md5", native_md5, 1);
2532
1.70k
    rt_register_native(rt, "base64_encode", native_base64_encode, 1);
2533
1.70k
    rt_register_native(rt, "base64_decode", native_base64_decode, 1);
2534
2535
    /* Regex */
2536
1.70k
    rt_register_native(rt, "regex_match", native_regex_match, 2);
2537
1.70k
    rt_register_native(rt, "regex_find_all", native_regex_find_all, 2);
2538
1.70k
    rt_register_native(rt, "regex_replace", native_regex_replace, 3);
2539
2540
    /* Time/DateTime */
2541
1.70k
    rt_register_native(rt, "time", native_time, 0);
2542
1.70k
    rt_register_native(rt, "sleep", native_sleep, 1);
2543
1.70k
    rt_register_native(rt, "time_format", native_time_format, 2);
2544
1.70k
    rt_register_native(rt, "time_parse", native_time_parse, 2);
2545
2546
    /* Environment */
2547
1.70k
    rt_register_native(rt, "env", native_env, 1);
2548
1.70k
    rt_register_native(rt, "env_set", native_env_set, 2);
2549
1.70k
    rt_register_native(rt, "env_keys", native_env_keys, 0);
2550
2551
    /* Process */
2552
1.70k
    rt_register_native(rt, "cwd", native_cwd, 0);
2553
1.70k
    rt_register_native(rt, "exec", native_exec_cmd, 1);
2554
1.70k
    rt_register_native(rt, "shell", native_shell, 1);
2555
1.70k
    rt_register_native(rt, "platform", native_platform, 0);
2556
1.70k
    rt_register_native(rt, "hostname", native_hostname, 0);
2557
1.70k
    rt_register_native(rt, "pid", native_pid, 0);
2558
2559
    /* Type/utility */
2560
1.70k
    rt_register_native(rt, "to_int", native_to_int, 1);
2561
1.70k
    rt_register_native(rt, "to_float", native_to_float, 1);
2562
1.70k
    rt_register_native(rt, "struct_name", native_struct_name, 1);
2563
1.70k
    rt_register_native(rt, "struct_fields", native_struct_fields, 1);
2564
1.70k
    rt_register_native(rt, "struct_to_map", native_struct_to_map, 1);
2565
1.70k
    rt_register_native(rt, "repr", native_repr, 1);
2566
1.70k
    rt_register_native(rt, "format", native_format, -1);
2567
1.70k
    rt_register_native(rt, "range", native_range, -1);
2568
1.70k
    rt_register_native(rt, "print_raw", native_print_raw, -1);
2569
1.70k
    rt_register_native(rt, "eprint", native_eprint, -1);
2570
1.70k
    rt_register_native(rt, "identity", native_identity, 1);
2571
1.70k
    rt_register_native(rt, "debug_assert", native_debug_assert, 2);
2572
1.70k
    rt_register_native(rt, "panic", native_panic, 1);
2573
2574
    /* Module loading */
2575
1.70k
    rt_register_native(rt, "require", native_require, 1);
2576
1.70k
    rt_register_native(rt, "require_ext", native_require_ext, 1);
2577
2578
    /* Metaprogramming/reflection */
2579
1.70k
    rt_register_native(rt, "args", native_args, 0);
2580
1.70k
    rt_register_native(rt, "struct_from_map", native_struct_from_map, 2);
2581
1.70k
    rt_register_native(rt, "is_complete", native_is_complete, 1);
2582
1.70k
    rt_register_native(rt, "tokenize", native_tokenize, 1);
2583
1.70k
    rt_register_native(rt, "lat_eval", native_lat_eval, 1);
2584
2585
    /* Bitwise float conversion (for bytecode serialization) */
2586
1.70k
    rt_register_native(rt, "float_to_bits", native_float_to_bits, 1);
2587
1.70k
    rt_register_native(rt, "bits_to_float", native_bits_to_float, 1);
2588
2589
    /* URL encoding */
2590
1.70k
    rt_register_native(rt, "url_encode", native_url_encode, 1);
2591
1.70k
    rt_register_native(rt, "url_decode", native_url_decode, 1);
2592
2593
    /* CSV */
2594
1.70k
    rt_register_native(rt, "csv_parse", native_csv_parse, 1);
2595
1.70k
    rt_register_native(rt, "csv_stringify", native_csv_stringify, 1);
2596
2597
    /* Functional */
2598
1.70k
    rt_register_native(rt, "pipe", native_pipe, -1);
2599
1.70k
    rt_register_native(rt, "compose", native_compose, 2);
2600
2601
1.70k
    intern_init();
2602
2603
1.70k
    intern_init();
2604
1.70k
}
2605
2606
1.70k
void lat_runtime_free(LatRuntime *rt) {
2607
1.70k
    if (rt->env) env_free(rt->env);
2608
1.70k
    if (rt->struct_meta) env_free(rt->struct_meta);
2609
1.70k
    free(rt->error);
2610
2611
    /* Free module cache */
2612
29.0k
    for (size_t i = 0; i < rt->module_cache.cap; i++) {
2613
27.2k
        if (rt->module_cache.entries[i].state == MAP_OCCUPIED) {
2614
0
            LatValue *v = (LatValue *)rt->module_cache.entries[i].value;
2615
0
            value_free(v);
2616
0
        }
2617
27.2k
    }
2618
1.70k
    lat_map_free(&rt->module_cache);
2619
1.70k
    lat_map_free(&rt->required_files);
2620
2621
    /* Free extension cache */
2622
29.0k
    for (size_t i = 0; i < rt->loaded_extensions.cap; i++) {
2623
27.2k
        if (rt->loaded_extensions.entries[i].state == MAP_OCCUPIED) {
2624
30
            LatValue *v = (LatValue *)rt->loaded_extensions.entries[i].value;
2625
30
            value_free(v);
2626
30
        }
2627
27.2k
    }
2628
1.70k
    lat_map_free(&rt->loaded_extensions);
2629
1.70k
    free(rt->script_dir);
2630
2631
    /* Free tracked vars */
2632
1.73k
    for (size_t i = 0; i < rt->tracked_count; i++) {
2633
26
        free(rt->tracked_vars[i].name);
2634
86
        for (size_t j = 0; j < rt->tracked_vars[i].snap_count; j++) {
2635
60
            free(rt->tracked_vars[i].snapshots[j].phase);
2636
60
            free(rt->tracked_vars[i].snapshots[j].fn_name);
2637
60
            value_free(&rt->tracked_vars[i].snapshots[j].value);
2638
60
        }
2639
26
        free(rt->tracked_vars[i].snapshots);
2640
26
    }
2641
1.70k
    free(rt->tracked_vars);
2642
2643
    /* Free pressures */
2644
1.71k
    for (size_t i = 0; i < rt->pressure_count; i++) {
2645
10
        free(rt->pressures[i].name);
2646
10
        free(rt->pressures[i].mode);
2647
10
    }
2648
1.70k
    free(rt->pressures);
2649
2650
    /* Free reactions */
2651
1.72k
    for (size_t i = 0; i < rt->reaction_count; i++) {
2652
14
        free(rt->reactions[i].var_name);
2653
30
        for (size_t j = 0; j < rt->reactions[i].cb_count; j++)
2654
16
            value_free(&rt->reactions[i].callbacks[j]);
2655
14
        free(rt->reactions[i].callbacks);
2656
14
    }
2657
1.70k
    free(rt->reactions);
2658
2659
    /* Free bonds */
2660
1.70k
    for (size_t i = 0; i < rt->bond_count; i++) {
2661
2
        free(rt->bonds[i].target);
2662
4
        for (size_t j = 0; j < rt->bonds[i].dep_count; j++) {
2663
2
            free(rt->bonds[i].deps[j]);
2664
2
            if (rt->bonds[i].dep_strategies) free(rt->bonds[i].dep_strategies[j]);
2665
2
        }
2666
2
        free(rt->bonds[i].deps);
2667
2
        free(rt->bonds[i].dep_strategies);
2668
2
    }
2669
1.70k
    free(rt->bonds);
2670
2671
    /* Free seeds */
2672
1.70k
    for (size_t i = 0; i < rt->seed_count; i++) {
2673
2
        free(rt->seeds[i].var_name);
2674
2
        value_free(&rt->seeds[i].contract);
2675
2
    }
2676
1.70k
    free(rt->seeds);
2677
2678
1.70k
    intern_free();
2679
1.70k
}