goon

goon

https://git.tonybtw.com/goon.git git://git.tonybtw.com/goon.git
47,569 bytes raw
1
#define _POSIX_C_SOURCE 200809L
2
#include "goon.h"
3
#include <stdlib.h>
4
#include <string.h>
5
#include <stdio.h>
6
#include <ctype.h>
7
#include <libgen.h>
8
9
typedef enum {
10
    TOK_EOF,
11
    TOK_LBRACE,
12
    TOK_RBRACE,
13
    TOK_LBRACKET,
14
    TOK_RBRACKET,
15
    TOK_LPAREN,
16
    TOK_RPAREN,
17
    TOK_SEMICOLON,
18
    TOK_COMMA,
19
    TOK_EQUALS,
20
    TOK_COLON,
21
    TOK_QUESTION,
22
    TOK_SPREAD,
23
    TOK_DOTDOT,
24
    TOK_DOT,
25
    TOK_ARROW,
26
    TOK_INT,
27
    TOK_STRING,
28
    TOK_IDENT,
29
    TOK_TRUE,
30
    TOK_FALSE,
31
    TOK_LET,
32
    TOK_IF,
33
    TOK_THEN,
34
    TOK_ELSE,
35
    TOK_IMPORT,
36
} Token_Type;
37
38
typedef struct {
39
    Token_Type type;
40
    union {
41
        int64_t integer;
42
        char *string;
43
    } data;
44
} Token;
45
46
typedef struct {
47
    const char *src;
48
    size_t pos;
49
    size_t len;
50
    size_t line;
51
    size_t col;
52
    size_t line_start;
53
    size_t token_start;
54
    Token current;
55
    char *error;
56
    size_t error_line;
57
    size_t error_col;
58
} Lexer;
59
60
static void lexer_init(Lexer *lex, const char *src) {
61
    lex->src = src;
62
    lex->pos = 0;
63
    lex->len = strlen(src);
64
    lex->line = 1;
65
    lex->col = 1;
66
    lex->line_start = 0;
67
    lex->token_start = 0;
68
    lex->current.type = TOK_EOF;
69
    lex->current.data.string = NULL;
70
    lex->error = NULL;
71
    lex->error_line = 0;
72
    lex->error_col = 0;
73
}
74
75
static void lexer_set_error(Lexer *lex, const char *msg) {
76
    if (lex->error) free(lex->error);
77
    lex->error = strdup(msg);
78
    lex->error_line = lex->line;
79
    lex->error_col = lex->col;
80
}
81
82
typedef struct {
83
    size_t pos;
84
    size_t line;
85
    size_t col;
86
    size_t line_start;
87
    size_t token_start;
88
    Token current;
89
} Lexer_State;
90
91
static void lexer_save(Lexer *lex, Lexer_State *state) {
92
    state->pos = lex->pos;
93
    state->line = lex->line;
94
    state->col = lex->col;
95
    state->line_start = lex->line_start;
96
    state->token_start = lex->token_start;
97
    state->current = lex->current;
98
    if (lex->current.type == TOK_STRING || lex->current.type == TOK_IDENT) {
99
        state->current.data.string = strdup(lex->current.data.string);
100
    }
101
}
102
103
static void lexer_restore(Lexer *lex, Lexer_State *state) {
104
    if (lex->current.type == TOK_STRING || lex->current.type == TOK_IDENT) {
105
        free(lex->current.data.string);
106
    }
107
    lex->pos = state->pos;
108
    lex->line = state->line;
109
    lex->col = state->col;
110
    lex->line_start = state->line_start;
111
    lex->token_start = state->token_start;
112
    lex->current = state->current;
113
}
114
115
static void lexer_state_free(Lexer_State *state) {
116
    if (state->current.type == TOK_STRING || state->current.type == TOK_IDENT) {
117
        free(state->current.data.string);
118
        state->current.data.string = NULL;
119
    }
120
}
121
122
static void lexer_advance(Lexer *lex) {
123
    if (lex->pos < lex->len) {
124
        if (lex->src[lex->pos] == '\n') {
125
            lex->line++;
126
            lex->col = 1;
127
            lex->pos++;
128
            lex->line_start = lex->pos;
129
        } else {
130
            lex->col++;
131
            lex->pos++;
132
        }
133
    }
134
}
135
136
static void lexer_skip_whitespace(Lexer *lex) {
137
    while (lex->pos < lex->len) {
138
        char c = lex->src[lex->pos];
139
        if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
140
            lexer_advance(lex);
141
        } else if (c == '/' && lex->pos + 1 < lex->len && lex->src[lex->pos + 1] == '/') {
142
            lexer_advance(lex);
143
            lexer_advance(lex);
144
            while (lex->pos < lex->len && lex->src[lex->pos] != '\n') {
145
                lexer_advance(lex);
146
            }
147
        } else if (c == '/' && lex->pos + 1 < lex->len && lex->src[lex->pos + 1] == '*') {
148
            lexer_advance(lex);
149
            lexer_advance(lex);
150
            while (lex->pos + 1 < lex->len) {
151
                if (lex->src[lex->pos] == '*' && lex->src[lex->pos + 1] == '/') {
152
                    lexer_advance(lex);
153
                    lexer_advance(lex);
154
                    break;
155
                }
156
                lexer_advance(lex);
157
            }
158
        } else {
159
            break;
160
        }
161
    }
162
}
163
164
static bool is_ident_start(char c) {
165
    return isalpha(c) || c == '_';
166
}
167
168
static bool is_ident_char(char c) {
169
    return isalnum(c) || c == '_';
170
}
171
172
static char *strdup_range(const char *start, size_t len) {
173
    char *s = malloc(len + 1);
174
    if (!s) return NULL;
175
    memcpy(s, start, len);
176
    s[len] = '\0';
177
    return s;
178
}
179
180
static bool lexer_next(Lexer *lex) {
181
    if (lex->current.type == TOK_STRING || lex->current.type == TOK_IDENT) {
182
        free(lex->current.data.string);
183
        lex->current.data.string = NULL;
184
    }
185
186
    lexer_skip_whitespace(lex);
187
    lex->token_start = lex->pos;
188
189
    if (lex->pos >= lex->len) {
190
        lex->current.type = TOK_EOF;
191
        return true;
192
    }
193
194
    char c = lex->src[lex->pos];
195
196
    if (c == '{') { lex->current.type = TOK_LBRACE; lexer_advance(lex); return true; }
197
    if (c == '}') { lex->current.type = TOK_RBRACE; lexer_advance(lex); return true; }
198
    if (c == '[') { lex->current.type = TOK_LBRACKET; lexer_advance(lex); return true; }
199
    if (c == ']') { lex->current.type = TOK_RBRACKET; lexer_advance(lex); return true; }
200
    if (c == '(') { lex->current.type = TOK_LPAREN; lexer_advance(lex); return true; }
201
    if (c == ')') { lex->current.type = TOK_RPAREN; lexer_advance(lex); return true; }
202
    if (c == ';') { lex->current.type = TOK_SEMICOLON; lexer_advance(lex); return true; }
203
    if (c == ',') { lex->current.type = TOK_COMMA; lexer_advance(lex); return true; }
204
    if (c == '=' && lex->pos + 1 < lex->len && lex->src[lex->pos + 1] == '>') {
205
        lex->current.type = TOK_ARROW;
206
        lexer_advance(lex);
207
        lexer_advance(lex);
208
        return true;
209
    }
210
    if (c == '=') { lex->current.type = TOK_EQUALS; lexer_advance(lex); return true; }
211
    if (c == ':') { lex->current.type = TOK_COLON; lexer_advance(lex); return true; }
212
    if (c == '?') { lex->current.type = TOK_QUESTION; lexer_advance(lex); return true; }
213
214
    if (c == '.' && lex->pos + 2 < lex->len &&
215
        lex->src[lex->pos + 1] == '.' && lex->src[lex->pos + 2] == '.') {
216
        lex->current.type = TOK_SPREAD;
217
        lexer_advance(lex);
218
        lexer_advance(lex);
219
        lexer_advance(lex);
220
        return true;
221
    }
222
223
    if (c == '.' && lex->pos + 1 < lex->len && lex->src[lex->pos + 1] == '.') {
224
        lex->current.type = TOK_DOTDOT;
225
        lexer_advance(lex);
226
        lexer_advance(lex);
227
        return true;
228
    }
229
230
    if (c == '.') {
231
        lex->current.type = TOK_DOT;
232
        lexer_advance(lex);
233
        return true;
234
    }
235
236
    if (c == '"') {
237
        lexer_advance(lex);
238
        size_t buf_size = 256;
239
        size_t buf_len = 0;
240
        char *buf = malloc(buf_size);
241
        if (!buf) return false;
242
243
        while (lex->pos < lex->len && lex->src[lex->pos] != '"') {
244
            char ch = lex->src[lex->pos];
245
            if (ch == '\\' && lex->pos + 1 < lex->len) {
246
                lexer_advance(lex);
247
                ch = lex->src[lex->pos];
248
                switch (ch) {
249
                    case 'n': ch = '\n'; break;
250
                    case 't': ch = '\t'; break;
251
                    case 'r': ch = '\r'; break;
252
                    case '\\': ch = '\\'; break;
253
                    case '"': ch = '"'; break;
254
                    case '$': ch = '$'; break;
255
                    default: break;
256
                }
257
            }
258
            if (buf_len + 1 >= buf_size) {
259
                buf_size *= 2;
260
                buf = realloc(buf, buf_size);
261
                if (!buf) return false;
262
            }
263
            buf[buf_len++] = ch;
264
            lexer_advance(lex);
265
        }
266
267
        if (lex->pos >= lex->len) {
268
            free(buf);
269
            lexer_set_error(lex, "unterminated string");
270
            return false;
271
        }
272
273
        lexer_advance(lex);
274
        buf[buf_len] = '\0';
275
        lex->current.type = TOK_STRING;
276
        lex->current.data.string = buf;
277
        return true;
278
    }
279
280
    if (isdigit(c) || (c == '-' && lex->pos + 1 < lex->len && isdigit(lex->src[lex->pos + 1]))) {
281
        int sign = 1;
282
        if (c == '-') {
283
            sign = -1;
284
            lexer_advance(lex);
285
        }
286
        int64_t val = 0;
287
        while (lex->pos < lex->len && isdigit(lex->src[lex->pos])) {
288
            val = val * 10 + (lex->src[lex->pos] - '0');
289
            lexer_advance(lex);
290
        }
291
        lex->current.type = TOK_INT;
292
        lex->current.data.integer = val * sign;
293
        return true;
294
    }
295
296
    if (is_ident_start(c)) {
297
        size_t start = lex->pos;
298
        while (lex->pos < lex->len && is_ident_char(lex->src[lex->pos])) {
299
            lexer_advance(lex);
300
        }
301
        char *ident = strdup_range(lex->src + start, lex->pos - start);
302
303
        if (strcmp(ident, "true") == 0) {
304
            free(ident);
305
            lex->current.type = TOK_TRUE;
306
            return true;
307
        }
308
        if (strcmp(ident, "false") == 0) {
309
            free(ident);
310
            lex->current.type = TOK_FALSE;
311
            return true;
312
        }
313
        if (strcmp(ident, "let") == 0) {
314
            free(ident);
315
            lex->current.type = TOK_LET;
316
            return true;
317
        }
318
        if (strcmp(ident, "if") == 0) {
319
            free(ident);
320
            lex->current.type = TOK_IF;
321
            return true;
322
        }
323
        if (strcmp(ident, "then") == 0) {
324
            free(ident);
325
            lex->current.type = TOK_THEN;
326
            return true;
327
        }
328
        if (strcmp(ident, "else") == 0) {
329
            free(ident);
330
            lex->current.type = TOK_ELSE;
331
            return true;
332
        }
333
        if (strcmp(ident, "import") == 0) {
334
            free(ident);
335
            lex->current.type = TOK_IMPORT;
336
            return true;
337
        }
338
339
        lex->current.type = TOK_IDENT;
340
        lex->current.data.string = ident;
341
        return true;
342
    }
343
344
    lexer_set_error(lex, "unexpected character");
345
    return false;
346
}
347
348
static Goon_Value *alloc_value(Goon_Ctx *ctx) {
349
    Goon_Value *val = malloc(sizeof(Goon_Value));
350
    if (!val) return NULL;
351
    val->type = GOON_NIL;
352
    val->next_alloc = ctx->values;
353
    ctx->values = val;
354
    return val;
355
}
356
357
static Goon_Record_Field *alloc_field(Goon_Ctx *ctx) {
358
    Goon_Record_Field *field = malloc(sizeof(Goon_Record_Field));
359
    if (!field) return NULL;
360
    field->key = NULL;
361
    field->value = NULL;
362
    field->next = ctx->fields;
363
    ctx->fields = field;
364
    return field;
365
}
366
367
Goon_Value *goon_nil(Goon_Ctx *ctx) {
368
    Goon_Value *val = alloc_value(ctx);
369
    if (!val) return NULL;
370
    val->type = GOON_NIL;
371
    return val;
372
}
373
374
Goon_Value *goon_bool(Goon_Ctx *ctx, bool b) {
375
    Goon_Value *val = alloc_value(ctx);
376
    if (!val) return NULL;
377
    val->type = GOON_BOOL;
378
    val->data.boolean = b;
379
    return val;
380
}
381
382
Goon_Value *goon_int(Goon_Ctx *ctx, int64_t i) {
383
    Goon_Value *val = alloc_value(ctx);
384
    if (!val) return NULL;
385
    val->type = GOON_INT;
386
    val->data.integer = i;
387
    return val;
388
}
389
390
Goon_Value *goon_string(Goon_Ctx *ctx, const char *s) {
391
    Goon_Value *val = alloc_value(ctx);
392
    if (!val) return NULL;
393
    val->type = GOON_STRING;
394
    val->data.string = strdup(s);
395
    return val;
396
}
397
398
Goon_Value *goon_list(Goon_Ctx *ctx) {
399
    Goon_Value *val = alloc_value(ctx);
400
    if (!val) return NULL;
401
    val->type = GOON_LIST;
402
    val->data.list.items = NULL;
403
    val->data.list.len = 0;
404
    val->data.list.cap = 0;
405
    return val;
406
}
407
408
Goon_Value *goon_record(Goon_Ctx *ctx) {
409
    Goon_Value *val = alloc_value(ctx);
410
    if (!val) return NULL;
411
    val->type = GOON_RECORD;
412
    val->data.record.fields = NULL;
413
    return val;
414
}
415
416
static Goon_Value *goon_lambda(Goon_Ctx *ctx, char **params, size_t param_count, const char *body, Goon_Binding *env) {
417
    Goon_Value *val = alloc_value(ctx);
418
    if (!val) return NULL;
419
    val->type = GOON_LAMBDA;
420
    val->data.lambda.params = malloc(param_count * sizeof(char *));
421
    if (!val->data.lambda.params && param_count > 0) return NULL;
422
    for (size_t i = 0; i < param_count; i++) {
423
        val->data.lambda.params[i] = strdup(params[i]);
424
    }
425
    val->data.lambda.param_count = param_count;
426
    val->data.lambda.body = strdup(body);
427
    val->data.lambda.env = env;
428
    return val;
429
}
430
431
bool goon_is_nil(Goon_Value *val) {
432
    return val == NULL || val->type == GOON_NIL;
433
}
434
435
bool goon_is_bool(Goon_Value *val) {
436
    return val != NULL && val->type == GOON_BOOL;
437
}
438
439
bool goon_is_int(Goon_Value *val) {
440
    return val != NULL && val->type == GOON_INT;
441
}
442
443
bool goon_is_string(Goon_Value *val) {
444
    return val != NULL && val->type == GOON_STRING;
445
}
446
447
bool goon_is_list(Goon_Value *val) {
448
    return val != NULL && val->type == GOON_LIST;
449
}
450
451
bool goon_is_record(Goon_Value *val) {
452
    return val != NULL && val->type == GOON_RECORD;
453
}
454
455
bool goon_to_bool(Goon_Value *val) {
456
    if (val == NULL) return false;
457
    if (val->type == GOON_BOOL) return val->data.boolean;
458
    if (val->type == GOON_NIL) return false;
459
    return true;
460
}
461
462
int64_t goon_to_int(Goon_Value *val) {
463
    if (val == NULL || val->type != GOON_INT) return 0;
464
    return val->data.integer;
465
}
466
467
const char *goon_to_string(Goon_Value *val) {
468
    if (val == NULL || val->type != GOON_STRING) return NULL;
469
    return val->data.string;
470
}
471
472
void goon_list_push(Goon_Ctx *ctx, Goon_Value *list, Goon_Value *item) {
473
    (void)ctx;
474
    if (!list || list->type != GOON_LIST) return;
475
    if (list->data.list.len >= list->data.list.cap) {
476
        size_t new_cap = list->data.list.cap == 0 ? 8 : list->data.list.cap * 2;
477
        Goon_Value **new_items = realloc(list->data.list.items, new_cap * sizeof(Goon_Value *));
478
        if (!new_items) return;
479
        list->data.list.items = new_items;
480
        list->data.list.cap = new_cap;
481
    }
482
    list->data.list.items[list->data.list.len++] = item;
483
}
484
485
size_t goon_list_len(Goon_Value *list) {
486
    if (!list || list->type != GOON_LIST) return 0;
487
    return list->data.list.len;
488
}
489
490
Goon_Value *goon_list_get(Goon_Value *list, size_t index) {
491
    if (!list || list->type != GOON_LIST) return NULL;
492
    if (index >= list->data.list.len) return NULL;
493
    return list->data.list.items[index];
494
}
495
496
void goon_record_set(Goon_Ctx *ctx, Goon_Value *record, const char *key, Goon_Value *value) {
497
    if (!record || record->type != GOON_RECORD) return;
498
499
    Goon_Record_Field *f = record->data.record.fields;
500
    while (f) {
501
        if (strcmp(f->key, key) == 0) {
502
            f->value = value;
503
            return;
504
        }
505
        f = f->next;
506
    }
507
508
    Goon_Record_Field *field = alloc_field(ctx);
509
    if (!field) return;
510
    field->key = strdup(key);
511
    field->value = value;
512
    field->next = record->data.record.fields;
513
    record->data.record.fields = field;
514
}
515
516
Goon_Value *goon_record_get(Goon_Value *record, const char *key) {
517
    if (!record || record->type != GOON_RECORD) return NULL;
518
    Goon_Record_Field *f = record->data.record.fields;
519
    while (f) {
520
        if (strcmp(f->key, key) == 0) {
521
            return f->value;
522
        }
523
        f = f->next;
524
    }
525
    return NULL;
526
}
527
528
static void goon_record_set_path(Goon_Ctx *ctx, Goon_Value *record, char **path, size_t path_len, Goon_Value *value) {
529
    if (path_len == 0) return;
530
531
    if (path_len == 1) {
532
        goon_record_set(ctx, record, path[0], value);
533
        return;
534
    }
535
536
    Goon_Value *existing = goon_record_get(record, path[0]);
537
    Goon_Value *intermediate;
538
539
    if (existing && existing->type == GOON_RECORD) {
540
        intermediate = existing;
541
    } else {
542
        intermediate = goon_record(ctx);
543
        goon_record_set(ctx, record, path[0], intermediate);
544
    }
545
546
    goon_record_set_path(ctx, intermediate, path + 1, path_len - 1, value);
547
}
548
549
Goon_Record_Field *goon_record_fields(Goon_Value *record) {
550
    if (!record || record->type != GOON_RECORD) return NULL;
551
    return record->data.record.fields;
552
}
553
554
static Goon_Value *lookup(Goon_Ctx *ctx, const char *name) {
555
    Goon_Binding *b = ctx->env;
556
    while (b) {
557
        if (strcmp(b->name, name) == 0) {
558
            return b->value;
559
        }
560
        b = b->next;
561
    }
562
    return NULL;
563
}
564
565
static void define(Goon_Ctx *ctx, const char *name, Goon_Value *value) {
566
    Goon_Binding *b = ctx->env;
567
    while (b) {
568
        if (strcmp(b->name, name) == 0) {
569
            b->value = value;
570
            return;
571
        }
572
        b = b->next;
573
    }
574
    b = malloc(sizeof(Goon_Binding));
575
    if (!b) return;
576
    b->name = strdup(name);
577
    b->value = value;
578
    b->next = ctx->env;
579
    ctx->env = b;
580
}
581
582
typedef struct {
583
    Goon_Ctx *ctx;
584
    Lexer *lex;
585
} Parser;
586
587
static Goon_Value *parse_expr(Parser *p);
588
589
static Goon_Value *call_lambda(Goon_Ctx *ctx, Goon_Value *fn, Goon_Value **args, size_t argc) {
590
    if (!fn || fn->type != GOON_LAMBDA) return goon_nil(ctx);
591
    if (argc != fn->data.lambda.param_count) return goon_nil(ctx);
592
593
    Goon_Binding *old_env = ctx->env;
594
    ctx->env = fn->data.lambda.env;
595
596
    for (size_t i = 0; i < argc; i++) {
597
        define(ctx, fn->data.lambda.params[i], args[i]);
598
    }
599
600
    Lexer body_lex;
601
    lexer_init(&body_lex, fn->data.lambda.body);
602
    Parser body_parser;
603
    body_parser.ctx = ctx;
604
    body_parser.lex = &body_lex;
605
606
    if (!lexer_next(&body_lex)) {
607
        ctx->env = old_env;
608
        return goon_nil(ctx);
609
    }
610
611
    Goon_Value *result = parse_expr(&body_parser);
612
613
    ctx->env = old_env;
614
    return result ? result : goon_nil(ctx);
615
}
616
617
static Goon_Value *builtin_map(Goon_Ctx *ctx, Goon_Value **args, size_t argc) {
618
    if (argc != 2) return goon_nil(ctx);
619
    Goon_Value *list = args[0];
620
    Goon_Value *fn = args[1];
621
622
    if (!list || list->type != GOON_LIST) return goon_nil(ctx);
623
    if (!fn || (fn->type != GOON_LAMBDA && fn->type != GOON_BUILTIN)) return goon_nil(ctx);
624
625
    Goon_Value *result = goon_list(ctx);
626
627
    for (size_t i = 0; i < list->data.list.len; i++) {
628
        Goon_Value *item = list->data.list.items[i];
629
        Goon_Value *mapped;
630
631
        if (fn->type == GOON_LAMBDA) {
632
            Goon_Value *fn_args[1] = { item };
633
            mapped = call_lambda(ctx, fn, fn_args, 1);
634
        } else {
635
            Goon_Value *fn_args[1] = { item };
636
            mapped = fn->data.builtin(ctx, fn_args, 1);
637
        }
638
639
        goon_list_push(ctx, result, mapped);
640
    }
641
642
    return result;
643
}
644
645
static Goon_Value *interpolate_string(Goon_Ctx *ctx, const char *str) {
646
    size_t len = strlen(str);
647
    size_t buf_size = len * 2 + 1;
648
    char *buf = malloc(buf_size);
649
    if (!buf) return goon_string(ctx, str);
650
651
    size_t buf_len = 0;
652
    size_t i = 0;
653
654
    while (i < len) {
655
        if (str[i] == '$' && i + 1 < len && str[i + 1] == '{') {
656
            i += 2;
657
            size_t var_start = i;
658
            while (i < len && str[i] != '}') {
659
                i++;
660
            }
661
            if (i < len) {
662
                char *var_name = strdup_range(str + var_start, i - var_start);
663
                Goon_Value *val = lookup(ctx, var_name);
664
                free(var_name);
665
666
                if (val) {
667
                    const char *insert = NULL;
668
                    char num_buf[32];
669
                    if (val->type == GOON_STRING) {
670
                        insert = val->data.string;
671
                    } else if (val->type == GOON_INT) {
672
                        snprintf(num_buf, sizeof(num_buf), "%ld", val->data.integer);
673
                        insert = num_buf;
674
                    } else if (val->type == GOON_BOOL) {
675
                        insert = val->data.boolean ? "true" : "false";
676
                    }
677
                    if (insert) {
678
                        size_t insert_len = strlen(insert);
679
                        while (buf_len + insert_len >= buf_size) {
680
                            buf_size *= 2;
681
                            buf = realloc(buf, buf_size);
682
                        }
683
                        memcpy(buf + buf_len, insert, insert_len);
684
                        buf_len += insert_len;
685
                    }
686
                }
687
                i++;
688
            }
689
        } else {
690
            if (buf_len + 1 >= buf_size) {
691
                buf_size *= 2;
692
                buf = realloc(buf, buf_size);
693
            }
694
            buf[buf_len++] = str[i++];
695
        }
696
    }
697
698
    buf[buf_len] = '\0';
699
    Goon_Value *result = goon_string(ctx, buf);
700
    free(buf);
701
    return result;
702
}
703
704
static Goon_Value *parse_record(Parser *p) {
705
    Goon_Value *record = goon_record(p->ctx);
706
707
    if (!lexer_next(p->lex)) return NULL;
708
709
    while (p->lex->current.type != TOK_RBRACE && p->lex->current.type != TOK_EOF) {
710
        if (p->lex->current.type == TOK_SPREAD) {
711
            if (!lexer_next(p->lex)) return NULL;
712
            Goon_Value *spread_val = parse_expr(p);
713
            if (spread_val && spread_val->type == GOON_RECORD) {
714
                Goon_Record_Field *f = spread_val->data.record.fields;
715
                while (f) {
716
                    goon_record_set(p->ctx, record, f->key, f->value);
717
                    f = f->next;
718
                }
719
            }
720
            if (p->lex->current.type == TOK_COMMA) {
721
                if (!lexer_next(p->lex)) return NULL;
722
            } else if (p->lex->current.type == TOK_SEMICOLON) {
723
                if (!lexer_next(p->lex)) return NULL;
724
            }
725
            continue;
726
        }
727
728
        if (p->lex->current.type != TOK_IDENT) {
729
            lexer_set_error(p->lex, "expected field name");
730
            return NULL;
731
        }
732
733
        char *path[32];
734
        size_t path_len = 0;
735
736
        path[path_len++] = strdup(p->lex->current.data.string);
737
        if (!lexer_next(p->lex)) { free(path[0]); return NULL; }
738
739
        while (p->lex->current.type == TOK_DOT && path_len < 32) {
740
            if (!lexer_next(p->lex)) {
741
                for (size_t i = 0; i < path_len; i++) free(path[i]);
742
                return NULL;
743
            }
744
            if (p->lex->current.type != TOK_IDENT) {
745
                lexer_set_error(p->lex, "expected field name after .");
746
                for (size_t i = 0; i < path_len; i++) free(path[i]);
747
                return NULL;
748
            }
749
            path[path_len++] = strdup(p->lex->current.data.string);
750
            if (!lexer_next(p->lex)) {
751
                for (size_t i = 0; i < path_len; i++) free(path[i]);
752
                return NULL;
753
            }
754
        }
755
756
        if (p->lex->current.type == TOK_COLON) {
757
            if (!lexer_next(p->lex)) {
758
                for (size_t i = 0; i < path_len; i++) free(path[i]);
759
                return NULL;
760
            }
761
            if (!lexer_next(p->lex)) {
762
                for (size_t i = 0; i < path_len; i++) free(path[i]);
763
                return NULL;
764
            }
765
        }
766
767
        if (p->lex->current.type != TOK_EQUALS) {
768
            lexer_set_error(p->lex, "expected = after field name");
769
            for (size_t i = 0; i < path_len; i++) free(path[i]);
770
            return NULL;
771
        }
772
773
        if (!lexer_next(p->lex)) {
774
            for (size_t i = 0; i < path_len; i++) free(path[i]);
775
            return NULL;
776
        }
777
778
        Goon_Value *value = parse_expr(p);
779
        if (!value) {
780
            for (size_t i = 0; i < path_len; i++) free(path[i]);
781
            return NULL;
782
        }
783
784
        goon_record_set_path(p->ctx, record, path, path_len, value);
785
        for (size_t i = 0; i < path_len; i++) free(path[i]);
786
787
        if (p->lex->current.type == TOK_SEMICOLON) {
788
            if (!lexer_next(p->lex)) return NULL;
789
        } else if (p->lex->current.type == TOK_COMMA) {
790
            if (!lexer_next(p->lex)) return NULL;
791
        }
792
    }
793
794
    if (p->lex->current.type != TOK_RBRACE) {
795
        lexer_set_error(p->lex, "expected }");
796
        return NULL;
797
    }
798
799
    if (!lexer_next(p->lex)) return NULL;
800
    return record;
801
}
802
803
static Goon_Value *parse_list(Parser *p) {
804
    Goon_Value *list = goon_list(p->ctx);
805
806
    if (!lexer_next(p->lex)) return NULL;
807
808
    while (p->lex->current.type != TOK_RBRACKET && p->lex->current.type != TOK_EOF) {
809
        if (p->lex->current.type == TOK_SPREAD) {
810
            if (!lexer_next(p->lex)) return NULL;
811
            Goon_Value *spread_val = parse_expr(p);
812
            if (spread_val && spread_val->type == GOON_LIST) {
813
                for (size_t i = 0; i < spread_val->data.list.len; i++) {
814
                    goon_list_push(p->ctx, list, spread_val->data.list.items[i]);
815
                }
816
            }
817
        } else if (p->lex->current.type == TOK_INT) {
818
            int64_t start = p->lex->current.data.integer;
819
            if (!lexer_next(p->lex)) return NULL;
820
821
            if (p->lex->current.type == TOK_DOTDOT) {
822
                if (!lexer_next(p->lex)) return NULL;
823
                if (p->lex->current.type != TOK_INT) {
824
                    lexer_set_error(p->lex, "expected integer after ..");
825
                    return NULL;
826
                }
827
                int64_t end = p->lex->current.data.integer;
828
                if (!lexer_next(p->lex)) return NULL;
829
830
                for (int64_t i = start; i <= end; i++) {
831
                    goon_list_push(p->ctx, list, goon_int(p->ctx, i));
832
                }
833
            } else {
834
                goon_list_push(p->ctx, list, goon_int(p->ctx, start));
835
            }
836
        } else {
837
            Goon_Value *item = parse_expr(p);
838
            if (!item) return NULL;
839
            goon_list_push(p->ctx, list, item);
840
        }
841
842
        if (p->lex->current.type == TOK_COMMA) {
843
            if (!lexer_next(p->lex)) return NULL;
844
        }
845
    }
846
847
    if (p->lex->current.type != TOK_RBRACKET) {
848
        lexer_set_error(p->lex, "expected ]");
849
        return NULL;
850
    }
851
852
    if (!lexer_next(p->lex)) return NULL;
853
    return list;
854
}
855
856
static Goon_Value *parse_import(Parser *p) {
857
    if (!lexer_next(p->lex)) return NULL;
858
859
    if (p->lex->current.type != TOK_LPAREN) {
860
        lexer_set_error(p->lex,"expected ( after import");
861
        return NULL;
862
    }
863
864
    if (!lexer_next(p->lex)) return NULL;
865
866
    if (p->lex->current.type != TOK_STRING) {
867
        lexer_set_error(p->lex,"expected string path in import");
868
        return NULL;
869
    }
870
871
    char *path = strdup(p->lex->current.data.string);
872
    if (!lexer_next(p->lex)) { free(path); return NULL; }
873
874
    if (p->lex->current.type != TOK_RPAREN) {
875
        lexer_set_error(p->lex,"expected ) after import path");
876
        free(path);
877
        return NULL;
878
    }
879
880
    if (!lexer_next(p->lex)) { free(path); return NULL; }
881
882
    char full_path[1024];
883
    if (path[0] == '.' && p->ctx->base_path) {
884
        char *base_copy = strdup(p->ctx->base_path);
885
        char *dir = dirname(base_copy);
886
        snprintf(full_path, sizeof(full_path), "%s/%s", dir, path);
887
        free(base_copy);
888
    } else if (p->ctx->base_path && path[0] != '/') {
889
        char *base_copy = strdup(p->ctx->base_path);
890
        char *dir = dirname(base_copy);
891
        snprintf(full_path, sizeof(full_path), "%s/%s", dir, path);
892
        free(base_copy);
893
    } else {
894
        snprintf(full_path, sizeof(full_path), "%s", path);
895
    }
896
897
    size_t plen = strlen(full_path);
898
    if (plen < 5 || strcmp(full_path + plen - 5, ".goon") != 0) {
899
        strncat(full_path, ".goon", sizeof(full_path) - plen - 1);
900
    }
901
902
    free(path);
903
904
    FILE *f = fopen(full_path, "r");
905
    if (!f) {
906
        lexer_set_error(p->lex,"could not open import file");
907
        return NULL;
908
    }
909
910
    fseek(f, 0, SEEK_END);
911
    long size = ftell(f);
912
    fseek(f, 0, SEEK_SET);
913
914
    char *source = malloc(size + 1);
915
    if (!source) {
916
        fclose(f);
917
        return NULL;
918
    }
919
920
    size_t read_size = fread(source, 1, size, f);
921
    source[read_size] = '\0';
922
    fclose(f);
923
924
    char *old_base = p->ctx->base_path;
925
    p->ctx->base_path = strdup(full_path);
926
927
    Lexer import_lex;
928
    lexer_init(&import_lex, source);
929
    Parser import_parser;
930
    import_parser.ctx = p->ctx;
931
    import_parser.lex = &import_lex;
932
933
    if (!lexer_next(&import_lex)) {
934
        free(source);
935
        free(p->ctx->base_path);
936
        p->ctx->base_path = old_base;
937
        return NULL;
938
    }
939
940
    Goon_Value *result = NULL;
941
    while (import_lex.current.type != TOK_EOF) {
942
        result = parse_expr(&import_parser);
943
        if (!result) break;
944
    }
945
946
    free(source);
947
    free(p->ctx->base_path);
948
    p->ctx->base_path = old_base;
949
950
    return result;
951
}
952
953
static Goon_Value *parse_call(Parser *p, const char *name) {
954
    Goon_Value *fn = lookup(p->ctx, name);
955
956
    if (!lexer_next(p->lex)) return NULL;
957
958
    Goon_Value *args[16];
959
    size_t argc = 0;
960
961
    while (p->lex->current.type != TOK_RPAREN && p->lex->current.type != TOK_EOF && argc < 16) {
962
        args[argc++] = parse_expr(p);
963
        if (p->lex->current.type == TOK_COMMA) {
964
            if (!lexer_next(p->lex)) return NULL;
965
        }
966
    }
967
968
    if (p->lex->current.type != TOK_RPAREN) {
969
        lexer_set_error(p->lex,"expected )");
970
        return NULL;
971
    }
972
973
    if (!lexer_next(p->lex)) return NULL;
974
975
    if (fn && fn->type == GOON_BUILTIN) {
976
        return fn->data.builtin(p->ctx, args, argc);
977
    }
978
979
    if (fn && fn->type == GOON_LAMBDA) {
980
        if (argc != fn->data.lambda.param_count) {
981
            lexer_set_error(p->lex, "wrong number of arguments");
982
            return NULL;
983
        }
984
985
        Goon_Binding *old_env = p->ctx->env;
986
        p->ctx->env = fn->data.lambda.env;
987
988
        for (size_t i = 0; i < argc; i++) {
989
            define(p->ctx, fn->data.lambda.params[i], args[i]);
990
        }
991
992
        Lexer body_lex;
993
        lexer_init(&body_lex, fn->data.lambda.body);
994
        Parser body_parser;
995
        body_parser.ctx = p->ctx;
996
        body_parser.lex = &body_lex;
997
998
        if (!lexer_next(&body_lex)) {
999
            p->ctx->env = old_env;
1000
            return NULL;
1001
        }
1002
1003
        Goon_Value *result = parse_expr(&body_parser);
1004
1005
        p->ctx->env = old_env;
1006
        return result ? result : goon_nil(p->ctx);
1007
    }
1008
1009
    return goon_nil(p->ctx);
1010
}
1011
1012
static Goon_Value *parse_primary(Parser *p) {
1013
    Token tok = p->lex->current;
1014
1015
    switch (tok.type) {
1016
        case TOK_INT: {
1017
            Goon_Value *val = goon_int(p->ctx, tok.data.integer);
1018
            if (!lexer_next(p->lex)) return NULL;
1019
            return val;
1020
        }
1021
1022
        case TOK_STRING: {
1023
            Goon_Value *val = interpolate_string(p->ctx, tok.data.string);
1024
            if (!lexer_next(p->lex)) return NULL;
1025
            return val;
1026
        }
1027
1028
        case TOK_TRUE: {
1029
            Goon_Value *val = goon_bool(p->ctx, true);
1030
            if (!lexer_next(p->lex)) return NULL;
1031
            return val;
1032
        }
1033
1034
        case TOK_FALSE: {
1035
            Goon_Value *val = goon_bool(p->ctx, false);
1036
            if (!lexer_next(p->lex)) return NULL;
1037
            return val;
1038
        }
1039
1040
        case TOK_IDENT: {
1041
            char *name = strdup(tok.data.string);
1042
            if (!lexer_next(p->lex)) { free(name); return NULL; }
1043
1044
            if (p->lex->current.type == TOK_LPAREN) {
1045
                Goon_Value *result = parse_call(p, name);
1046
                free(name);
1047
                return result;
1048
            }
1049
1050
            if (p->lex->current.type == TOK_DOT) {
1051
                Goon_Value *val = lookup(p->ctx, name);
1052
                free(name);
1053
1054
                while (p->lex->current.type == TOK_DOT) {
1055
                    if (!lexer_next(p->lex)) return NULL;
1056
                    if (p->lex->current.type != TOK_IDENT) {
1057
                        lexer_set_error(p->lex,"expected field name after .");
1058
                        return NULL;
1059
                    }
1060
                    char *field = p->lex->current.data.string;
1061
                    val = goon_record_get(val, field);
1062
                    if (!lexer_next(p->lex)) return NULL;
1063
                }
1064
                return val ? val : goon_nil(p->ctx);
1065
            }
1066
1067
            Goon_Value *val = lookup(p->ctx, name);
1068
            free(name);
1069
            return val ? val : goon_nil(p->ctx);
1070
        }
1071
1072
        case TOK_LBRACE:
1073
            return parse_record(p);
1074
1075
        case TOK_LBRACKET:
1076
            return parse_list(p);
1077
1078
        case TOK_IMPORT:
1079
            return parse_import(p);
1080
1081
        case TOK_LPAREN: {
1082
            Lexer_State saved;
1083
            lexer_save(p->lex, &saved);
1084
1085
            if (!lexer_next(p->lex)) { lexer_state_free(&saved); return NULL; }
1086
1087
            char *params[16];
1088
            size_t param_count = 0;
1089
            bool is_lambda = true;
1090
1091
            if (p->lex->current.type == TOK_RPAREN) {
1092
                if (!lexer_next(p->lex)) { lexer_state_free(&saved); return NULL; }
1093
                is_lambda = (p->lex->current.type == TOK_ARROW);
1094
            } else if (p->lex->current.type == TOK_IDENT) {
1095
                while (is_lambda && param_count < 16) {
1096
                    if (p->lex->current.type != TOK_IDENT) {
1097
                        is_lambda = false;
1098
                        break;
1099
                    }
1100
                    params[param_count++] = strdup(p->lex->current.data.string);
1101
                    if (!lexer_next(p->lex)) {
1102
                        for (size_t i = 0; i < param_count; i++) free(params[i]);
1103
                        lexer_state_free(&saved);
1104
                        return NULL;
1105
                    }
1106
                    if (p->lex->current.type == TOK_COMMA) {
1107
                        if (!lexer_next(p->lex)) {
1108
                            for (size_t i = 0; i < param_count; i++) free(params[i]);
1109
                            lexer_state_free(&saved);
1110
                            return NULL;
1111
                        }
1112
                    } else if (p->lex->current.type == TOK_RPAREN) {
1113
                        if (!lexer_next(p->lex)) {
1114
                            for (size_t i = 0; i < param_count; i++) free(params[i]);
1115
                            lexer_state_free(&saved);
1116
                            return NULL;
1117
                        }
1118
                        is_lambda = (p->lex->current.type == TOK_ARROW);
1119
                        break;
1120
                    } else {
1121
                        is_lambda = false;
1122
                        break;
1123
                    }
1124
                }
1125
            } else {
1126
                is_lambda = false;
1127
            }
1128
1129
            if (is_lambda) {
1130
                lexer_state_free(&saved);
1131
                if (!lexer_next(p->lex)) {
1132
                    for (size_t i = 0; i < param_count; i++) free(params[i]);
1133
                    return NULL;
1134
                }
1135
                size_t body_start = p->lex->token_start;
1136
                Goon_Value *body_val = parse_expr(p);
1137
                if (!body_val) {
1138
                    for (size_t i = 0; i < param_count; i++) free(params[i]);
1139
                    return NULL;
1140
                }
1141
                size_t body_end = p->lex->token_start;
1142
                char *body_src = strdup_range(p->lex->src + body_start, body_end - body_start);
1143
1144
                Goon_Value *lambda = goon_lambda(p->ctx, params, param_count, body_src, p->ctx->env);
1145
                free(body_src);
1146
                for (size_t i = 0; i < param_count; i++) free(params[i]);
1147
                return lambda;
1148
            } else {
1149
                for (size_t i = 0; i < param_count; i++) free(params[i]);
1150
                lexer_restore(p->lex, &saved);
1151
1152
                if (!lexer_next(p->lex)) return NULL;
1153
                Goon_Value *val = parse_expr(p);
1154
                if (p->lex->current.type != TOK_RPAREN) {
1155
                    lexer_set_error(p->lex, "expected )");
1156
                    return NULL;
1157
                }
1158
                if (!lexer_next(p->lex)) return NULL;
1159
                return val;
1160
            }
1161
        }
1162
1163
        default:
1164
            return NULL;
1165
    }
1166
}
1167
1168
static Goon_Value *parse_expr(Parser *p) {
1169
    if (p->lex->current.type == TOK_LET) {
1170
        if (!lexer_next(p->lex)) return NULL;
1171
1172
        if (p->lex->current.type != TOK_IDENT) {
1173
            lexer_set_error(p->lex,"expected identifier after let");
1174
            return NULL;
1175
        }
1176
1177
        char *name = strdup(p->lex->current.data.string);
1178
        if (!lexer_next(p->lex)) { free(name); return NULL; }
1179
1180
        if (p->lex->current.type == TOK_COLON) {
1181
            if (!lexer_next(p->lex)) { free(name); return NULL; }
1182
            if (!lexer_next(p->lex)) { free(name); return NULL; }
1183
        }
1184
1185
        if (p->lex->current.type != TOK_EQUALS) {
1186
            lexer_set_error(p->lex,"expected = in let binding");
1187
            free(name);
1188
            return NULL;
1189
        }
1190
1191
        if (!lexer_next(p->lex)) { free(name); return NULL; }
1192
1193
        Goon_Value *value = parse_expr(p);
1194
        if (!value) { free(name); return NULL; }
1195
1196
        define(p->ctx, name, value);
1197
        free(name);
1198
1199
        if (p->lex->current.type != TOK_SEMICOLON) {
1200
            lexer_set_error(p->lex, "expected ; after let binding");
1201
            return NULL;
1202
        }
1203
        if (!lexer_next(p->lex)) return NULL;
1204
1205
        return value;
1206
    }
1207
1208
    if (p->lex->current.type == TOK_IF) {
1209
        if (!lexer_next(p->lex)) return NULL;
1210
1211
        Goon_Value *cond = parse_expr(p);
1212
        if (!cond) return NULL;
1213
1214
        if (p->lex->current.type != TOK_THEN) {
1215
            lexer_set_error(p->lex,"expected 'then' after if condition");
1216
            return NULL;
1217
        }
1218
1219
        if (!lexer_next(p->lex)) return NULL;
1220
1221
        Goon_Value *then_val = parse_expr(p);
1222
        if (!then_val) return NULL;
1223
1224
        if (p->lex->current.type != TOK_ELSE) {
1225
            lexer_set_error(p->lex,"expected 'else' after then branch");
1226
            return NULL;
1227
        }
1228
1229
        if (!lexer_next(p->lex)) return NULL;
1230
1231
        Goon_Value *else_val = parse_expr(p);
1232
        if (!else_val) return NULL;
1233
1234
        return goon_to_bool(cond) ? then_val : else_val;
1235
    }
1236
1237
    Goon_Value *val = parse_primary(p);
1238
    if (!val) return NULL;
1239
1240
    if (p->lex->current.type == TOK_QUESTION) {
1241
        if (!lexer_next(p->lex)) return NULL;
1242
1243
        Goon_Value *then_val = parse_expr(p);
1244
        if (!then_val) return NULL;
1245
1246
        if (p->lex->current.type != TOK_COLON) {
1247
            lexer_set_error(p->lex,"expected : in ternary");
1248
            return NULL;
1249
        }
1250
1251
        if (!lexer_next(p->lex)) return NULL;
1252
1253
        Goon_Value *else_val = parse_expr(p);
1254
        if (!else_val) return NULL;
1255
1256
        return goon_to_bool(val) ? then_val : else_val;
1257
    }
1258
1259
    return val;
1260
}
1261
1262
static void clear_error(Goon_Ctx *ctx) {
1263
    if (ctx->error.message) { free(ctx->error.message); ctx->error.message = NULL; }
1264
    if (ctx->error.file) { free(ctx->error.file); ctx->error.file = NULL; }
1265
    if (ctx->error.source_line) { free(ctx->error.source_line); ctx->error.source_line = NULL; }
1266
    ctx->error.line = 0;
1267
    ctx->error.col = 0;
1268
}
1269
1270
static char *get_source_line(const char *src, size_t line_start) {
1271
    const char *start = src + line_start;
1272
    const char *end = start;
1273
    while (*end && *end != '\n') end++;
1274
    return strdup_range(start, end - start);
1275
}
1276
1277
Goon_Ctx *goon_create(void) {
1278
    Goon_Ctx *ctx = malloc(sizeof(Goon_Ctx));
1279
    if (!ctx) return NULL;
1280
    ctx->env = NULL;
1281
    ctx->values = NULL;
1282
    ctx->fields = NULL;
1283
    ctx->error.message = NULL;
1284
    ctx->error.file = NULL;
1285
    ctx->error.line = 0;
1286
    ctx->error.col = 0;
1287
    ctx->error.source_line = NULL;
1288
    ctx->base_path = NULL;
1289
    ctx->userdata = NULL;
1290
1291
    goon_register(ctx, "map", builtin_map);
1292
1293
    return ctx;
1294
}
1295
1296
void goon_destroy(Goon_Ctx *ctx) {
1297
    if (!ctx) return;
1298
1299
    Goon_Binding *b = ctx->env;
1300
    while (b) {
1301
        Goon_Binding *next = b->next;
1302
        free(b->name);
1303
        free(b);
1304
        b = next;
1305
    }
1306
1307
    Goon_Value *v = ctx->values;
1308
    while (v) {
1309
        Goon_Value *next = v->next_alloc;
1310
        if (v->type == GOON_STRING && v->data.string) {
1311
            free(v->data.string);
1312
        } else if (v->type == GOON_LIST && v->data.list.items) {
1313
            free(v->data.list.items);
1314
        } else if (v->type == GOON_LAMBDA) {
1315
            if (v->data.lambda.params) {
1316
                for (size_t i = 0; i < v->data.lambda.param_count; i++) {
1317
                    free(v->data.lambda.params[i]);
1318
                }
1319
                free(v->data.lambda.params);
1320
            }
1321
            if (v->data.lambda.body) {
1322
                free(v->data.lambda.body);
1323
            }
1324
        }
1325
        free(v);
1326
        v = next;
1327
    }
1328
1329
    Goon_Record_Field *f = ctx->fields;
1330
    while (f) {
1331
        Goon_Record_Field *next = f->next;
1332
        if (f->key) free(f->key);
1333
        free(f);
1334
        f = next;
1335
    }
1336
1337
    clear_error(ctx);
1338
    if (ctx->base_path) free(ctx->base_path);
1339
    free(ctx);
1340
}
1341
1342
void goon_set_userdata(Goon_Ctx *ctx, void *userdata) {
1343
    ctx->userdata = userdata;
1344
}
1345
1346
void *goon_get_userdata(Goon_Ctx *ctx) {
1347
    return ctx->userdata;
1348
}
1349
1350
void goon_register(Goon_Ctx *ctx, const char *name, Goon_Builtin_Fn fn) {
1351
    Goon_Value *val = alloc_value(ctx);
1352
    if (!val) return;
1353
    val->type = GOON_BUILTIN;
1354
    val->data.builtin = fn;
1355
    define(ctx, name, val);
1356
}
1357
1358
static Goon_Value *last_result = NULL;
1359
1360
static void set_error_from_lexer(Goon_Ctx *ctx, Lexer *lex, const char *source) {
1361
    clear_error(ctx);
1362
    if (lex->error) {
1363
        ctx->error.message = lex->error;
1364
        lex->error = NULL;
1365
    }
1366
    ctx->error.line = lex->error_line > 0 ? lex->error_line : lex->line;
1367
    ctx->error.col = lex->error_col > 0 ? lex->error_col : lex->col;
1368
    if (ctx->base_path) {
1369
        ctx->error.file = strdup(ctx->base_path);
1370
    }
1371
    size_t line_start = lex->line_start;
1372
    if (lex->error_line > 0 && lex->error_line < lex->line) {
1373
        const char *p = source;
1374
        size_t cur_line = 1;
1375
        while (*p && cur_line < lex->error_line) {
1376
            if (*p == '\n') cur_line++;
1377
            p++;
1378
        }
1379
        line_start = p - source;
1380
    }
1381
    ctx->error.source_line = get_source_line(source, line_start);
1382
}
1383
1384
bool goon_load_string(Goon_Ctx *ctx, const char *source) {
1385
    Lexer lex;
1386
    lexer_init(&lex, source);
1387
1388
    Parser parser;
1389
    parser.ctx = ctx;
1390
    parser.lex = &lex;
1391
1392
    if (!lexer_next(&lex)) {
1393
        set_error_from_lexer(ctx, &lex, source);
1394
        return false;
1395
    }
1396
1397
    last_result = NULL;
1398
1399
    while (lex.current.type != TOK_EOF) {
1400
        Goon_Value *expr = parse_expr(&parser);
1401
        if (!expr) {
1402
            set_error_from_lexer(ctx, &lex, source);
1403
            return false;
1404
        }
1405
        last_result = expr;
1406
    }
1407
1408
    return true;
1409
}
1410
1411
bool goon_load_file(Goon_Ctx *ctx, const char *path) {
1412
    FILE *f = fopen(path, "r");
1413
    if (!f) {
1414
        clear_error(ctx);
1415
        ctx->error.message = strdup("could not open file");
1416
        ctx->error.file = strdup(path);
1417
        return false;
1418
    }
1419
1420
    fseek(f, 0, SEEK_END);
1421
    long size = ftell(f);
1422
    fseek(f, 0, SEEK_SET);
1423
1424
    char *source = malloc(size + 1);
1425
    if (!source) {
1426
        fclose(f);
1427
        clear_error(ctx);
1428
        ctx->error.message = strdup("out of memory");
1429
        return false;
1430
    }
1431
1432
    size_t read_size = fread(source, 1, size, f);
1433
    source[read_size] = '\0';
1434
    fclose(f);
1435
1436
    ctx->base_path = strdup(path);
1437
1438
    bool result = goon_load_string(ctx, source);
1439
    free(source);
1440
    return result;
1441
}
1442
1443
const char *goon_get_error(Goon_Ctx *ctx) {
1444
    return ctx->error.message;
1445
}
1446
1447
const Goon_Error *goon_get_error_info(Goon_Ctx *ctx) {
1448
    if (!ctx->error.message) return NULL;
1449
    return &ctx->error;
1450
}
1451
1452
void goon_error_print(const Goon_Error *err) {
1453
    if (!err || !err->message) return;
1454
1455
    fprintf(stderr, "error: %s\n", err->message);
1456
1457
    if (err->file && err->line > 0) {
1458
        fprintf(stderr, "  --> %s:%zu:%zu\n", err->file, err->line, err->col);
1459
    } else if (err->line > 0) {
1460
        fprintf(stderr, "  --> %zu:%zu\n", err->line, err->col);
1461
    }
1462
1463
    if (err->source_line) {
1464
        fprintf(stderr, "   |\n");
1465
        fprintf(stderr, "%3zu| %s\n", err->line, err->source_line);
1466
        fprintf(stderr, "   |");
1467
        for (size_t i = 0; i < err->col; i++) {
1468
            fprintf(stderr, " ");
1469
        }
1470
        fprintf(stderr, "^\n");
1471
    }
1472
}
1473
1474
Goon_Value *goon_eval_result(Goon_Ctx *ctx) {
1475
    (void)ctx;
1476
    return last_result;
1477
}
1478
1479
typedef struct {
1480
    char *buf;
1481
    size_t len;
1482
    size_t cap;
1483
} String_Builder;
1484
1485
static void sb_init(String_Builder *sb) {
1486
    sb->buf = malloc(256);
1487
    sb->len = 0;
1488
    sb->cap = 256;
1489
    if (sb->buf) sb->buf[0] = '\0';
1490
}
1491
1492
static void sb_append(String_Builder *sb, const char *str) {
1493
    if (!sb->buf) return;
1494
    size_t add_len = strlen(str);
1495
    while (sb->len + add_len + 1 > sb->cap) {
1496
        sb->cap *= 2;
1497
        sb->buf = realloc(sb->buf, sb->cap);
1498
        if (!sb->buf) return;
1499
    }
1500
    memcpy(sb->buf + sb->len, str, add_len + 1);
1501
    sb->len += add_len;
1502
}
1503
1504
static void sb_append_char(String_Builder *sb, char c) {
1505
    char tmp[2] = {c, '\0'};
1506
    sb_append(sb, tmp);
1507
}
1508
1509
static void json_escape_string(String_Builder *sb, const char *str) {
1510
    sb_append_char(sb, '"');
1511
    while (*str) {
1512
        switch (*str) {
1513
            case '"':  sb_append(sb, "\\\""); break;
1514
            case '\\': sb_append(sb, "\\\\"); break;
1515
            case '\n': sb_append(sb, "\\n"); break;
1516
            case '\r': sb_append(sb, "\\r"); break;
1517
            case '\t': sb_append(sb, "\\t"); break;
1518
            default:   sb_append_char(sb, *str); break;
1519
        }
1520
        str++;
1521
    }
1522
    sb_append_char(sb, '"');
1523
}
1524
1525
static void value_to_json(String_Builder *sb, Goon_Value *val, int indent, int depth);
1526
1527
static void append_indent(String_Builder *sb, int indent, int depth) {
1528
    if (indent <= 0) return;
1529
    for (int i = 0; i < indent * depth; i++) {
1530
        sb_append_char(sb, ' ');
1531
    }
1532
}
1533
1534
static void value_to_json(String_Builder *sb, Goon_Value *val, int indent, int depth) {
1535
    if (!val || val->type == GOON_NIL) {
1536
        sb_append(sb, "null");
1537
        return;
1538
    }
1539
1540
    switch (val->type) {
1541
        case GOON_BOOL:
1542
            sb_append(sb, val->data.boolean ? "true" : "false");
1543
            break;
1544
1545
        case GOON_INT: {
1546
            char num[32];
1547
            snprintf(num, sizeof(num), "%ld", val->data.integer);
1548
            sb_append(sb, num);
1549
            break;
1550
        }
1551
1552
        case GOON_STRING:
1553
            json_escape_string(sb, val->data.string);
1554
            break;
1555
1556
        case GOON_LIST: {
1557
            sb_append_char(sb, '[');
1558
            if (indent > 0 && val->data.list.len > 0) sb_append_char(sb, '\n');
1559
            for (size_t i = 0; i < val->data.list.len; i++) {
1560
                if (indent > 0) append_indent(sb, indent, depth + 1);
1561
                value_to_json(sb, val->data.list.items[i], indent, depth + 1);
1562
                if (i < val->data.list.len - 1) sb_append_char(sb, ',');
1563
                if (indent > 0) sb_append_char(sb, '\n');
1564
            }
1565
            if (indent > 0 && val->data.list.len > 0) append_indent(sb, indent, depth);
1566
            sb_append_char(sb, ']');
1567
            break;
1568
        }
1569
1570
        case GOON_RECORD: {
1571
            sb_append_char(sb, '{');
1572
            Goon_Record_Field *f = val->data.record.fields;
1573
            size_t count = 0;
1574
            Goon_Record_Field *tmp = f;
1575
            while (tmp) { count++; tmp = tmp->next; }
1576
            if (indent > 0 && count > 0) sb_append_char(sb, '\n');
1577
            size_t idx = 0;
1578
            while (f) {
1579
                if (indent > 0) append_indent(sb, indent, depth + 1);
1580
                json_escape_string(sb, f->key);
1581
                sb_append_char(sb, ':');
1582
                if (indent > 0) sb_append_char(sb, ' ');
1583
                value_to_json(sb, f->value, indent, depth + 1);
1584
                if (f->next) sb_append_char(sb, ',');
1585
                if (indent > 0) sb_append_char(sb, '\n');
1586
                f = f->next;
1587
                idx++;
1588
            }
1589
            if (indent > 0 && count > 0) append_indent(sb, indent, depth);
1590
            sb_append_char(sb, '}');
1591
            break;
1592
        }
1593
1594
        default:
1595
            sb_append(sb, "null");
1596
            break;
1597
    }
1598
}
1599
1600
char *goon_to_json(Goon_Value *val) {
1601
    String_Builder sb;
1602
    sb_init(&sb);
1603
    value_to_json(&sb, val, 0, 0);
1604
    return sb.buf;
1605
}
1606
1607
char *goon_to_json_pretty(Goon_Value *val, int indent) {
1608
    String_Builder sb;
1609
    sb_init(&sb);
1610
    value_to_json(&sb, val, indent, 0);
1611
    return sb.buf;
1612
}