Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
reader.c
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <lxsameer@lxsameer.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include "serene/ir/reader.h"
20
21#include <stdio.h>
22#include <string.h>
23
24#include "serene/rt/context.h"
25#include "serene/rt/core.h"
26#include "serene/rt/engine.h"
27#include "serene/rt/errors.h"
28#include "serene/rt/fiber.h"
31#include "serene/utils.h"
32#include "serene/vendor/utf8.h"
33
34#define UNEXPECTED_CHAR_MSG_LEN 48
35#define CHAR_BUFF_SIZE 5
36
37#define C_LAMBDA 0x03BB
38
39#define DEBUG_BUF_MAX_SIZE 4096
40#define METADATA_STR_DEBUG_SIZE 48
41
42static inline bool is_atom_char(utf8_int32_t c);
43
44// -----------------------------------------------------------------------------
45// Utilities
46// -----------------------------------------------------------------------------
47
48static inline utf8_int32_t peek(srn_sexp_parser_t *p) {
49 utf8_int32_t c;
50 utf8codepoint((const utf8_int8_t *)p->buffer + p->pos, &c);
51 return c;
52}
53
54static inline utf8_int32_t advance(srn_sexp_parser_t *p) {
55 utf8_int32_t c;
56 const utf8_int8_t *next = utf8codepoint((const utf8_int8_t *)p->buffer + p->pos, &c);
57 p->pos = (size_t)(next - (const utf8_int8_t *)p->buffer);
58 return c;
59}
60
61/// The codepoint one past the current one, without moving `p`. `peek` only ever reads
62/// at `p->pos` itself, so this is what tells a signed number ('-5') from a bare symbol
63/// ('-'): decode the current codepoint just to find where it ends, then decode the one
64/// after it.
65static inline utf8_int32_t peek_next(srn_sexp_parser_t *p) {
66 utf8_int32_t current;
67 const utf8_int8_t *after_current =
68 utf8codepoint((const utf8_int8_t *)p->buffer + p->pos, &current);
69 // The terminator is the last byte the reader may look at, there is nothing
70 // after it to decode.
71 if (current == '\0') {
72 return '\0';
73 }
74 utf8_int32_t next;
75 utf8codepoint(after_current, &next);
76 return next;
77}
78
79static inline bool is_whitespace(utf8_int32_t c) {
80 return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') != 0;
81}
82
83/* static inline bool is_alpha(utf8_int32_t c) { */
84/* return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) != 0; */
85/* } */
86
87static inline bool is_digit(utf8_int32_t c) { return c >= '0' && c <= '9'; }
88
89static inline bool is_at_end(srn_sexp_parser_t *p) { return peek(p) == '\0'; }
90
91static inline void read_comment(srn_sexp_parser_t *p) {
92 while (!is_at_end(p) && peek(p) != '\n') {
93 advance(p);
94 }
95}
96
97static inline void skip_whitespace(srn_sexp_parser_t *p) {
98 while (!is_at_end(p)) {
99 if (is_whitespace(peek(p))) {
100 advance(p);
101 } else if (peek(p) == ';') {
102 read_comment(p);
103 } else {
104 break;
105 }
106 }
107}
108
109static inline bool is_atom_start(utf8_int32_t c) { return (!is_digit(c) && is_atom_char(c)) != 0; }
110
111static inline bool is_number_start(utf8_int32_t c, utf8_int32_t next) {
112 return (is_digit(c) || ((c == '-' || c == '+') && is_digit(next))) != 0;
113}
114
115static inline bool is_atom_char(utf8_int32_t c) {
116 if (c == '\0' || is_whitespace(c)) {
117 return false;
118 }
119 switch (c) {
120 case '(':
121 case ')':
122 case '[':
123 case ']':
124 case '{':
125 case '}':
126 case '"':
127 case ';':
128 return false;
129 default:
130 return true;
131 }
132}
133
134static inline srn_error_t *
135parse_error(srn_sexp_parser_t *parser, const utf8_int32_t c, const char *msg) {
137 PANIC_IF_NULL(loc);
138 loc->buffer_id = parser->buffer_id;
139 loc->offset = parser->pos;
140 loc->len = 1;
141
142 srn_metadata_t *metadata = ALLOC(parser->ctx, srn_metadata_t);
143 PANIC_IF_NULL(metadata);
144 metadata->loc = loc;
145 metadata->raw_expr = nullptr;
146 metadata->ir_type = IR_TYPE_IR_READER;
147
148 char *error_msg;
149
150 if (msg == nullptr) {
151 char char_buf[CHAR_BUFF_SIZE] = {0};
152 error_msg = ALLOCN(parser->ctx, char, UNEXPECTED_CHAR_MSG_LEN);
153 utf8catcodepoint(char_buf, c, sizeof(char_buf) - 1);
154 SNPRINTF(
155 error_msg, UNEXPECTED_CHAR_MSG_LEN - 1, "Unexpected char '%s' at offset %u", char_buf,
156 parser->pos
157 );
158 } else {
159 size_t len = strlen(msg);
160 error_msg = ALLOCN_OR_PANIC(parser->ctx, char, len + 1);
161 memcpy(error_msg, msg, len + 1);
162 }
163
164 srn_error_t *err = ALLOC(parser->ctx, srn_error_t);
165 PANIC_IF_NULL(err);
166 err->layer = SRN_ERR_PARSE_LAYER;
167 err->tag = IR_PARSE_ERROR;
168 err->msg = error_msg;
169 err->as.metadata = metadata;
170 return err;
171}
172
173// -----------------------------------------------------------------------------
174// Node builders
175// -----------------------------------------------------------------------------
177 // NOTE: We assume name is allocated in an block, we just take it
178 PANIC_IF_NULL(name);
181
182 atom->name = (char *)name;
183 n->as.atom = atom;
184 return n;
185}
186
188 // NOTE: We assume data is allocated in an block, we just take it
191
192 s->data = (char *)data;
193 n->as.string = s;
194
195 return n;
196}
197
199 // NOTE: We assume text is allocated in a block, we just take it
200 PANIC_IF_NULL(text);
203
204 num->text = (char *)text;
205 n->as.num = num;
206
207 return n;
208}
209
212 PANIC_IF_NULL(head);
213
216
217 c->head = head;
218 c->tail = tail;
219 n->as.cons = c;
220
221 return n;
222}
223
227
228 l->head = nullptr;
229 l->len = 0;
230 n->as.list = l;
231
232 return n;
233}
235 auto n = ALLOC_OR_PANIC(ctx, srn_sexp_node_t);
236 n->type = type;
237 n->metadata = nullptr;
238 return n;
239}
240
241// -----------------------------------------------------------------------------
242// Readers
243// -----------------------------------------------------------------------------
244
245// Note to myself. A nested call (read_expr and what it dispatches to) reports a parse
246// error by writing through it. That write has to reach the caller, so it must be `srn_error_t **`,
247// not `srn_error_t *`. A plain pointer is passed by value in C, so an assignment like
248// `err = parse_error(...)` inside the callee would only change the callee's own local copy and the
249// caller's `err` would never see it.
251 auto start = parser->pos;
252
253 advance(parser); // consume '('
254
255 skip_whitespace(parser);
256
257 auto list = srn_sexp_make_list(parser->ctx);
258 srn_sexp_node_t *current_tail = nullptr;
259
260 while (!is_at_end(parser) && peek(parser) != ')') {
261 srn_sexp_node_t *elem = read_expr(parser, err);
262
263 if (*err != nullptr) {
264 // We have a parse error, the error is already set via `err`
265 return nullptr;
266 }
267
268 // Since elem might be the last element in the list tail is nullptr
269 auto cons = srn_sexp_make_cons(parser->ctx, elem, nullptr);
270
271 if (list->as.list->head == nullptr) {
272 // empty list
273 list->as.list->head = cons;
274 } else {
275 current_tail->as.cons->tail = cons;
276 }
277
278 current_tail = cons;
279 list->as.list->len++;
280
281 skip_whitespace(parser);
282 }
283
284 if (is_at_end(parser)) {
285 *err = parse_error(parser, '\0', "Unterminated list, expected ')'");
286 return nullptr;
287 }
288
289 advance(parser); // consume ')'
290
292 PANIC_IF_NULL(loc);
293 loc->buffer_id = parser->buffer_id;
294 loc->offset = start;
295 loc->len = parser->pos - start;
296
297 srn_metadata_t *meta = ALLOC(parser->ctx, srn_metadata_t);
298 meta->loc = loc;
299 meta->raw_expr = list;
301 list->metadata = meta;
302
303 return list;
304}
305
307 auto start = parser->pos;
308
309 advance(parser); // consume '['
310 array_list_t *elems = ALLOC(parser->ctx, array_list_t);
311 PANIC_IF_NULL(elems);
312 *elems = array_list_empty(parser->ctx);
313
314 skip_whitespace(parser);
315
316 while (!is_at_end(parser) && peek(parser) != ']') {
317 srn_sexp_node_t *elem = read_expr(parser, err);
318
319 if (*err != nullptr) {
320 // We have a parse error
321 return nullptr;
322 }
323
324 srn_error_t *push_err = array_list_push(elems, elem);
325 if (push_err != nullptr) {
326 *err = push_err;
327 return nullptr;
328 }
329
330 skip_whitespace(parser);
331 }
332
333 if (is_at_end(parser)) {
334 *err = parse_error(parser, '\0', "Unterminated list, expected ')'");
335 return nullptr;
336 }
337
338 advance(parser); // consume ']'
339
341 PANIC_IF_NULL(loc);
342 loc->buffer_id = parser->buffer_id;
343 loc->offset = start;
344 loc->len = parser->pos - start;
345
346 srn_sexp_node_t *node = ALLOC(parser->ctx, srn_sexp_node_t);
347 PANIC_IF_NULL(node);
348
349 srn_metadata_t *meta = ALLOC(parser->ctx, srn_metadata_t);
350 meta->loc = loc;
351 meta->raw_expr = node;
353
354 node->type = SRN_SEXP_SEQ;
355 node->metadata = meta;
356 node->as.seq = elems;
357
358 return node;
359}
360
362 UNUSED(err);
363 auto start = parser->pos;
364
365 while (!is_at_end(parser) && is_atom_char(peek(parser))) {
366 advance(parser);
367 }
368
369 size_t len = parser->pos - start;
370 char *name = ALLOCN_OR_PANIC(parser->ctx, char, len + 1);
371
372 memcpy(name, (const char *)parser->buffer + start, len);
373 name[len] = '\0';
374
375 auto node = srn_sexp_make_atom(parser->ctx, name);
376
378 PANIC_IF_NULL(loc);
379 loc->buffer_id = parser->buffer_id;
380 loc->offset = start;
381 loc->len = parser->pos - start;
382
383 srn_metadata_t *meta = ALLOC(parser->ctx, srn_metadata_t);
384 PANIC_IF_NULL(meta);
385 meta->loc = loc;
386 meta->raw_expr = node;
388
389 node->metadata = meta;
390
391 return node;
392}
393
394/**
395 * A number literal: an optional leading sign, digits, an optional '.'-separated
396 * fractional part, and an optional 'e'/'E' exponent. Kept as the raw source text (see
397 * srn_sexp_num_t), the reader only recognises the shape.
398 */
400 auto start = parser->pos;
401
402 if (peek(parser) == '-' || peek(parser) == '+') {
403 advance(parser);
404 }
405
406 while (!is_at_end(parser) && is_digit(peek(parser))) {
407 advance(parser);
408 }
409
410 if (peek(parser) == '.' && is_digit(peek_next(parser))) {
411 advance(parser); // consume '.'
412 while (!is_at_end(parser) && is_digit(peek(parser))) {
413 advance(parser);
414 }
415 }
416
417 if (peek(parser) == 'e' || peek(parser) == 'E') {
418 auto mark = parser->pos;
419 advance(parser); // consume 'e'/'E'
420
421 if (peek(parser) == '-' || peek(parser) == '+') {
422 advance(parser);
423 }
424
425 if (is_digit(peek(parser))) {
426 while (!is_at_end(parser) && is_digit(peek(parser))) {
427 advance(parser);
428 }
429 } else {
430 // Not actually an exponent (e.g. "1e" or "1efoo"), back out to before the
431 // 'e'/'E' so it is left for the atom-char check below.
432 parser->pos = mark;
433 }
434 }
435
436 if (!is_at_end(parser) && is_atom_char(peek(parser))) {
437 *err = parse_error(parser, peek(parser), "Malformed number literal");
438 return nullptr;
439 }
440
441 size_t len = parser->pos - start;
442 char *text = ALLOCN_OR_PANIC(parser->ctx, char, len + 1);
443
444 memcpy(text, (const char *)parser->buffer + start, len);
445 text[len] = '\0';
446
447 auto node = srn_sexp_make_num(parser->ctx, text);
448
450 PANIC_IF_NULL(loc);
451 loc->buffer_id = parser->buffer_id;
452 loc->offset = start;
453 loc->len = parser->pos - start;
454
455 srn_metadata_t *meta = ALLOC(parser->ctx, srn_metadata_t);
456 PANIC_IF_NULL(meta);
457 meta->loc = loc;
458 meta->raw_expr = node;
460
461 node->metadata = meta;
462
463 return node;
464}
465
467 auto start = parser->pos;
468
469 advance(parser); // consume opening '"'
470
471 // Upper bound: raw byte count between the quotes can't exceed total buffer length.
472 // Allocate that much, escape sequences only shrink the output.
473 size_t cap = parser->len - parser->pos + 1;
474 char *data = ALLOCN_OR_PANIC(parser->ctx, char, cap);
475 size_t out = 0;
476
477 while (!is_at_end(parser) && peek(parser) != '"') {
478 utf8_int32_t c = advance(parser);
479
480 if (c == '\\') {
481 if (is_at_end(parser)) {
482 break;
483 }
484
485 utf8_int32_t escaped = advance(parser);
486 switch (escaped) {
487 case '"':
488 data[out++] = '"';
489 break;
490 case '\\':
491 data[out++] = '\\';
492 break;
493 case 'n':
494 data[out++] = '\n';
495 break;
496 case 't':
497 data[out++] = '\t';
498 break;
499 case 'r':
500 data[out++] = '\r';
501 break;
502 default: {
503 data[out++] = '\\';
504 // Re-encode the unrecognised codepoint as UTF-8. utf8catcodepoint returns the
505 // advanced write pointer, not a byte count, so the bytes written is the
506 // difference between it and where we started writing.
507 char *dst = data + out;
508 char *next = (char *)utf8catcodepoint((utf8_int8_t *)dst, escaped, cap - out - 1);
509 // The buffer was validated on entry, so a well formed codepoint
510 // re-encodes to the same number of bytes it was read from and always
511 // fits in `cap`.
512 PANIC_IF_NULL(next);
513 out += (size_t)(next - dst);
514 break;
515 }
516 }
517 } else {
518 char *dst = data + out;
519 char *next = (char *)utf8catcodepoint((utf8_int8_t *)dst, c, cap - out - 1);
520 PANIC_IF_NULL(next);
521 out += (size_t)(next - dst);
522 }
523 }
524
525 if (is_at_end(parser)) {
526 *err = parse_error(parser, '\0', "Unterminated string, expected '\"'");
527 return nullptr;
528 }
529
530 advance(parser); // consume closing '"'
531 data[out] = '\0';
532
533 auto node = srn_sexp_make_str(parser->ctx, data);
534
536 PANIC_IF_NULL(loc);
537 loc->buffer_id = parser->buffer_id;
538 loc->offset = start;
539 loc->len = parser->pos - start;
540
541 srn_metadata_t *meta = ALLOC(parser->ctx, srn_metadata_t);
542 PANIC_IF_NULL(meta);
543 meta->loc = loc;
544 meta->raw_expr = node;
546
547 node->metadata = meta;
548
549 return node;
550}
551
553 skip_whitespace(parser);
554
555 utf8_int32_t c = peek(parser);
556
557 if (c == '(') {
558 return read_list(parser, err);
559 }
560
561 // Checked before is_atom_start: a sign like '-' is itself a valid atom-start
562 // character (see is_atom_char), so "-5" would otherwise be read as the atom "-5"
563 // never reaching read_number at all.
564 if (is_number_start(c, peek_next(parser))) {
565 return read_number(parser, err);
566 }
567
568 if (is_atom_start(c)) {
569 return read_atom(parser, err);
570 }
571
572 if (c == '"') {
573 return read_string(parser, err);
574 }
575
576 if (c == '[') {
577 return read_seq(parser, err);
578 }
579
580 *err = parse_error(parser, c, nullptr);
581 return nullptr;
582}
583
585 UNUSED(ctx);
586 reader_fiber_arg_t *fiber_arg = arg;
587
588 srn_sexp_parser_t *parser = fiber_arg->parser;
589
590 auto sexps = array_list_empty(parser->ctx);
591 srn_error_t *err = nullptr;
592
593 // Every decode below trusts the lead byte of a sequence and reads the
594 // continuation bytes without looking for the terminator, so the whole buffer
595 // is checked once here. A sequence cut short by the end of the buffer, a
596 // continuation byte with no lead byte, and an overlong encoding are all
597 // rejected, which also guarantees that re-encoding a codepoint never needs
598 // more bytes than it was read from.
599 const utf8_int8_t *bad = utf8nvalid((const utf8_int8_t *)parser->buffer, parser->len);
600 if (bad != nullptr) {
601 parser->pos = (uint32_t)(bad - (const utf8_int8_t *)parser->buffer);
602 return parse_error(parser, '\0', "Malformed UTF-8 sequence");
603 }
604
605 // Whitespace and comments after the last expression are not an expression,
606 // so they are consumed here rather than handed to `read_expr`.
607 for (;;) {
608 skip_whitespace(parser);
609 if (is_at_end(parser)) {
610 break;
611 }
612
613 auto expr = read_expr(parser, &err);
614
615 if (err != nullptr) {
616 return err;
617 }
618
619 err = array_list_push(&sexps, expr);
620 if (err != nullptr) {
621 return err;
622 }
624 }
625
627 tree->maybe_error = nullptr;
628 tree->reading_context = parser->reading_context;
629 tree->exprs = sexps;
630 fiber_arg->tree = tree;
631
632 return nullptr;
633}
634
636srn_sexp_read_from_string(srn_context_t *ctx, void *reading_context, const char *buf) {
637 // TODO(lxsameer): Extend the source manager to support sparse buffers and add the given
638 // buffer here as a sparse buffer for the given namespace i guess
639 return nullptr;
640}
641
643srn_sexp_read_from_file(srn_context_t *ctx, void *reading_context, const char *path) {
645
646 srn_sm_t *sm = ctx->engine->source_manager;
648
649 if (HAS_ERROR(maybe_id)) {
650 PUSH_AND_MOVE(sm->ctx, maybe_id, *result);
651 return result;
652 }
653
654 srn_sm_buffer_t *buf = srn_sm_get_buffer(sm, maybe_id.id);
655 PANIC_IF_NULL(buf);
656
657 if (buf->data == nullptr || buf->len == 0) {
658 result->maybe_error = nullptr;
659 result->exprs = array_list_empty(ctx);
660 result->reading_context = reading_context;
661 return result;
662 }
663
664 srn_sexp_parser_t parser = {
665 .ctx = ctx,
666 .reading_context = reading_context,
667 .buffer_id = maybe_id.id,
668 .buffer = srn_sm_buffer_data(buf),
669 .len = srn_sm_buffer_len(buf),
670 .pos = 0,
671 };
672
673 reader_fiber_arg_t fiber_arg = {
674 .parser = &parser,
675 // This will be the output
676 .tree = nullptr,
677 };
678
679 srn_fiber_t *fiber = srn_fiber_spawn(ctx, read_from_buffer, &fiber_arg);
680 srn_fiber_result_t fiber_result = srn_fiber_wait_for(fiber);
681
682 if (fiber_result != nullptr) {
683 result->maybe_error = (srn_error_t *)fiber_result;
684 PUSH_TRACE(sm->ctx, *result);
685 return result;
686 }
687
688 result = fiber_arg.tree;
689 return result;
690}
691
692// -----------------------------------------------------------------------------
693// Debug stuff
694// -----------------------------------------------------------------------------
695
696static inline const char *node_type_debug_name(srn_sexp_node_type_e type) {
697 switch (type) {
698 case SRN_SEXP_ATOM:
699 return "ATOM";
700 case SRN_SEXP_CONS:
701 return "CONS";
702 case SRN_SEXP_LIST:
703 return "LIST";
704 case SRN_SEXP_NUM:
705 return "NUM";
706 case SRN_SEXP_SEQ:
707 return "SEQ";
708 case SRN_SEXP_STR:
709 return "STR";
710 }
711 return "UNKNOWN";
712}
713
714/**
715 * Append `fragment` to the end of the `out` debug buffer (a `DEBUG_BUF_MAX_SIZE` buffer,
716 * treated as a growing C string). Returns an error instead of overflowing the buffer.
717 */
718static srn_error_t *debug_buf_append(const srn_context_t *ctx, char *out, const char *fragment) {
719 size_t used = strlen(out);
720 size_t len = strlen(fragment);
721
722 if (used + len + 1 > DEBUG_BUF_MAX_SIZE) {
723 return ERR(
724 ctx, STRING_LENGTH_LIMIT_EXCEEDED, "Debug string exceeds the maximum debug buffer size"
725 );
726 }
727
728 memcpy(out + used, fragment, len + 1);
729 return nullptr;
730}
731
732static srn_error_t *
733debug_buf_append_location(const srn_context_t *ctx, char *out, const srn_metadata_t *metadata) {
734 char buf[METADATA_STR_DEBUG_SIZE] = {0};
735
736 if (metadata != nullptr && metadata->loc != nullptr) {
737 (void)snprintf(
738 buf, sizeof(buf), "%" PRIu16 ":%" PRIu32 ":%" PRIu32, metadata->loc->buffer_id,
739 metadata->loc->offset, metadata->loc->len
740 );
741 } else {
742 (void)snprintf(buf, sizeof(buf), "?:?:?");
743 }
744
745 return debug_buf_append(ctx, out, buf);
746}
747
748static srn_error_t *
749node_to_debug_strig(const srn_context_t *ctx, srn_sexp_node_t *node, char *out) {
750 srn_error_t *err = debug_buf_append(ctx, out, "<");
751 if (err != nullptr) {
752 return err;
753 }
754
755 err = debug_buf_append(ctx, out, node_type_debug_name(node->type));
756 if (err != nullptr) {
757 return err;
758 }
759
760 err = debug_buf_append(ctx, out, "|");
761 if (err != nullptr) {
762 return err;
763 }
764
765 switch (node->type) {
766 case SRN_SEXP_ATOM:
767 err = debug_buf_append(ctx, out, node->as.atom->name);
768 break;
769
770 case SRN_SEXP_STR:
771 err = debug_buf_append(ctx, out, node->as.string->data);
772 break;
773
774 case SRN_SEXP_NUM:
775 err = debug_buf_append(ctx, out, node->as.num->text);
776 break;
777
778 case SRN_SEXP_CONS:
779 err = node_to_debug_strig(ctx, node->as.cons->head, out);
780
781 if (err == nullptr) {
782 err = debug_buf_append(ctx, out, " . ");
783 }
784
785 if (err == nullptr) {
786 if (node->as.cons->tail != nullptr) {
787 err = node_to_debug_strig(ctx, node->as.cons->tail, out);
788 } else {
789 err = debug_buf_append(ctx, out, "nil");
790 }
791 }
792 break;
793
794 case SRN_SEXP_LIST: {
795 srn_sexp_node_t *cons_node = node->as.list->head;
796 bool first = true;
797
798 while (cons_node != nullptr && err == nullptr) {
799 if (!first) {
800 err = debug_buf_append(ctx, out, " ");
801 }
802 first = false;
803
804 if (err == nullptr) {
805 err = node_to_debug_strig(ctx, cons_node->as.cons->head, out);
806 }
807
808 if (err == nullptr) {
809 cons_node = cons_node->as.cons->tail;
810 }
811 }
812 break;
813 }
814
815 case SRN_SEXP_SEQ: {
816 for (size_t i = 0; i < node->as.seq->len && err == nullptr; i++) {
817 if (i > 0) {
818 err = debug_buf_append(ctx, out, " ");
819 }
820
821 if (err == nullptr) {
823
824 if (HAS_ERROR(maybe)) {
825 err = maybe.maybe_error;
826 } else {
827 err = node_to_debug_strig(ctx, (srn_sexp_node_t *)maybe.data, out);
828 }
829 }
830 }
831 break;
832 }
833 }
834
835 if (err != nullptr) {
836 return err;
837 }
838
839 err = debug_buf_append(ctx, out, "|");
840 if (err != nullptr) {
841 return err;
842 }
843
844 err = debug_buf_append_location(ctx, out, node->metadata);
845 if (err != nullptr) {
846 return err;
847 }
848
849 return debug_buf_append(ctx, out, ">");
850}
851
852static srn_error_t *
853sexp_tree_reducer(array_list_t *exprs_array, size_t n, void *expr, void *state) {
854 UNUSED(n);
855 srn_sexp_node_t *node = expr;
856 auto err = node_to_debug_strig(exprs_array->ctx, node, state);
857 return err;
858}
859
861 // Since this function is for debugging purposes we set a hard limit on the buffer
862 char *buf = srn_mm_malloc(ctx->engine->mm, sizeof(char) * DEBUG_BUF_MAX_SIZE);
863 PANIC_IF_NULL(buf);
864 buf[0] = '\0';
865
866 auto err = array_list_reduce(&tree->exprs, sexp_tree_reducer, buf);
867 PANIC_IF(err != nullptr, srn_error_to_string(ctx, err));
868
869 return buf;
870}
int n
Definition acutest.h:525
array_list_maybe_element_t array_list_get(const array_list_t *al, array_list_index_t n)
Negative index is not supported.
Definition array_list.c:235
srn_error_t * array_list_reduce(array_list_t *al, array_list_reduce_f_t f, void *init)
Definition array_list.c:345
array_list_t array_list_empty(const srn_context_t *ctx)
Create an empty array list in the given context ctx.
Definition array_list.c:101
srn_error_t * array_list_push(array_list_t *al, array_list_elem_t x)
Push the given element x to the end of array_list al.
Definition array_list.c:114
A bit-partitioned trie with a tail buffer, used as a growable indexed sequence in the runtime.
#define ALLOCN(ctx, T, N)
Definition context.h:96
#define ALLOC(ctx, T)
Definition context.h:95
#define ALLOC_OR_PANIC(ctx, T)
Definition context.h:98
#define ALLOCN_OR_PANIC(ctx, T, N)
Definition context.h:99
@ IR_TYPE_IR_READER
Definition core.h:66
void * srn_mm_malloc(srn_mm_t *mm, size_t size)
Generic allocations that do not participate in the block based pools.
Definition default.c:155
char * srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err)
Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.
Definition errors.c:133
Error handling for the runtime.
#define PUSH_AND_MOVE(ctx, from, to)
Definition errors.h:73
#define HAS_ERROR(x)
True when x – a value carrying an ERROR_HEADER – has an error attached.
Definition errors.h:70
@ STRING_LENGTH_LIMIT_EXCEEDED
Definition errors.h:110
@ IR_PARSE_ERROR
Definition errors.h:146
#define PUSH_TRACE(ctx, x)
Definition errors.h:72
#define ERR(ctx, err, msg)
Definition errors.h:223
srn_fiber_t * srn_fiber_spawn(srn_context_t *ctx, srn_fiber_entry_t entry, void *arg)
Make and schedule a fiber with every default, the engine's scheduler, the configured stack size,...
Definition fiber.c:250
AI Generated (🤦) Fiber subsystem overview.
void * srn_fiber_result_t
What a fiber's entry produces, type-erased.
Definition fiber.h:171
static utf8_int32_t peek_next(srn_sexp_parser_t *p)
The codepoint one past the current one, without moving p.
Definition reader.c:65
static void skip_whitespace(srn_sexp_parser_t *p)
Definition reader.c:97
srn_sexp_node_t * srn_sexp_make_list(srn_context_t *ctx)
Definition reader.c:224
srn_sexp_node_t * srn_sexp_make_atom(srn_context_t *ctx, const char *name)
Definition reader.c:176
static bool is_atom_char(utf8_int32_t c)
Definition reader.c:115
srn_sexp_node_t * read_expr(srn_sexp_parser_t *parser, srn_error_t **err)
Definition reader.c:552
static const char * node_type_debug_name(srn_sexp_node_type_e type)
Definition reader.c:696
static srn_error_t * parse_error(srn_sexp_parser_t *parser, const utf8_int32_t c, const char *msg)
Definition reader.c:135
static srn_error_t * debug_buf_append_location(const srn_context_t *ctx, char *out, const srn_metadata_t *metadata)
Definition reader.c:733
srn_sexp_node_t * srn_sexp_make_num(srn_context_t *ctx, const char *text)
Definition reader.c:198
static srn_error_t * sexp_tree_reducer(array_list_t *exprs_array, size_t n, void *expr, void *state)
Definition reader.c:853
static void read_comment(srn_sexp_parser_t *p)
Definition reader.c:91
static srn_error_t * node_to_debug_strig(const srn_context_t *ctx, srn_sexp_node_t *node, char *out)
Definition reader.c:749
const char * srn_sexp_to_debug_string(srn_context_t *ctx, srn_sexp_tree_t *tree)
Convert the given sexp to a string for debugging purposes.
Definition reader.c:860
static srn_sexp_node_t * read_atom(srn_sexp_parser_t *parser, srn_error_t **err)
Definition reader.c:361
srn_sexp_tree_t * srn_sexp_read_from_string(srn_context_t *ctx, void *reading_context, const char *buf)
Read the symbolic expressions from a buffer.
Definition reader.c:636
static srn_sexp_node_t * read_number(srn_sexp_parser_t *parser, srn_error_t **err)
A number literal: an optional leading sign, digits, an optional '.
Definition reader.c:399
#define UNEXPECTED_CHAR_MSG_LEN
Definition reader.c:34
srn_sexp_node_t * srn_sexp_make_str(srn_context_t *ctx, const char *data)
Definition reader.c:187
static utf8_int32_t peek(srn_sexp_parser_t *p)
Definition reader.c:48
static srn_sexp_node_t * read_seq(srn_sexp_parser_t *parser, srn_error_t **err)
Definition reader.c:306
#define CHAR_BUFF_SIZE
Definition reader.c:35
#define METADATA_STR_DEBUG_SIZE
Definition reader.c:40
static bool is_atom_start(utf8_int32_t c)
Definition reader.c:109
static srn_error_t * debug_buf_append(const srn_context_t *ctx, char *out, const char *fragment)
Append fragment to the end of the out debug buffer (a DEBUG_BUF_MAX_SIZE buffer, treated as a growing...
Definition reader.c:718
#define DEBUG_BUF_MAX_SIZE
Definition reader.c:39
static utf8_int32_t advance(srn_sexp_parser_t *p)
Definition reader.c:54
srn_fiber_result_t read_from_buffer(srn_context_t *ctx, void *arg)
Definition reader.c:584
srn_sexp_node_t * srn_sexp_make_node(srn_context_t *ctx, srn_sexp_node_type_e type)
Definition reader.c:234
static srn_sexp_node_t * read_list(srn_sexp_parser_t *parser, srn_error_t **err)
Definition reader.c:250
static bool is_number_start(utf8_int32_t c, utf8_int32_t next)
Definition reader.c:111
static bool is_at_end(srn_sexp_parser_t *p)
Definition reader.c:89
srn_sexp_node_t * srn_sexp_make_cons(srn_context_t *ctx, srn_sexp_node_t *head, srn_sexp_node_t *tail)
Definition reader.c:211
static srn_sexp_node_t * read_string(srn_sexp_parser_t *parser, srn_error_t **err)
Definition reader.c:466
srn_sexp_tree_t * srn_sexp_read_from_file(srn_context_t *ctx, void *reading_context, const char *path)
Read the symbolic expressions from a file.
Definition reader.c:643
static bool is_digit(utf8_int32_t c)
Definition reader.c:87
static bool is_whitespace(utf8_int32_t c)
Definition reader.c:79
A simple sexp reader to be used with textual IR.
srn_sexp_node_type_e
Definition reader.h:51
@ SRN_SEXP_CONS
Definition reader.h:53
@ SRN_SEXP_ATOM
Definition reader.h:52
@ SRN_SEXP_NUM
Definition reader.h:55
@ SRN_SEXP_LIST
Definition reader.h:54
@ SRN_SEXP_STR
Definition reader.h:57
@ SRN_SEXP_SEQ
Definition reader.h:56
srn_fiber_result_t srn_fiber_wait_for(srn_fiber_t *target)
Block the calling fiber until target finishes, then return its result.
Definition scheduler.c:1130
void srn_fiber_yield(void)
Yield cooperatively, re-enqueue the running fiber and run the next ready one.
Definition scheduler.c:1039
srn_sm_buffer_t * srn_sm_get_buffer(srn_sm_t *sm, srn_sm_buffer_id_t id)
Return the buffer pointer of the given id, nullptr if the id does not exists.
srn_sm_maybe_id_t srn_sm_read_buffer_file(srn_sm_t *sm, const char *filepath, bool reread)
Read and buffer the given filepath into the source manager and return the buffer id.
static size_t srn_sm_buffer_len(srn_sm_buffer_t *buf)
static const unsigned char * srn_sm_buffer_data(srn_sm_buffer_t *buf)
const size_t len
srn_sm_t * sm
void * arg
auto first
char path[PATH_MAX]
auto result
array_list_elem_t data
Definition array_list.h:142
size_t len
logical length.
Definition array_list.h:168
const srn_context_t * ctx
The context that owns every allocation the array list retains.
Definition array_list.h:183
srn_sexp_parser_t * parser
Definition reader.h:183
srn_sexp_tree_t * tree
Definition reader.h:184
srn_engine_t * engine
Long term state of the compiler.
Definition context.h:49
srn_mm_t * mm
Memory manager.
Definition engine.h:68
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
union srn_error_t::@110141362101221075206211275212023153042233307220 as
srn_error_layer_t layer
Definition errors.h:174
char * msg
Definition errors.h:176
srn_error_tag_t tag
Definition errors.h:175
srn_ir_type_t ir_type
The type or better to say the category of the raw_expr above.
Definition core.h:79
srn_src_location_t * loc
nullptr is a valid value and represents an unknown location.
Definition core.h:74
void * raw_expr
An optional pointer to the original and raw expression.
Definition core.h:77
char * name
Definition reader.h:64
srn_sexp_node_t * tail
Definition reader.h:98
srn_sexp_node_t * head
Definition reader.h:97
srn_sexp_node_t * head
Definition reader.h:110
srn_sexp_list_t * list
Definition reader.h:125
union srn_sexp_node_t::@313353022266020117306231170157030062126255275140 as
srn_sexp_str_t * string
Definition reader.h:128
srn_sexp_cons_t * cons
Definition reader.h:126
srn_sexp_node_type_e type
Definition reader.h:120
srn_sexp_atom_t * atom
Definition reader.h:124
array_list_t * seq
Definition reader.h:127
srn_sexp_num_t * num
Definition reader.h:129
srn_metadata_t * metadata
Definition reader.h:121
A number literal, kept as the raw text it was written with (e.g.
Definition reader.h:86
char * text
Definition reader.h:87
const unsigned char * buffer
Definition reader.h:146
void * reading_context
We store whatever the caller passes as reading context to pass it back later on in the parsed tree.
Definition reader.h:143
uint32_t pos
Definition reader.h:150
srn_context_t * ctx
Definition reader.h:140
const srn_sm_buffer_id_t buffer_id
Definition reader.h:145
char * data
Definition reader.h:74
void * reading_context
Definition reader.h:158
array_list_t exprs
Definition reader.h:156
unsigned char * data
nullptr when the source file was empty (no mapping exists)
srn_sm_buffer_id_t id
uint16_t buffer_id
ID of the source buffer that is registered with the source manager.
Definition core.h:57
uint32_t len
Length of the form in bytes.
Definition core.h:62
uint32_t offset
Byte offset of the form's first byte in the buffer.
Definition core.h:59
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define SNPRINTF(buf, s, f,...)
Definition utils.h:72
#define PANIC_IF(cond, msg)
Definition utils.h:59
#define UNUSED(x)
Definition utils.h:45